Roo/bootstrap/Column.js
[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, 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 (return false to block)
2064          * @param {Roo.menu.Menu} this
2065          */
2066         beforeshow : true,
2067         /**
2068          * @event beforehide
2069          * Fires before this menu is hidden (return false to block)
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     {
2293         if (false === this.fireEvent("beforeshow", this)) {
2294             Roo.log("show canceled");
2295             return;
2296         }
2297         this.parentMenu = parentMenu;
2298         if(!this.el){
2299             this.render();
2300         }
2301         
2302         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2303     },
2304      /**
2305      * Displays this menu at a specific xy position
2306      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2307      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2308      */
2309     showAt : function(xy, parentMenu, /* private: */_e){
2310         this.parentMenu = parentMenu;
2311         if(!this.el){
2312             this.render();
2313         }
2314         if(_e !== false){
2315             this.fireEvent("beforeshow", this);
2316             //xy = this.el.adjustForConstraints(xy);
2317         }
2318         
2319         //this.el.show();
2320         this.hideMenuItems();
2321         this.hidden = false;
2322         this.triggerEl.addClass('open');
2323         this.el.addClass('show');
2324         
2325         // reassign x when hitting right
2326         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2327             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2328         }
2329         
2330         // reassign y when hitting bottom
2331         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2332             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2333         }
2334         
2335         // but the list may align on trigger left or trigger top... should it be a properity?
2336         
2337         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2338             this.el.setXY(xy);
2339         }
2340         
2341         this.focus();
2342         this.fireEvent("show", this);
2343     },
2344     
2345     focus : function(){
2346         return;
2347         if(!this.hidden){
2348             this.doFocus.defer(50, this);
2349         }
2350     },
2351
2352     doFocus : function(){
2353         if(!this.hidden){
2354             this.focusEl.focus();
2355         }
2356     },
2357
2358     /**
2359      * Hides this menu and optionally all parent menus
2360      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2361      */
2362     hide : function(deep)
2363     {
2364         if (false === this.fireEvent("beforehide", this)) {
2365             Roo.log("hide canceled");
2366             return;
2367         }
2368         this.hideMenuItems();
2369         if(this.el && this.isVisible()){
2370            
2371             if(this.activeItem){
2372                 this.activeItem.deactivate();
2373                 this.activeItem = null;
2374             }
2375             this.triggerEl.removeClass('open');;
2376             this.el.removeClass('show');
2377             this.hidden = true;
2378             this.fireEvent("hide", this);
2379         }
2380         if(deep === true && this.parentMenu){
2381             this.parentMenu.hide(true);
2382         }
2383     },
2384     
2385     onTriggerClick : function(e)
2386     {
2387         Roo.log('trigger click');
2388         
2389         var target = e.getTarget();
2390         
2391         Roo.log(target.nodeName.toLowerCase());
2392         
2393         if(target.nodeName.toLowerCase() === 'i'){
2394             e.preventDefault();
2395         }
2396         
2397     },
2398     
2399     onTriggerPress  : function(e)
2400     {
2401         Roo.log('trigger press');
2402         //Roo.log(e.getTarget());
2403        // Roo.log(this.triggerEl.dom);
2404        
2405         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2406         var pel = Roo.get(e.getTarget());
2407         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2408             Roo.log('is treeview or dropdown?');
2409             return;
2410         }
2411         
2412         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2413             return;
2414         }
2415         
2416         if (this.isVisible()) {
2417             Roo.log('hide');
2418             this.hide();
2419         } else {
2420             Roo.log('show');
2421             this.show(this.triggerEl, '?', false);
2422         }
2423         
2424         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2425             e.stopEvent();
2426         }
2427         
2428     },
2429        
2430     
2431     hideMenuItems : function()
2432     {
2433         Roo.log("hide Menu Items");
2434         if (!this.el) { 
2435             return;
2436         }
2437         
2438         this.el.select('.open',true).each(function(aa) {
2439             
2440             aa.removeClass('open');
2441          
2442         });
2443     },
2444     addxtypeChild : function (tree, cntr) {
2445         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2446           
2447         this.menuitems.add(comp);
2448         return comp;
2449
2450     },
2451     getEl : function()
2452     {
2453         Roo.log(this.el);
2454         return this.el;
2455     },
2456     
2457     clear : function()
2458     {
2459         this.getEl().dom.innerHTML = '';
2460         this.menuitems.clear();
2461     }
2462 });
2463
2464  
2465  /*
2466  * - LGPL
2467  *
2468  * menu item
2469  * 
2470  */
2471
2472
2473 /**
2474  * @class Roo.bootstrap.MenuItem
2475  * @extends Roo.bootstrap.Component
2476  * Bootstrap MenuItem class
2477  * @cfg {String} html the menu label
2478  * @cfg {String} href the link
2479  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2480  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2481  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2482  * @cfg {String} fa favicon to show on left of menu item.
2483  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2484  * 
2485  * 
2486  * @constructor
2487  * Create a new MenuItem
2488  * @param {Object} config The config object
2489  */
2490
2491
2492 Roo.bootstrap.MenuItem = function(config){
2493     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2494     this.addEvents({
2495         // raw events
2496         /**
2497          * @event click
2498          * The raw click event for the entire grid.
2499          * @param {Roo.bootstrap.MenuItem} this
2500          * @param {Roo.EventObject} e
2501          */
2502         "click" : true
2503     });
2504 };
2505
2506 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2507     
2508     href : false,
2509     html : false,
2510     preventDefault: false,
2511     isContainer : false,
2512     active : false,
2513     fa: false,
2514     
2515     getAutoCreate : function(){
2516         
2517         if(this.isContainer){
2518             return {
2519                 tag: 'li',
2520                 cls: 'dropdown-menu-item '
2521             };
2522         }
2523         var ctag = {
2524             tag: 'span',
2525             html: 'Link'
2526         };
2527         
2528         var anc = {
2529             tag : 'a',
2530             cls : 'dropdown-item',
2531             href : '#',
2532             cn : [  ]
2533         };
2534         
2535         if (this.fa !== false) {
2536             anc.cn.push({
2537                 tag : 'i',
2538                 cls : 'fa fa-' + this.fa
2539             });
2540         }
2541         
2542         anc.cn.push(ctag);
2543         
2544         
2545         var cfg= {
2546             tag: 'li',
2547             cls: 'dropdown-menu-item',
2548             cn: [ anc ]
2549         };
2550         if (this.parent().type == 'treeview') {
2551             cfg.cls = 'treeview-menu';
2552         }
2553         if (this.active) {
2554             cfg.cls += ' active';
2555         }
2556         
2557         
2558         
2559         anc.href = this.href || cfg.cn[0].href ;
2560         ctag.html = this.html || cfg.cn[0].html ;
2561         return cfg;
2562     },
2563     
2564     initEvents: function()
2565     {
2566         if (this.parent().type == 'treeview') {
2567             this.el.select('a').on('click', this.onClick, this);
2568         }
2569         
2570         if (this.menu) {
2571             this.menu.parentType = this.xtype;
2572             this.menu.triggerEl = this.el;
2573             this.menu = this.addxtype(Roo.apply({}, this.menu));
2574         }
2575         
2576     },
2577     onClick : function(e)
2578     {
2579         Roo.log('item on click ');
2580         
2581         if(this.preventDefault){
2582             e.preventDefault();
2583         }
2584         //this.parent().hideMenuItems();
2585         
2586         this.fireEvent('click', this, e);
2587     },
2588     getEl : function()
2589     {
2590         return this.el;
2591     } 
2592 });
2593
2594  
2595
2596  /*
2597  * - LGPL
2598  *
2599  * menu separator
2600  * 
2601  */
2602
2603
2604 /**
2605  * @class Roo.bootstrap.MenuSeparator
2606  * @extends Roo.bootstrap.Component
2607  * Bootstrap MenuSeparator class
2608  * 
2609  * @constructor
2610  * Create a new MenuItem
2611  * @param {Object} config The config object
2612  */
2613
2614
2615 Roo.bootstrap.MenuSeparator = function(config){
2616     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2617 };
2618
2619 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2620     
2621     getAutoCreate : function(){
2622         var cfg = {
2623             cls: 'divider',
2624             tag : 'li'
2625         };
2626         
2627         return cfg;
2628     }
2629    
2630 });
2631
2632  
2633
2634  
2635 /*
2636 * Licence: LGPL
2637 */
2638
2639 /**
2640  * @class Roo.bootstrap.Modal
2641  * @extends Roo.bootstrap.Component
2642  * Bootstrap Modal class
2643  * @cfg {String} title Title of dialog
2644  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2645  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2646  * @cfg {Boolean} specificTitle default false
2647  * @cfg {Array} buttons Array of buttons or standard button set..
2648  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
2649  * @cfg {Boolean} animate default true
2650  * @cfg {Boolean} allow_close default true
2651  * @cfg {Boolean} fitwindow default false
2652  * @cfg {String} size (sm|lg) default empty
2653  * @cfg {Number} max_width set the max width of modal
2654  *
2655  *
2656  * @constructor
2657  * Create a new Modal Dialog
2658  * @param {Object} config The config object
2659  */
2660
2661 Roo.bootstrap.Modal = function(config){
2662     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2663     this.addEvents({
2664         // raw events
2665         /**
2666          * @event btnclick
2667          * The raw btnclick event for the button
2668          * @param {Roo.EventObject} e
2669          */
2670         "btnclick" : true,
2671         /**
2672          * @event resize
2673          * Fire when dialog resize
2674          * @param {Roo.bootstrap.Modal} this
2675          * @param {Roo.EventObject} e
2676          */
2677         "resize" : true
2678     });
2679     this.buttons = this.buttons || [];
2680
2681     if (this.tmpl) {
2682         this.tmpl = Roo.factory(this.tmpl);
2683     }
2684
2685 };
2686
2687 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2688
2689     title : 'test dialog',
2690
2691     buttons : false,
2692
2693     // set on load...
2694
2695     html: false,
2696
2697     tmp: false,
2698
2699     specificTitle: false,
2700
2701     buttonPosition: 'right',
2702
2703     allow_close : true,
2704
2705     animate : true,
2706
2707     fitwindow: false,
2708     
2709      // private
2710     dialogEl: false,
2711     bodyEl:  false,
2712     footerEl:  false,
2713     titleEl:  false,
2714     closeEl:  false,
2715
2716     size: '',
2717     
2718     max_width: 0,
2719     
2720     max_height: 0,
2721     
2722     fit_content: false,
2723
2724     onRender : function(ct, position)
2725     {
2726         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2727
2728         if(!this.el){
2729             var cfg = Roo.apply({},  this.getAutoCreate());
2730             cfg.id = Roo.id();
2731             //if(!cfg.name){
2732             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2733             //}
2734             //if (!cfg.name.length) {
2735             //    delete cfg.name;
2736            // }
2737             if (this.cls) {
2738                 cfg.cls += ' ' + this.cls;
2739             }
2740             if (this.style) {
2741                 cfg.style = this.style;
2742             }
2743             this.el = Roo.get(document.body).createChild(cfg, position);
2744         }
2745         //var type = this.el.dom.type;
2746
2747
2748         if(this.tabIndex !== undefined){
2749             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2750         }
2751
2752         this.dialogEl = this.el.select('.modal-dialog',true).first();
2753         this.bodyEl = this.el.select('.modal-body',true).first();
2754         this.closeEl = this.el.select('.modal-header .close', true).first();
2755         this.headerEl = this.el.select('.modal-header',true).first();
2756         this.titleEl = this.el.select('.modal-title',true).first();
2757         this.footerEl = this.el.select('.modal-footer',true).first();
2758
2759         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2760         
2761         //this.el.addClass("x-dlg-modal");
2762
2763         if (this.buttons.length) {
2764             Roo.each(this.buttons, function(bb) {
2765                 var b = Roo.apply({}, bb);
2766                 b.xns = b.xns || Roo.bootstrap;
2767                 b.xtype = b.xtype || 'Button';
2768                 if (typeof(b.listeners) == 'undefined') {
2769                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2770                 }
2771
2772                 var btn = Roo.factory(b);
2773
2774                 btn.render(this.getButtonContainer());
2775
2776             },this);
2777         }
2778         // render the children.
2779         var nitems = [];
2780
2781         if(typeof(this.items) != 'undefined'){
2782             var items = this.items;
2783             delete this.items;
2784
2785             for(var i =0;i < items.length;i++) {
2786                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2787             }
2788         }
2789
2790         this.items = nitems;
2791
2792         // where are these used - they used to be body/close/footer
2793
2794
2795         this.initEvents();
2796         //this.el.addClass([this.fieldClass, this.cls]);
2797
2798     },
2799
2800     getAutoCreate : function()
2801     {
2802         var bdy = {
2803                 cls : 'modal-body',
2804                 html : this.html || ''
2805         };
2806
2807         var title = {
2808             tag: 'h4',
2809             cls : 'modal-title',
2810             html : this.title
2811         };
2812
2813         if(this.specificTitle){
2814             title = this.title;
2815
2816         }
2817
2818         var header = [];
2819         if (this.allow_close && Roo.bootstrap.version == 3) {
2820             header.push({
2821                 tag: 'button',
2822                 cls : 'close',
2823                 html : '&times'
2824             });
2825         }
2826
2827         header.push(title);
2828
2829         if (this.allow_close && Roo.bootstrap.version == 4) {
2830             header.push({
2831                 tag: 'button',
2832                 cls : 'close',
2833                 html : '&times'
2834             });
2835         }
2836         
2837         var size = '';
2838
2839         if(this.size.length){
2840             size = 'modal-' + this.size;
2841         }
2842         
2843         var footer = Roo.bootstrap.version == 3 ?
2844             {
2845                 cls : 'modal-footer',
2846                 cn : [
2847                     {
2848                         tag: 'div',
2849                         cls: 'btn-' + this.buttonPosition
2850                     }
2851                 ]
2852
2853             } :
2854             {  // BS4 uses mr-auto on left buttons....
2855                 cls : 'modal-footer'
2856             };
2857
2858             
2859
2860         
2861         
2862         var modal = {
2863             cls: "modal",
2864              cn : [
2865                 {
2866                     cls: "modal-dialog " + size,
2867                     cn : [
2868                         {
2869                             cls : "modal-content",
2870                             cn : [
2871                                 {
2872                                     cls : 'modal-header',
2873                                     cn : header
2874                                 },
2875                                 bdy,
2876                                 footer
2877                             ]
2878
2879                         }
2880                     ]
2881
2882                 }
2883             ]
2884         };
2885
2886         if(this.animate){
2887             modal.cls += ' fade';
2888         }
2889
2890         return modal;
2891
2892     },
2893     getChildContainer : function() {
2894
2895          return this.bodyEl;
2896
2897     },
2898     getButtonContainer : function() {
2899         
2900          return Roo.bootstrap.version == 4 ?
2901             this.el.select('.modal-footer',true).first()
2902             : this.el.select('.modal-footer div',true).first();
2903
2904     },
2905     initEvents : function()
2906     {
2907         if (this.allow_close) {
2908             this.closeEl.on('click', this.hide, this);
2909         }
2910         Roo.EventManager.onWindowResize(this.resize, this, true);
2911
2912
2913     },
2914   
2915
2916     resize : function()
2917     {
2918         this.maskEl.setSize(
2919             Roo.lib.Dom.getViewWidth(true),
2920             Roo.lib.Dom.getViewHeight(true)
2921         );
2922         
2923         if (this.fitwindow) {
2924             
2925            
2926             this.setSize(
2927                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2928                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
2929             );
2930             return;
2931         }
2932         
2933         if(this.max_width !== 0) {
2934             
2935             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2936             
2937             if(this.height) {
2938                 this.setSize(w, this.height);
2939                 return;
2940             }
2941             
2942             if(this.max_height) {
2943                 this.setSize(w,Math.min(
2944                     this.max_height,
2945                     Roo.lib.Dom.getViewportHeight(true) - 60
2946                 ));
2947                 
2948                 return;
2949             }
2950             
2951             if(!this.fit_content) {
2952                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2953                 return;
2954             }
2955             
2956             this.setSize(w, Math.min(
2957                 60 +
2958                 this.headerEl.getHeight() + 
2959                 this.footerEl.getHeight() + 
2960                 this.getChildHeight(this.bodyEl.dom.childNodes),
2961                 Roo.lib.Dom.getViewportHeight(true) - 60)
2962             );
2963         }
2964         
2965     },
2966
2967     setSize : function(w,h)
2968     {
2969         if (!w && !h) {
2970             return;
2971         }
2972         
2973         this.resizeTo(w,h);
2974     },
2975
2976     show : function() {
2977
2978         if (!this.rendered) {
2979             this.render();
2980         }
2981
2982         //this.el.setStyle('display', 'block');
2983         this.el.removeClass('hideing');
2984         this.el.dom.style.display='block';
2985         
2986         Roo.get(document.body).addClass('modal-open');
2987  
2988         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2989             
2990             (function(){
2991                 this.el.addClass('show');
2992                 this.el.addClass('in');
2993             }).defer(50, this);
2994         }else{
2995             this.el.addClass('show');
2996             this.el.addClass('in');
2997         }
2998
2999         // not sure how we can show data in here..
3000         //if (this.tmpl) {
3001         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
3002         //}
3003
3004         Roo.get(document.body).addClass("x-body-masked");
3005         
3006         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
3007         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3008         this.maskEl.dom.style.display = 'block';
3009         this.maskEl.addClass('show');
3010         
3011         
3012         this.resize();
3013         
3014         this.fireEvent('show', this);
3015
3016         // set zindex here - otherwise it appears to be ignored...
3017         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3018
3019         (function () {
3020             this.items.forEach( function(e) {
3021                 e.layout ? e.layout() : false;
3022
3023             });
3024         }).defer(100,this);
3025
3026     },
3027     hide : function()
3028     {
3029         if(this.fireEvent("beforehide", this) !== false){
3030             
3031             this.maskEl.removeClass('show');
3032             
3033             this.maskEl.dom.style.display = '';
3034             Roo.get(document.body).removeClass("x-body-masked");
3035             this.el.removeClass('in');
3036             this.el.select('.modal-dialog', true).first().setStyle('transform','');
3037
3038             if(this.animate){ // why
3039                 this.el.addClass('hideing');
3040                 this.el.removeClass('show');
3041                 (function(){
3042                     if (!this.el.hasClass('hideing')) {
3043                         return; // it's been shown again...
3044                     }
3045                     
3046                     this.el.dom.style.display='';
3047
3048                     Roo.get(document.body).removeClass('modal-open');
3049                     this.el.removeClass('hideing');
3050                 }).defer(150,this);
3051                 
3052             }else{
3053                 this.el.removeClass('show');
3054                 this.el.dom.style.display='';
3055                 Roo.get(document.body).removeClass('modal-open');
3056
3057             }
3058             this.fireEvent('hide', this);
3059         }
3060     },
3061     isVisible : function()
3062     {
3063         
3064         return this.el.hasClass('show') && !this.el.hasClass('hideing');
3065         
3066     },
3067
3068     addButton : function(str, cb)
3069     {
3070
3071
3072         var b = Roo.apply({}, { html : str } );
3073         b.xns = b.xns || Roo.bootstrap;
3074         b.xtype = b.xtype || 'Button';
3075         if (typeof(b.listeners) == 'undefined') {
3076             b.listeners = { click : cb.createDelegate(this)  };
3077         }
3078
3079         var btn = Roo.factory(b);
3080
3081         btn.render(this.getButtonContainer());
3082
3083         return btn;
3084
3085     },
3086
3087     setDefaultButton : function(btn)
3088     {
3089         //this.el.select('.modal-footer').()
3090     },
3091
3092     resizeTo: function(w,h)
3093     {
3094         this.dialogEl.setWidth(w);
3095         
3096         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
3097
3098         this.bodyEl.setHeight(h - diff);
3099         
3100         this.fireEvent('resize', this);
3101     },
3102     
3103     setContentSize  : function(w, h)
3104     {
3105
3106     },
3107     onButtonClick: function(btn,e)
3108     {
3109         //Roo.log([a,b,c]);
3110         this.fireEvent('btnclick', btn.name, e);
3111     },
3112      /**
3113      * Set the title of the Dialog
3114      * @param {String} str new Title
3115      */
3116     setTitle: function(str) {
3117         this.titleEl.dom.innerHTML = str;
3118     },
3119     /**
3120      * Set the body of the Dialog
3121      * @param {String} str new Title
3122      */
3123     setBody: function(str) {
3124         this.bodyEl.dom.innerHTML = str;
3125     },
3126     /**
3127      * Set the body of the Dialog using the template
3128      * @param {Obj} data - apply this data to the template and replace the body contents.
3129      */
3130     applyBody: function(obj)
3131     {
3132         if (!this.tmpl) {
3133             Roo.log("Error - using apply Body without a template");
3134             //code
3135         }
3136         this.tmpl.overwrite(this.bodyEl, obj);
3137     },
3138     
3139     getChildHeight : function(child_nodes)
3140     {
3141         if(
3142             !child_nodes ||
3143             child_nodes.length == 0
3144         ) {
3145             return;
3146         }
3147         
3148         var child_height = 0;
3149         
3150         for(var i = 0; i < child_nodes.length; i++) {
3151             
3152             /*
3153             * for modal with tabs...
3154             if(child_nodes[i].classList.contains('roo-layout-panel')) {
3155                 
3156                 var layout_childs = child_nodes[i].childNodes;
3157                 
3158                 for(var j = 0; j < layout_childs.length; j++) {
3159                     
3160                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3161                         
3162                         var layout_body_childs = layout_childs[j].childNodes;
3163                         
3164                         for(var k = 0; k < layout_body_childs.length; k++) {
3165                             
3166                             if(layout_body_childs[k].classList.contains('navbar')) {
3167                                 child_height += layout_body_childs[k].offsetHeight;
3168                                 continue;
3169                             }
3170                             
3171                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3172                                 
3173                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3174                                 
3175                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3176                                     
3177                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3178                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3179                                         continue;
3180                                     }
3181                                     
3182                                 }
3183                                 
3184                             }
3185                             
3186                         }
3187                     }
3188                 }
3189                 continue;
3190             }
3191             */
3192             
3193             child_height += child_nodes[i].offsetHeight;
3194             // Roo.log(child_nodes[i].offsetHeight);
3195         }
3196         
3197         return child_height;
3198     }
3199
3200 });
3201
3202
3203 Roo.apply(Roo.bootstrap.Modal,  {
3204     /**
3205          * Button config that displays a single OK button
3206          * @type Object
3207          */
3208         OK :  [{
3209             name : 'ok',
3210             weight : 'primary',
3211             html : 'OK'
3212         }],
3213         /**
3214          * Button config that displays Yes and No buttons
3215          * @type Object
3216          */
3217         YESNO : [
3218             {
3219                 name  : 'no',
3220                 html : 'No'
3221             },
3222             {
3223                 name  :'yes',
3224                 weight : 'primary',
3225                 html : 'Yes'
3226             }
3227         ],
3228
3229         /**
3230          * Button config that displays OK and Cancel buttons
3231          * @type Object
3232          */
3233         OKCANCEL : [
3234             {
3235                name : 'cancel',
3236                 html : 'Cancel'
3237             },
3238             {
3239                 name : 'ok',
3240                 weight : 'primary',
3241                 html : 'OK'
3242             }
3243         ],
3244         /**
3245          * Button config that displays Yes, No and Cancel buttons
3246          * @type Object
3247          */
3248         YESNOCANCEL : [
3249             {
3250                 name : 'yes',
3251                 weight : 'primary',
3252                 html : 'Yes'
3253             },
3254             {
3255                 name : 'no',
3256                 html : 'No'
3257             },
3258             {
3259                 name : 'cancel',
3260                 html : 'Cancel'
3261             }
3262         ],
3263         
3264         zIndex : 10001
3265 });
3266 /*
3267  * - LGPL
3268  *
3269  * messagebox - can be used as a replace
3270  * 
3271  */
3272 /**
3273  * @class Roo.MessageBox
3274  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3275  * Example usage:
3276  *<pre><code>
3277 // Basic alert:
3278 Roo.Msg.alert('Status', 'Changes saved successfully.');
3279
3280 // Prompt for user data:
3281 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3282     if (btn == 'ok'){
3283         // process text value...
3284     }
3285 });
3286
3287 // Show a dialog using config options:
3288 Roo.Msg.show({
3289    title:'Save Changes?',
3290    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3291    buttons: Roo.Msg.YESNOCANCEL,
3292    fn: processResult,
3293    animEl: 'elId'
3294 });
3295 </code></pre>
3296  * @singleton
3297  */
3298 Roo.bootstrap.MessageBox = function(){
3299     var dlg, opt, mask, waitTimer;
3300     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3301     var buttons, activeTextEl, bwidth;
3302
3303     
3304     // private
3305     var handleButton = function(button){
3306         dlg.hide();
3307         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3308     };
3309
3310     // private
3311     var handleHide = function(){
3312         if(opt && opt.cls){
3313             dlg.el.removeClass(opt.cls);
3314         }
3315         //if(waitTimer){
3316         //    Roo.TaskMgr.stop(waitTimer);
3317         //    waitTimer = null;
3318         //}
3319     };
3320
3321     // private
3322     var updateButtons = function(b){
3323         var width = 0;
3324         if(!b){
3325             buttons["ok"].hide();
3326             buttons["cancel"].hide();
3327             buttons["yes"].hide();
3328             buttons["no"].hide();
3329             dlg.footerEl.hide();
3330             
3331             return width;
3332         }
3333         dlg.footerEl.show();
3334         for(var k in buttons){
3335             if(typeof buttons[k] != "function"){
3336                 if(b[k]){
3337                     buttons[k].show();
3338                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3339                     width += buttons[k].el.getWidth()+15;
3340                 }else{
3341                     buttons[k].hide();
3342                 }
3343             }
3344         }
3345         return width;
3346     };
3347
3348     // private
3349     var handleEsc = function(d, k, e){
3350         if(opt && opt.closable !== false){
3351             dlg.hide();
3352         }
3353         if(e){
3354             e.stopEvent();
3355         }
3356     };
3357
3358     return {
3359         /**
3360          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3361          * @return {Roo.BasicDialog} The BasicDialog element
3362          */
3363         getDialog : function(){
3364            if(!dlg){
3365                 dlg = new Roo.bootstrap.Modal( {
3366                     //draggable: true,
3367                     //resizable:false,
3368                     //constraintoviewport:false,
3369                     //fixedcenter:true,
3370                     //collapsible : false,
3371                     //shim:true,
3372                     //modal: true,
3373                 //    width: 'auto',
3374                   //  height:100,
3375                     //buttonAlign:"center",
3376                     closeClick : function(){
3377                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3378                             handleButton("no");
3379                         }else{
3380                             handleButton("cancel");
3381                         }
3382                     }
3383                 });
3384                 dlg.render();
3385                 dlg.on("hide", handleHide);
3386                 mask = dlg.mask;
3387                 //dlg.addKeyListener(27, handleEsc);
3388                 buttons = {};
3389                 this.buttons = buttons;
3390                 var bt = this.buttonText;
3391                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3392                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3393                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3394                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3395                 //Roo.log(buttons);
3396                 bodyEl = dlg.bodyEl.createChild({
3397
3398                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3399                         '<textarea class="roo-mb-textarea"></textarea>' +
3400                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3401                 });
3402                 msgEl = bodyEl.dom.firstChild;
3403                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3404                 textboxEl.enableDisplayMode();
3405                 textboxEl.addKeyListener([10,13], function(){
3406                     if(dlg.isVisible() && opt && opt.buttons){
3407                         if(opt.buttons.ok){
3408                             handleButton("ok");
3409                         }else if(opt.buttons.yes){
3410                             handleButton("yes");
3411                         }
3412                     }
3413                 });
3414                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3415                 textareaEl.enableDisplayMode();
3416                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3417                 progressEl.enableDisplayMode();
3418                 
3419                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3420                 var pf = progressEl.dom.firstChild;
3421                 if (pf) {
3422                     pp = Roo.get(pf.firstChild);
3423                     pp.setHeight(pf.offsetHeight);
3424                 }
3425                 
3426             }
3427             return dlg;
3428         },
3429
3430         /**
3431          * Updates the message box body text
3432          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3433          * the XHTML-compliant non-breaking space character '&amp;#160;')
3434          * @return {Roo.MessageBox} This message box
3435          */
3436         updateText : function(text)
3437         {
3438             if(!dlg.isVisible() && !opt.width){
3439                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3440                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3441             }
3442             msgEl.innerHTML = text || '&#160;';
3443       
3444             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3445             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3446             var w = Math.max(
3447                     Math.min(opt.width || cw , this.maxWidth), 
3448                     Math.max(opt.minWidth || this.minWidth, bwidth)
3449             );
3450             if(opt.prompt){
3451                 activeTextEl.setWidth(w);
3452             }
3453             if(dlg.isVisible()){
3454                 dlg.fixedcenter = false;
3455             }
3456             // to big, make it scroll. = But as usual stupid IE does not support
3457             // !important..
3458             
3459             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3460                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3461                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3462             } else {
3463                 bodyEl.dom.style.height = '';
3464                 bodyEl.dom.style.overflowY = '';
3465             }
3466             if (cw > w) {
3467                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3468             } else {
3469                 bodyEl.dom.style.overflowX = '';
3470             }
3471             
3472             dlg.setContentSize(w, bodyEl.getHeight());
3473             if(dlg.isVisible()){
3474                 dlg.fixedcenter = true;
3475             }
3476             return this;
3477         },
3478
3479         /**
3480          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3481          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3482          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3483          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3484          * @return {Roo.MessageBox} This message box
3485          */
3486         updateProgress : function(value, text){
3487             if(text){
3488                 this.updateText(text);
3489             }
3490             
3491             if (pp) { // weird bug on my firefox - for some reason this is not defined
3492                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3493                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3494             }
3495             return this;
3496         },        
3497
3498         /**
3499          * Returns true if the message box is currently displayed
3500          * @return {Boolean} True if the message box is visible, else false
3501          */
3502         isVisible : function(){
3503             return dlg && dlg.isVisible();  
3504         },
3505
3506         /**
3507          * Hides the message box if it is displayed
3508          */
3509         hide : function(){
3510             if(this.isVisible()){
3511                 dlg.hide();
3512             }  
3513         },
3514
3515         /**
3516          * Displays a new message box, or reinitializes an existing message box, based on the config options
3517          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3518          * The following config object properties are supported:
3519          * <pre>
3520 Property    Type             Description
3521 ----------  ---------------  ------------------------------------------------------------------------------------
3522 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3523                                    closes (defaults to undefined)
3524 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3525                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3526 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3527                                    progress and wait dialogs will ignore this property and always hide the
3528                                    close button as they can only be closed programmatically.
3529 cls               String           A custom CSS class to apply to the message box element
3530 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3531                                    displayed (defaults to 75)
3532 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3533                                    function will be btn (the name of the button that was clicked, if applicable,
3534                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3535                                    Progress and wait dialogs will ignore this option since they do not respond to
3536                                    user actions and can only be closed programmatically, so any required function
3537                                    should be called by the same code after it closes the dialog.
3538 icon              String           A CSS class that provides a background image to be used as an icon for
3539                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3540 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3541 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3542 modal             Boolean          False to allow user interaction with the page while the message box is
3543                                    displayed (defaults to true)
3544 msg               String           A string that will replace the existing message box body text (defaults
3545                                    to the XHTML-compliant non-breaking space character '&#160;')
3546 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3547 progress          Boolean          True to display a progress bar (defaults to false)
3548 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3549 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3550 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3551 title             String           The title text
3552 value             String           The string value to set into the active textbox element if displayed
3553 wait              Boolean          True to display a progress bar (defaults to false)
3554 width             Number           The width of the dialog in pixels
3555 </pre>
3556          *
3557          * Example usage:
3558          * <pre><code>
3559 Roo.Msg.show({
3560    title: 'Address',
3561    msg: 'Please enter your address:',
3562    width: 300,
3563    buttons: Roo.MessageBox.OKCANCEL,
3564    multiline: true,
3565    fn: saveAddress,
3566    animEl: 'addAddressBtn'
3567 });
3568 </code></pre>
3569          * @param {Object} config Configuration options
3570          * @return {Roo.MessageBox} This message box
3571          */
3572         show : function(options)
3573         {
3574             
3575             // this causes nightmares if you show one dialog after another
3576             // especially on callbacks..
3577              
3578             if(this.isVisible()){
3579                 
3580                 this.hide();
3581                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3582                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3583                 Roo.log("New Dialog Message:" +  options.msg )
3584                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3585                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3586                 
3587             }
3588             var d = this.getDialog();
3589             opt = options;
3590             d.setTitle(opt.title || "&#160;");
3591             d.closeEl.setDisplayed(opt.closable !== false);
3592             activeTextEl = textboxEl;
3593             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3594             if(opt.prompt){
3595                 if(opt.multiline){
3596                     textboxEl.hide();
3597                     textareaEl.show();
3598                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3599                         opt.multiline : this.defaultTextHeight);
3600                     activeTextEl = textareaEl;
3601                 }else{
3602                     textboxEl.show();
3603                     textareaEl.hide();
3604                 }
3605             }else{
3606                 textboxEl.hide();
3607                 textareaEl.hide();
3608             }
3609             progressEl.setDisplayed(opt.progress === true);
3610             if (opt.progress) {
3611                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
3612             }
3613             this.updateProgress(0);
3614             activeTextEl.dom.value = opt.value || "";
3615             if(opt.prompt){
3616                 dlg.setDefaultButton(activeTextEl);
3617             }else{
3618                 var bs = opt.buttons;
3619                 var db = null;
3620                 if(bs && bs.ok){
3621                     db = buttons["ok"];
3622                 }else if(bs && bs.yes){
3623                     db = buttons["yes"];
3624                 }
3625                 dlg.setDefaultButton(db);
3626             }
3627             bwidth = updateButtons(opt.buttons);
3628             this.updateText(opt.msg);
3629             if(opt.cls){
3630                 d.el.addClass(opt.cls);
3631             }
3632             d.proxyDrag = opt.proxyDrag === true;
3633             d.modal = opt.modal !== false;
3634             d.mask = opt.modal !== false ? mask : false;
3635             if(!d.isVisible()){
3636                 // force it to the end of the z-index stack so it gets a cursor in FF
3637                 document.body.appendChild(dlg.el.dom);
3638                 d.animateTarget = null;
3639                 d.show(options.animEl);
3640             }
3641             return this;
3642         },
3643
3644         /**
3645          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3646          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3647          * and closing the message box when the process is complete.
3648          * @param {String} title The title bar text
3649          * @param {String} msg The message box body text
3650          * @return {Roo.MessageBox} This message box
3651          */
3652         progress : function(title, msg){
3653             this.show({
3654                 title : title,
3655                 msg : msg,
3656                 buttons: false,
3657                 progress:true,
3658                 closable:false,
3659                 minWidth: this.minProgressWidth,
3660                 modal : true
3661             });
3662             return this;
3663         },
3664
3665         /**
3666          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3667          * If a callback function is passed it will be called after the user clicks the button, and the
3668          * id of the button that was clicked will be passed as the only parameter to the callback
3669          * (could also be the top-right close button).
3670          * @param {String} title The title bar text
3671          * @param {String} msg The message box body text
3672          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3673          * @param {Object} scope (optional) The scope of the callback function
3674          * @return {Roo.MessageBox} This message box
3675          */
3676         alert : function(title, msg, fn, scope)
3677         {
3678             this.show({
3679                 title : title,
3680                 msg : msg,
3681                 buttons: this.OK,
3682                 fn: fn,
3683                 closable : false,
3684                 scope : scope,
3685                 modal : true
3686             });
3687             return this;
3688         },
3689
3690         /**
3691          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3692          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3693          * You are responsible for closing the message box when the process is complete.
3694          * @param {String} msg The message box body text
3695          * @param {String} title (optional) The title bar text
3696          * @return {Roo.MessageBox} This message box
3697          */
3698         wait : function(msg, title){
3699             this.show({
3700                 title : title,
3701                 msg : msg,
3702                 buttons: false,
3703                 closable:false,
3704                 progress:true,
3705                 modal:true,
3706                 width:300,
3707                 wait:true
3708             });
3709             waitTimer = Roo.TaskMgr.start({
3710                 run: function(i){
3711                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3712                 },
3713                 interval: 1000
3714             });
3715             return this;
3716         },
3717
3718         /**
3719          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3720          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3721          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3722          * @param {String} title The title bar text
3723          * @param {String} msg The message box body text
3724          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3725          * @param {Object} scope (optional) The scope of the callback function
3726          * @return {Roo.MessageBox} This message box
3727          */
3728         confirm : function(title, msg, fn, scope){
3729             this.show({
3730                 title : title,
3731                 msg : msg,
3732                 buttons: this.YESNO,
3733                 fn: fn,
3734                 scope : scope,
3735                 modal : true
3736             });
3737             return this;
3738         },
3739
3740         /**
3741          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3742          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3743          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3744          * (could also be the top-right close button) and the text that was entered will be passed as the two
3745          * parameters to the callback.
3746          * @param {String} title The title bar text
3747          * @param {String} msg The message box body text
3748          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3749          * @param {Object} scope (optional) The scope of the callback function
3750          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3751          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3752          * @return {Roo.MessageBox} This message box
3753          */
3754         prompt : function(title, msg, fn, scope, multiline){
3755             this.show({
3756                 title : title,
3757                 msg : msg,
3758                 buttons: this.OKCANCEL,
3759                 fn: fn,
3760                 minWidth:250,
3761                 scope : scope,
3762                 prompt:true,
3763                 multiline: multiline,
3764                 modal : true
3765             });
3766             return this;
3767         },
3768
3769         /**
3770          * Button config that displays a single OK button
3771          * @type Object
3772          */
3773         OK : {ok:true},
3774         /**
3775          * Button config that displays Yes and No buttons
3776          * @type Object
3777          */
3778         YESNO : {yes:true, no:true},
3779         /**
3780          * Button config that displays OK and Cancel buttons
3781          * @type Object
3782          */
3783         OKCANCEL : {ok:true, cancel:true},
3784         /**
3785          * Button config that displays Yes, No and Cancel buttons
3786          * @type Object
3787          */
3788         YESNOCANCEL : {yes:true, no:true, cancel:true},
3789
3790         /**
3791          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3792          * @type Number
3793          */
3794         defaultTextHeight : 75,
3795         /**
3796          * The maximum width in pixels of the message box (defaults to 600)
3797          * @type Number
3798          */
3799         maxWidth : 600,
3800         /**
3801          * The minimum width in pixels of the message box (defaults to 100)
3802          * @type Number
3803          */
3804         minWidth : 100,
3805         /**
3806          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3807          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3808          * @type Number
3809          */
3810         minProgressWidth : 250,
3811         /**
3812          * An object containing the default button text strings that can be overriden for localized language support.
3813          * Supported properties are: ok, cancel, yes and no.
3814          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3815          * @type Object
3816          */
3817         buttonText : {
3818             ok : "OK",
3819             cancel : "Cancel",
3820             yes : "Yes",
3821             no : "No"
3822         }
3823     };
3824 }();
3825
3826 /**
3827  * Shorthand for {@link Roo.MessageBox}
3828  */
3829 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3830 Roo.Msg = Roo.Msg || Roo.MessageBox;
3831 /*
3832  * - LGPL
3833  *
3834  * navbar
3835  * 
3836  */
3837
3838 /**
3839  * @class Roo.bootstrap.Navbar
3840  * @extends Roo.bootstrap.Component
3841  * Bootstrap Navbar class
3842
3843  * @constructor
3844  * Create a new Navbar
3845  * @param {Object} config The config object
3846  */
3847
3848
3849 Roo.bootstrap.Navbar = function(config){
3850     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3851     this.addEvents({
3852         // raw events
3853         /**
3854          * @event beforetoggle
3855          * Fire before toggle the menu
3856          * @param {Roo.EventObject} e
3857          */
3858         "beforetoggle" : true
3859     });
3860 };
3861
3862 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3863     
3864     
3865    
3866     // private
3867     navItems : false,
3868     loadMask : false,
3869     
3870     
3871     getAutoCreate : function(){
3872         
3873         
3874         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3875         
3876     },
3877     
3878     initEvents :function ()
3879     {
3880         //Roo.log(this.el.select('.navbar-toggle',true));
3881         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
3882         
3883         var mark = {
3884             tag: "div",
3885             cls:"x-dlg-mask"
3886         };
3887         
3888         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3889         
3890         var size = this.el.getSize();
3891         this.maskEl.setSize(size.width, size.height);
3892         this.maskEl.enableDisplayMode("block");
3893         this.maskEl.hide();
3894         
3895         if(this.loadMask){
3896             this.maskEl.show();
3897         }
3898     },
3899     
3900     
3901     getChildContainer : function()
3902     {
3903         if (this.el && this.el.select('.collapse').getCount()) {
3904             return this.el.select('.collapse',true).first();
3905         }
3906         
3907         return this.el;
3908     },
3909     
3910     mask : function()
3911     {
3912         this.maskEl.show();
3913     },
3914     
3915     unmask : function()
3916     {
3917         this.maskEl.hide();
3918     },
3919     onToggle : function()
3920     {
3921         
3922         if(this.fireEvent('beforetoggle', this) === false){
3923             return;
3924         }
3925         var ce = this.el.select('.navbar-collapse',true).first();
3926       
3927         if (!ce.hasClass('show')) {
3928            this.expand();
3929         } else {
3930             this.collapse();
3931         }
3932         
3933         
3934     
3935     },
3936     /**
3937      * Expand the navbar pulldown 
3938      */
3939     expand : function ()
3940     {
3941        
3942         var ce = this.el.select('.navbar-collapse',true).first();
3943         if (ce.hasClass('collapsing')) {
3944             return;
3945         }
3946         ce.dom.style.height = '';
3947                // show it...
3948         ce.addClass('in'); // old...
3949         ce.removeClass('collapse');
3950         ce.addClass('show');
3951         var h = ce.getHeight();
3952         Roo.log(h);
3953         ce.removeClass('show');
3954         // at this point we should be able to see it..
3955         ce.addClass('collapsing');
3956         
3957         ce.setHeight(0); // resize it ...
3958         ce.on('transitionend', function() {
3959             //Roo.log('done transition');
3960             ce.removeClass('collapsing');
3961             ce.addClass('show');
3962             ce.removeClass('collapse');
3963
3964             ce.dom.style.height = '';
3965         }, this, { single: true} );
3966         ce.setHeight(h);
3967         ce.dom.scrollTop = 0;
3968     },
3969     /**
3970      * Collapse the navbar pulldown 
3971      */
3972     collapse : function()
3973     {
3974          var ce = this.el.select('.navbar-collapse',true).first();
3975        
3976         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
3977             // it's collapsed or collapsing..
3978             return;
3979         }
3980         ce.removeClass('in'); // old...
3981         ce.setHeight(ce.getHeight());
3982         ce.removeClass('show');
3983         ce.addClass('collapsing');
3984         
3985         ce.on('transitionend', function() {
3986             ce.dom.style.height = '';
3987             ce.removeClass('collapsing');
3988             ce.addClass('collapse');
3989         }, this, { single: true} );
3990         ce.setHeight(0);
3991     }
3992     
3993     
3994     
3995 });
3996
3997
3998
3999  
4000
4001  /*
4002  * - LGPL
4003  *
4004  * navbar
4005  * 
4006  */
4007
4008 /**
4009  * @class Roo.bootstrap.NavSimplebar
4010  * @extends Roo.bootstrap.Navbar
4011  * Bootstrap Sidebar class
4012  *
4013  * @cfg {Boolean} inverse is inverted color
4014  * 
4015  * @cfg {String} type (nav | pills | tabs)
4016  * @cfg {Boolean} arrangement stacked | justified
4017  * @cfg {String} align (left | right) alignment
4018  * 
4019  * @cfg {Boolean} main (true|false) main nav bar? default false
4020  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
4021  * 
4022  * @cfg {String} tag (header|footer|nav|div) default is nav 
4023
4024  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
4025  * 
4026  * 
4027  * @constructor
4028  * Create a new Sidebar
4029  * @param {Object} config The config object
4030  */
4031
4032
4033 Roo.bootstrap.NavSimplebar = function(config){
4034     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
4035 };
4036
4037 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
4038     
4039     inverse: false,
4040     
4041     type: false,
4042     arrangement: '',
4043     align : false,
4044     
4045     weight : 'light',
4046     
4047     main : false,
4048     
4049     
4050     tag : false,
4051     
4052     
4053     getAutoCreate : function(){
4054         
4055         
4056         var cfg = {
4057             tag : this.tag || 'div',
4058             cls : 'navbar navbar-expand-lg roo-navbar-simple'
4059         };
4060         if (['light','white'].indexOf(this.weight) > -1) {
4061             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4062         }
4063         cfg.cls += ' bg-' + this.weight;
4064         
4065         if (this.inverse) {
4066             cfg.cls += ' navbar-inverse';
4067             
4068         }
4069         
4070         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
4071         
4072         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
4073             return cfg;
4074         }
4075         
4076         
4077     
4078         
4079         cfg.cn = [
4080             {
4081                 cls: 'nav nav-' + this.xtype,
4082                 tag : 'ul'
4083             }
4084         ];
4085         
4086          
4087         this.type = this.type || 'nav';
4088         if (['tabs','pills'].indexOf(this.type) != -1) {
4089             cfg.cn[0].cls += ' nav-' + this.type
4090         
4091         
4092         } else {
4093             if (this.type!=='nav') {
4094                 Roo.log('nav type must be nav/tabs/pills')
4095             }
4096             cfg.cn[0].cls += ' navbar-nav'
4097         }
4098         
4099         
4100         
4101         
4102         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
4103             cfg.cn[0].cls += ' nav-' + this.arrangement;
4104         }
4105         
4106         
4107         if (this.align === 'right') {
4108             cfg.cn[0].cls += ' navbar-right';
4109         }
4110         
4111         
4112         
4113         
4114         return cfg;
4115     
4116         
4117     }
4118     
4119     
4120     
4121 });
4122
4123
4124
4125  
4126
4127  
4128        /*
4129  * - LGPL
4130  *
4131  * navbar
4132  * navbar-fixed-top
4133  * navbar-expand-md  fixed-top 
4134  */
4135
4136 /**
4137  * @class Roo.bootstrap.NavHeaderbar
4138  * @extends Roo.bootstrap.NavSimplebar
4139  * Bootstrap Sidebar class
4140  *
4141  * @cfg {String} brand what is brand
4142  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4143  * @cfg {String} brand_href href of the brand
4144  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
4145  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4146  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4147  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4148  * 
4149  * @constructor
4150  * Create a new Sidebar
4151  * @param {Object} config The config object
4152  */
4153
4154
4155 Roo.bootstrap.NavHeaderbar = function(config){
4156     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4157       
4158 };
4159
4160 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
4161     
4162     position: '',
4163     brand: '',
4164     brand_href: false,
4165     srButton : true,
4166     autohide : false,
4167     desktopCenter : false,
4168    
4169     
4170     getAutoCreate : function(){
4171         
4172         var   cfg = {
4173             tag: this.nav || 'nav',
4174             cls: 'navbar navbar-expand-md',
4175             role: 'navigation',
4176             cn: []
4177         };
4178         
4179         var cn = cfg.cn;
4180         if (this.desktopCenter) {
4181             cn.push({cls : 'container', cn : []});
4182             cn = cn[0].cn;
4183         }
4184         
4185         if(this.srButton){
4186             var btn = {
4187                 tag: 'button',
4188                 type: 'button',
4189                 cls: 'navbar-toggle navbar-toggler',
4190                 'data-toggle': 'collapse',
4191                 cn: [
4192                     {
4193                         tag: 'span',
4194                         cls: 'sr-only',
4195                         html: 'Toggle navigation'
4196                     },
4197                     {
4198                         tag: 'span',
4199                         cls: 'icon-bar navbar-toggler-icon'
4200                     },
4201                     {
4202                         tag: 'span',
4203                         cls: 'icon-bar'
4204                     },
4205                     {
4206                         tag: 'span',
4207                         cls: 'icon-bar'
4208                     }
4209                 ]
4210             };
4211             
4212             cn.push( Roo.bootstrap.version == 4 ? btn : {
4213                 tag: 'div',
4214                 cls: 'navbar-header',
4215                 cn: [
4216                     btn
4217                 ]
4218             });
4219         }
4220         
4221         cn.push({
4222             tag: 'div',
4223             cls: 'collapse navbar-collapse',
4224             cn : []
4225         });
4226         
4227         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4228         
4229         if (['light','white'].indexOf(this.weight) > -1) {
4230             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4231         }
4232         cfg.cls += ' bg-' + this.weight;
4233         
4234         
4235         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4236             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4237             
4238             // tag can override this..
4239             
4240             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4241         }
4242         
4243         if (this.brand !== '') {
4244             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4245             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4246                 tag: 'a',
4247                 href: this.brand_href ? this.brand_href : '#',
4248                 cls: 'navbar-brand',
4249                 cn: [
4250                 this.brand
4251                 ]
4252             });
4253         }
4254         
4255         if(this.main){
4256             cfg.cls += ' main-nav';
4257         }
4258         
4259         
4260         return cfg;
4261
4262         
4263     },
4264     getHeaderChildContainer : function()
4265     {
4266         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4267             return this.el.select('.navbar-header',true).first();
4268         }
4269         
4270         return this.getChildContainer();
4271     },
4272     
4273     
4274     initEvents : function()
4275     {
4276         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4277         
4278         if (this.autohide) {
4279             
4280             var prevScroll = 0;
4281             var ft = this.el;
4282             
4283             Roo.get(document).on('scroll',function(e) {
4284                 var ns = Roo.get(document).getScroll().top;
4285                 var os = prevScroll;
4286                 prevScroll = ns;
4287                 
4288                 if(ns > os){
4289                     ft.removeClass('slideDown');
4290                     ft.addClass('slideUp');
4291                     return;
4292                 }
4293                 ft.removeClass('slideUp');
4294                 ft.addClass('slideDown');
4295                  
4296               
4297           },this);
4298         }
4299     }    
4300     
4301 });
4302
4303
4304
4305  
4306
4307  /*
4308  * - LGPL
4309  *
4310  * navbar
4311  * 
4312  */
4313
4314 /**
4315  * @class Roo.bootstrap.NavSidebar
4316  * @extends Roo.bootstrap.Navbar
4317  * Bootstrap Sidebar class
4318  * 
4319  * @constructor
4320  * Create a new Sidebar
4321  * @param {Object} config The config object
4322  */
4323
4324
4325 Roo.bootstrap.NavSidebar = function(config){
4326     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4327 };
4328
4329 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4330     
4331     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4332     
4333     getAutoCreate : function(){
4334         
4335         
4336         return  {
4337             tag: 'div',
4338             cls: 'sidebar sidebar-nav'
4339         };
4340     
4341         
4342     }
4343     
4344     
4345     
4346 });
4347
4348
4349
4350  
4351
4352  /*
4353  * - LGPL
4354  *
4355  * nav group
4356  * 
4357  */
4358
4359 /**
4360  * @class Roo.bootstrap.NavGroup
4361  * @extends Roo.bootstrap.Component
4362  * Bootstrap NavGroup class
4363  * @cfg {String} align (left|right)
4364  * @cfg {Boolean} inverse
4365  * @cfg {String} type (nav|pills|tab) default nav
4366  * @cfg {String} navId - reference Id for navbar.
4367
4368  * 
4369  * @constructor
4370  * Create a new nav group
4371  * @param {Object} config The config object
4372  */
4373
4374 Roo.bootstrap.NavGroup = function(config){
4375     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4376     this.navItems = [];
4377    
4378     Roo.bootstrap.NavGroup.register(this);
4379      this.addEvents({
4380         /**
4381              * @event changed
4382              * Fires when the active item changes
4383              * @param {Roo.bootstrap.NavGroup} this
4384              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4385              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4386          */
4387         'changed': true
4388      });
4389     
4390 };
4391
4392 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4393     
4394     align: '',
4395     inverse: false,
4396     form: false,
4397     type: 'nav',
4398     navId : '',
4399     // private
4400     
4401     navItems : false, 
4402     
4403     getAutoCreate : function()
4404     {
4405         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4406         
4407         cfg = {
4408             tag : 'ul',
4409             cls: 'nav' 
4410         };
4411         if (Roo.bootstrap.version == 4) {
4412             if (['tabs','pills'].indexOf(this.type) != -1) {
4413                 cfg.cls += ' nav-' + this.type; 
4414             } else {
4415                 cfg.cls += ' navbar-nav';
4416             }
4417         } else {
4418             if (['tabs','pills'].indexOf(this.type) != -1) {
4419                 cfg.cls += ' nav-' + this.type
4420             } else {
4421                 if (this.type !== 'nav') {
4422                     Roo.log('nav type must be nav/tabs/pills')
4423                 }
4424                 cfg.cls += ' navbar-nav'
4425             }
4426         }
4427         
4428         if (this.parent() && this.parent().sidebar) {
4429             cfg = {
4430                 tag: 'ul',
4431                 cls: 'dashboard-menu sidebar-menu'
4432             };
4433             
4434             return cfg;
4435         }
4436         
4437         if (this.form === true) {
4438             cfg = {
4439                 tag: 'form',
4440                 cls: 'navbar-form form-inline'
4441             };
4442             
4443             if (this.align === 'right') {
4444                 cfg.cls += ' navbar-right ml-md-auto';
4445             } else {
4446                 cfg.cls += ' navbar-left';
4447             }
4448         }
4449         
4450         if (this.align === 'right') {
4451             cfg.cls += ' navbar-right ml-md-auto';
4452         } else {
4453             cfg.cls += ' mr-auto';
4454         }
4455         
4456         if (this.inverse) {
4457             cfg.cls += ' navbar-inverse';
4458             
4459         }
4460         
4461         
4462         return cfg;
4463     },
4464     /**
4465     * sets the active Navigation item
4466     * @param {Roo.bootstrap.NavItem} the new current navitem
4467     */
4468     setActiveItem : function(item)
4469     {
4470         var prev = false;
4471         Roo.each(this.navItems, function(v){
4472             if (v == item) {
4473                 return ;
4474             }
4475             if (v.isActive()) {
4476                 v.setActive(false, true);
4477                 prev = v;
4478                 
4479             }
4480             
4481         });
4482
4483         item.setActive(true, true);
4484         this.fireEvent('changed', this, item, prev);
4485         
4486         
4487     },
4488     /**
4489     * gets the active Navigation item
4490     * @return {Roo.bootstrap.NavItem} the current navitem
4491     */
4492     getActive : function()
4493     {
4494         
4495         var prev = false;
4496         Roo.each(this.navItems, function(v){
4497             
4498             if (v.isActive()) {
4499                 prev = v;
4500                 
4501             }
4502             
4503         });
4504         return prev;
4505     },
4506     
4507     indexOfNav : function()
4508     {
4509         
4510         var prev = false;
4511         Roo.each(this.navItems, function(v,i){
4512             
4513             if (v.isActive()) {
4514                 prev = i;
4515                 
4516             }
4517             
4518         });
4519         return prev;
4520     },
4521     /**
4522     * adds a Navigation item
4523     * @param {Roo.bootstrap.NavItem} the navitem to add
4524     */
4525     addItem : function(cfg)
4526     {
4527         if (this.form && Roo.bootstrap.version == 4) {
4528             cfg.tag = 'div';
4529         }
4530         var cn = new Roo.bootstrap.NavItem(cfg);
4531         this.register(cn);
4532         cn.parentId = this.id;
4533         cn.onRender(this.el, null);
4534         return cn;
4535     },
4536     /**
4537     * register a Navigation item
4538     * @param {Roo.bootstrap.NavItem} the navitem to add
4539     */
4540     register : function(item)
4541     {
4542         this.navItems.push( item);
4543         item.navId = this.navId;
4544     
4545     },
4546     
4547     /**
4548     * clear all the Navigation item
4549     */
4550    
4551     clearAll : function()
4552     {
4553         this.navItems = [];
4554         this.el.dom.innerHTML = '';
4555     },
4556     
4557     getNavItem: function(tabId)
4558     {
4559         var ret = false;
4560         Roo.each(this.navItems, function(e) {
4561             if (e.tabId == tabId) {
4562                ret =  e;
4563                return false;
4564             }
4565             return true;
4566             
4567         });
4568         return ret;
4569     },
4570     
4571     setActiveNext : function()
4572     {
4573         var i = this.indexOfNav(this.getActive());
4574         if (i > this.navItems.length) {
4575             return;
4576         }
4577         this.setActiveItem(this.navItems[i+1]);
4578     },
4579     setActivePrev : function()
4580     {
4581         var i = this.indexOfNav(this.getActive());
4582         if (i  < 1) {
4583             return;
4584         }
4585         this.setActiveItem(this.navItems[i-1]);
4586     },
4587     clearWasActive : function(except) {
4588         Roo.each(this.navItems, function(e) {
4589             if (e.tabId != except.tabId && e.was_active) {
4590                e.was_active = false;
4591                return false;
4592             }
4593             return true;
4594             
4595         });
4596     },
4597     getWasActive : function ()
4598     {
4599         var r = false;
4600         Roo.each(this.navItems, function(e) {
4601             if (e.was_active) {
4602                r = e;
4603                return false;
4604             }
4605             return true;
4606             
4607         });
4608         return r;
4609     }
4610     
4611     
4612 });
4613
4614  
4615 Roo.apply(Roo.bootstrap.NavGroup, {
4616     
4617     groups: {},
4618      /**
4619     * register a Navigation Group
4620     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4621     */
4622     register : function(navgrp)
4623     {
4624         this.groups[navgrp.navId] = navgrp;
4625         
4626     },
4627     /**
4628     * fetch a Navigation Group based on the navigation ID
4629     * @param {string} the navgroup to add
4630     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4631     */
4632     get: function(navId) {
4633         if (typeof(this.groups[navId]) == 'undefined') {
4634             return false;
4635             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4636         }
4637         return this.groups[navId] ;
4638     }
4639     
4640     
4641     
4642 });
4643
4644  /*
4645  * - LGPL
4646  *
4647  * row
4648  * 
4649  */
4650
4651 /**
4652  * @class Roo.bootstrap.NavItem
4653  * @extends Roo.bootstrap.Component
4654  * Bootstrap Navbar.NavItem class
4655  * @cfg {String} href  link to
4656  * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
4657
4658  * @cfg {String} html content of button
4659  * @cfg {String} badge text inside badge
4660  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4661  * @cfg {String} glyphicon DEPRICATED - use fa
4662  * @cfg {String} icon DEPRICATED - use fa
4663  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4664  * @cfg {Boolean} active Is item active
4665  * @cfg {Boolean} disabled Is item disabled
4666  
4667  * @cfg {Boolean} preventDefault (true | false) default false
4668  * @cfg {String} tabId the tab that this item activates.
4669  * @cfg {String} tagtype (a|span) render as a href or span?
4670  * @cfg {Boolean} animateRef (true|false) link to element default false  
4671   
4672  * @constructor
4673  * Create a new Navbar Item
4674  * @param {Object} config The config object
4675  */
4676 Roo.bootstrap.NavItem = function(config){
4677     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4678     this.addEvents({
4679         // raw events
4680         /**
4681          * @event click
4682          * The raw click event for the entire grid.
4683          * @param {Roo.EventObject} e
4684          */
4685         "click" : true,
4686          /**
4687             * @event changed
4688             * Fires when the active item active state changes
4689             * @param {Roo.bootstrap.NavItem} this
4690             * @param {boolean} state the new state
4691              
4692          */
4693         'changed': true,
4694         /**
4695             * @event scrollto
4696             * Fires when scroll to element
4697             * @param {Roo.bootstrap.NavItem} this
4698             * @param {Object} options
4699             * @param {Roo.EventObject} e
4700              
4701          */
4702         'scrollto': true
4703     });
4704    
4705 };
4706
4707 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4708     
4709     href: false,
4710     html: '',
4711     badge: '',
4712     icon: false,
4713     fa : false,
4714     glyphicon: false,
4715     active: false,
4716     preventDefault : false,
4717     tabId : false,
4718     tagtype : 'a',
4719     tag: 'li',
4720     disabled : false,
4721     animateRef : false,
4722     was_active : false,
4723     button_weight : '',
4724     button_outline : false,
4725     
4726     navLink: false,
4727     
4728     getAutoCreate : function(){
4729          
4730         var cfg = {
4731             tag: this.tag,
4732             cls: 'nav-item'
4733         };
4734         
4735         if (this.active) {
4736             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4737         }
4738         if (this.disabled) {
4739             cfg.cls += ' disabled';
4740         }
4741         
4742         // BS4 only?
4743         if (this.button_weight.length) {
4744             cfg.tag = this.href ? 'a' : 'button';
4745             cfg.html = this.html || '';
4746             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
4747             if (this.href) {
4748                 cfg.href = this.href;
4749             }
4750             if (this.fa) {
4751                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
4752             }
4753             
4754             // menu .. should add dropdown-menu class - so no need for carat..
4755             
4756             if (this.badge !== '') {
4757                  
4758                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4759             }
4760             return cfg;
4761         }
4762         
4763         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
4764             cfg.cn = [
4765                 {
4766                     tag: this.tagtype,
4767                     href : this.href || "#",
4768                     html: this.html || ''
4769                 }
4770             ];
4771             if (this.tagtype == 'a') {
4772                 cfg.cn[0].cls = 'nav-link';
4773             }
4774             if (this.icon) {
4775                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
4776             }
4777             if (this.fa) {
4778                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
4779             }
4780             if(this.glyphicon) {
4781                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4782             }
4783             
4784             if (this.menu) {
4785                 
4786                 cfg.cn[0].html += " <span class='caret'></span>";
4787              
4788             }
4789             
4790             if (this.badge !== '') {
4791                  
4792                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4793             }
4794         }
4795         
4796         
4797         
4798         return cfg;
4799     },
4800     onRender : function(ct, position)
4801     {
4802        // Roo.log("Call onRender: " + this.xtype);
4803         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
4804             this.tag = 'div';
4805         }
4806         
4807         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
4808         this.navLink = this.el.select('.nav-link',true).first();
4809         return ret;
4810     },
4811       
4812     
4813     initEvents: function() 
4814     {
4815         if (typeof (this.menu) != 'undefined') {
4816             this.menu.parentType = this.xtype;
4817             this.menu.triggerEl = this.el;
4818             this.menu = this.addxtype(Roo.apply({}, this.menu));
4819         }
4820         
4821         this.el.select('a',true).on('click', this.onClick, this);
4822         
4823         if(this.tagtype == 'span'){
4824             this.el.select('span',true).on('click', this.onClick, this);
4825         }
4826        
4827         // at this point parent should be available..
4828         this.parent().register(this);
4829     },
4830     
4831     onClick : function(e)
4832     {
4833         if (e.getTarget('.dropdown-menu-item')) {
4834             // did you click on a menu itemm.... - then don't trigger onclick..
4835             return;
4836         }
4837         
4838         if(
4839                 this.preventDefault || 
4840                 this.href == '#' 
4841         ){
4842             Roo.log("NavItem - prevent Default?");
4843             e.preventDefault();
4844         }
4845         
4846         if (this.disabled) {
4847             return;
4848         }
4849         
4850         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4851         if (tg && tg.transition) {
4852             Roo.log("waiting for the transitionend");
4853             return;
4854         }
4855         
4856         
4857         
4858         //Roo.log("fire event clicked");
4859         if(this.fireEvent('click', this, e) === false){
4860             return;
4861         };
4862         
4863         if(this.tagtype == 'span'){
4864             return;
4865         }
4866         
4867         //Roo.log(this.href);
4868         var ael = this.el.select('a',true).first();
4869         //Roo.log(ael);
4870         
4871         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4872             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4873             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4874                 return; // ignore... - it's a 'hash' to another page.
4875             }
4876             Roo.log("NavItem - prevent Default?");
4877             e.preventDefault();
4878             this.scrollToElement(e);
4879         }
4880         
4881         
4882         var p =  this.parent();
4883    
4884         if (['tabs','pills'].indexOf(p.type)!==-1) {
4885             if (typeof(p.setActiveItem) !== 'undefined') {
4886                 p.setActiveItem(this);
4887             }
4888         }
4889         
4890         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4891         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4892             // remove the collapsed menu expand...
4893             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4894         }
4895     },
4896     
4897     isActive: function () {
4898         return this.active
4899     },
4900     setActive : function(state, fire, is_was_active)
4901     {
4902         if (this.active && !state && this.navId) {
4903             this.was_active = true;
4904             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4905             if (nv) {
4906                 nv.clearWasActive(this);
4907             }
4908             
4909         }
4910         this.active = state;
4911         
4912         if (!state ) {
4913             this.el.removeClass('active');
4914             this.navLink ? this.navLink.removeClass('active') : false;
4915         } else if (!this.el.hasClass('active')) {
4916             
4917             this.el.addClass('active');
4918             if (Roo.bootstrap.version == 4 && this.navLink ) {
4919                 this.navLink.addClass('active');
4920             }
4921             
4922         }
4923         if (fire) {
4924             this.fireEvent('changed', this, state);
4925         }
4926         
4927         // show a panel if it's registered and related..
4928         
4929         if (!this.navId || !this.tabId || !state || is_was_active) {
4930             return;
4931         }
4932         
4933         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4934         if (!tg) {
4935             return;
4936         }
4937         var pan = tg.getPanelByName(this.tabId);
4938         if (!pan) {
4939             return;
4940         }
4941         // if we can not flip to new panel - go back to old nav highlight..
4942         if (false == tg.showPanel(pan)) {
4943             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4944             if (nv) {
4945                 var onav = nv.getWasActive();
4946                 if (onav) {
4947                     onav.setActive(true, false, true);
4948                 }
4949             }
4950             
4951         }
4952         
4953         
4954         
4955     },
4956      // this should not be here...
4957     setDisabled : function(state)
4958     {
4959         this.disabled = state;
4960         if (!state ) {
4961             this.el.removeClass('disabled');
4962         } else if (!this.el.hasClass('disabled')) {
4963             this.el.addClass('disabled');
4964         }
4965         
4966     },
4967     
4968     /**
4969      * Fetch the element to display the tooltip on.
4970      * @return {Roo.Element} defaults to this.el
4971      */
4972     tooltipEl : function()
4973     {
4974         return this.el.select('' + this.tagtype + '', true).first();
4975     },
4976     
4977     scrollToElement : function(e)
4978     {
4979         var c = document.body;
4980         
4981         /*
4982          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4983          */
4984         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4985             c = document.documentElement;
4986         }
4987         
4988         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4989         
4990         if(!target){
4991             return;
4992         }
4993
4994         var o = target.calcOffsetsTo(c);
4995         
4996         var options = {
4997             target : target,
4998             value : o[1]
4999         };
5000         
5001         this.fireEvent('scrollto', this, options, e);
5002         
5003         Roo.get(c).scrollTo('top', options.value, true);
5004         
5005         return;
5006     }
5007 });
5008  
5009
5010  /*
5011  * - LGPL
5012  *
5013  * sidebar item
5014  *
5015  *  li
5016  *    <span> icon </span>
5017  *    <span> text </span>
5018  *    <span>badge </span>
5019  */
5020
5021 /**
5022  * @class Roo.bootstrap.NavSidebarItem
5023  * @extends Roo.bootstrap.NavItem
5024  * Bootstrap Navbar.NavSidebarItem class
5025  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
5026  * {Boolean} open is the menu open
5027  * {Boolean} buttonView use button as the tigger el rather that a (default false)
5028  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
5029  * {String} buttonSize (sm|md|lg)the extra classes for the button
5030  * {Boolean} showArrow show arrow next to the text (default true)
5031  * @constructor
5032  * Create a new Navbar Button
5033  * @param {Object} config The config object
5034  */
5035 Roo.bootstrap.NavSidebarItem = function(config){
5036     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
5037     this.addEvents({
5038         // raw events
5039         /**
5040          * @event click
5041          * The raw click event for the entire grid.
5042          * @param {Roo.EventObject} e
5043          */
5044         "click" : true,
5045          /**
5046             * @event changed
5047             * Fires when the active item active state changes
5048             * @param {Roo.bootstrap.NavSidebarItem} this
5049             * @param {boolean} state the new state
5050              
5051          */
5052         'changed': true
5053     });
5054    
5055 };
5056
5057 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
5058     
5059     badgeWeight : 'default',
5060     
5061     open: false,
5062     
5063     buttonView : false,
5064     
5065     buttonWeight : 'default',
5066     
5067     buttonSize : 'md',
5068     
5069     showArrow : true,
5070     
5071     getAutoCreate : function(){
5072         
5073         
5074         var a = {
5075                 tag: 'a',
5076                 href : this.href || '#',
5077                 cls: '',
5078                 html : '',
5079                 cn : []
5080         };
5081         
5082         if(this.buttonView){
5083             a = {
5084                 tag: 'button',
5085                 href : this.href || '#',
5086                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
5087                 html : this.html,
5088                 cn : []
5089             };
5090         }
5091         
5092         var cfg = {
5093             tag: 'li',
5094             cls: '',
5095             cn: [ a ]
5096         };
5097         
5098         if (this.active) {
5099             cfg.cls += ' active';
5100         }
5101         
5102         if (this.disabled) {
5103             cfg.cls += ' disabled';
5104         }
5105         if (this.open) {
5106             cfg.cls += ' open x-open';
5107         }
5108         // left icon..
5109         if (this.glyphicon || this.icon) {
5110             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
5111             a.cn.push({ tag : 'i', cls : c }) ;
5112         }
5113         
5114         if(!this.buttonView){
5115             var span = {
5116                 tag: 'span',
5117                 html : this.html || ''
5118             };
5119
5120             a.cn.push(span);
5121             
5122         }
5123         
5124         if (this.badge !== '') {
5125             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
5126         }
5127         
5128         if (this.menu) {
5129             
5130             if(this.showArrow){
5131                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5132             }
5133             
5134             a.cls += ' dropdown-toggle treeview' ;
5135         }
5136         
5137         return cfg;
5138     },
5139     
5140     initEvents : function()
5141     { 
5142         if (typeof (this.menu) != 'undefined') {
5143             this.menu.parentType = this.xtype;
5144             this.menu.triggerEl = this.el;
5145             this.menu = this.addxtype(Roo.apply({}, this.menu));
5146         }
5147         
5148         this.el.on('click', this.onClick, this);
5149         
5150         if(this.badge !== ''){
5151             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5152         }
5153         
5154     },
5155     
5156     onClick : function(e)
5157     {
5158         if(this.disabled){
5159             e.preventDefault();
5160             return;
5161         }
5162         
5163         if(this.preventDefault){
5164             e.preventDefault();
5165         }
5166         
5167         this.fireEvent('click', this, e);
5168     },
5169     
5170     disable : function()
5171     {
5172         this.setDisabled(true);
5173     },
5174     
5175     enable : function()
5176     {
5177         this.setDisabled(false);
5178     },
5179     
5180     setDisabled : function(state)
5181     {
5182         if(this.disabled == state){
5183             return;
5184         }
5185         
5186         this.disabled = state;
5187         
5188         if (state) {
5189             this.el.addClass('disabled');
5190             return;
5191         }
5192         
5193         this.el.removeClass('disabled');
5194         
5195         return;
5196     },
5197     
5198     setActive : function(state)
5199     {
5200         if(this.active == state){
5201             return;
5202         }
5203         
5204         this.active = state;
5205         
5206         if (state) {
5207             this.el.addClass('active');
5208             return;
5209         }
5210         
5211         this.el.removeClass('active');
5212         
5213         return;
5214     },
5215     
5216     isActive: function () 
5217     {
5218         return this.active;
5219     },
5220     
5221     setBadge : function(str)
5222     {
5223         if(!this.badgeEl){
5224             return;
5225         }
5226         
5227         this.badgeEl.dom.innerHTML = str;
5228     }
5229     
5230    
5231      
5232  
5233 });
5234  
5235
5236  /*
5237  * - LGPL
5238  *
5239  * row
5240  * 
5241  */
5242
5243 /**
5244  * @class Roo.bootstrap.Row
5245  * @extends Roo.bootstrap.Component
5246  * Bootstrap Row class (contains columns...)
5247  * 
5248  * @constructor
5249  * Create a new Row
5250  * @param {Object} config The config object
5251  */
5252
5253 Roo.bootstrap.Row = function(config){
5254     Roo.bootstrap.Row.superclass.constructor.call(this, config);
5255 };
5256
5257 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
5258     
5259     getAutoCreate : function(){
5260        return {
5261             cls: 'row clearfix'
5262        };
5263     }
5264     
5265     
5266 });
5267
5268  
5269
5270  /*
5271  * - LGPL
5272  *
5273  * element
5274  * 
5275  */
5276
5277 /**
5278  * @class Roo.bootstrap.Element
5279  * @extends Roo.bootstrap.Component
5280  * Bootstrap Element class
5281  * @cfg {String} html contents of the element
5282  * @cfg {String} tag tag of the element
5283  * @cfg {String} cls class of the element
5284  * @cfg {Boolean} preventDefault (true|false) default false
5285  * @cfg {Boolean} clickable (true|false) default false
5286  * 
5287  * @constructor
5288  * Create a new Element
5289  * @param {Object} config The config object
5290  */
5291
5292 Roo.bootstrap.Element = function(config){
5293     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5294     
5295     this.addEvents({
5296         // raw events
5297         /**
5298          * @event click
5299          * When a element is chick
5300          * @param {Roo.bootstrap.Element} this
5301          * @param {Roo.EventObject} e
5302          */
5303         "click" : true
5304     });
5305 };
5306
5307 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5308     
5309     tag: 'div',
5310     cls: '',
5311     html: '',
5312     preventDefault: false, 
5313     clickable: false,
5314     
5315     getAutoCreate : function(){
5316         
5317         var cfg = {
5318             tag: this.tag,
5319             // cls: this.cls, double assign in parent class Component.js :: onRender
5320             html: this.html
5321         };
5322         
5323         return cfg;
5324     },
5325     
5326     initEvents: function() 
5327     {
5328         Roo.bootstrap.Element.superclass.initEvents.call(this);
5329         
5330         if(this.clickable){
5331             this.el.on('click', this.onClick, this);
5332         }
5333         
5334     },
5335     
5336     onClick : function(e)
5337     {
5338         if(this.preventDefault){
5339             e.preventDefault();
5340         }
5341         
5342         this.fireEvent('click', this, e);
5343     },
5344     
5345     getValue : function()
5346     {
5347         return this.el.dom.innerHTML;
5348     },
5349     
5350     setValue : function(value)
5351     {
5352         this.el.dom.innerHTML = value;
5353     }
5354    
5355 });
5356
5357  
5358
5359  /*
5360  * - LGPL
5361  *
5362  * pagination
5363  * 
5364  */
5365
5366 /**
5367  * @class Roo.bootstrap.Pagination
5368  * @extends Roo.bootstrap.Component
5369  * Bootstrap Pagination class
5370  * @cfg {String} size xs | sm | md | lg
5371  * @cfg {Boolean} inverse false | true
5372  * 
5373  * @constructor
5374  * Create a new Pagination
5375  * @param {Object} config The config object
5376  */
5377
5378 Roo.bootstrap.Pagination = function(config){
5379     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5380 };
5381
5382 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5383     
5384     cls: false,
5385     size: false,
5386     inverse: false,
5387     
5388     getAutoCreate : function(){
5389         var cfg = {
5390             tag: 'ul',
5391                 cls: 'pagination'
5392         };
5393         if (this.inverse) {
5394             cfg.cls += ' inverse';
5395         }
5396         if (this.html) {
5397             cfg.html=this.html;
5398         }
5399         if (this.cls) {
5400             cfg.cls += " " + this.cls;
5401         }
5402         return cfg;
5403     }
5404    
5405 });
5406
5407  
5408
5409  /*
5410  * - LGPL
5411  *
5412  * Pagination item
5413  * 
5414  */
5415
5416
5417 /**
5418  * @class Roo.bootstrap.PaginationItem
5419  * @extends Roo.bootstrap.Component
5420  * Bootstrap PaginationItem class
5421  * @cfg {String} html text
5422  * @cfg {String} href the link
5423  * @cfg {Boolean} preventDefault (true | false) default true
5424  * @cfg {Boolean} active (true | false) default false
5425  * @cfg {Boolean} disabled default false
5426  * 
5427  * 
5428  * @constructor
5429  * Create a new PaginationItem
5430  * @param {Object} config The config object
5431  */
5432
5433
5434 Roo.bootstrap.PaginationItem = function(config){
5435     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5436     this.addEvents({
5437         // raw events
5438         /**
5439          * @event click
5440          * The raw click event for the entire grid.
5441          * @param {Roo.EventObject} e
5442          */
5443         "click" : true
5444     });
5445 };
5446
5447 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5448     
5449     href : false,
5450     html : false,
5451     preventDefault: true,
5452     active : false,
5453     cls : false,
5454     disabled: false,
5455     
5456     getAutoCreate : function(){
5457         var cfg= {
5458             tag: 'li',
5459             cn: [
5460                 {
5461                     tag : 'a',
5462                     href : this.href ? this.href : '#',
5463                     html : this.html ? this.html : ''
5464                 }
5465             ]
5466         };
5467         
5468         if(this.cls){
5469             cfg.cls = this.cls;
5470         }
5471         
5472         if(this.disabled){
5473             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5474         }
5475         
5476         if(this.active){
5477             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5478         }
5479         
5480         return cfg;
5481     },
5482     
5483     initEvents: function() {
5484         
5485         this.el.on('click', this.onClick, this);
5486         
5487     },
5488     onClick : function(e)
5489     {
5490         Roo.log('PaginationItem on click ');
5491         if(this.preventDefault){
5492             e.preventDefault();
5493         }
5494         
5495         if(this.disabled){
5496             return;
5497         }
5498         
5499         this.fireEvent('click', this, e);
5500     }
5501    
5502 });
5503
5504  
5505
5506  /*
5507  * - LGPL
5508  *
5509  * slider
5510  * 
5511  */
5512
5513
5514 /**
5515  * @class Roo.bootstrap.Slider
5516  * @extends Roo.bootstrap.Component
5517  * Bootstrap Slider class
5518  *    
5519  * @constructor
5520  * Create a new Slider
5521  * @param {Object} config The config object
5522  */
5523
5524 Roo.bootstrap.Slider = function(config){
5525     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5526 };
5527
5528 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5529     
5530     getAutoCreate : function(){
5531         
5532         var cfg = {
5533             tag: 'div',
5534             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5535             cn: [
5536                 {
5537                     tag: 'a',
5538                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5539                 }
5540             ]
5541         };
5542         
5543         return cfg;
5544     }
5545    
5546 });
5547
5548  /*
5549  * Based on:
5550  * Ext JS Library 1.1.1
5551  * Copyright(c) 2006-2007, Ext JS, LLC.
5552  *
5553  * Originally Released Under LGPL - original licence link has changed is not relivant.
5554  *
5555  * Fork - LGPL
5556  * <script type="text/javascript">
5557  */
5558  
5559
5560 /**
5561  * @class Roo.grid.ColumnModel
5562  * @extends Roo.util.Observable
5563  * This is the default implementation of a ColumnModel used by the Grid. It defines
5564  * the columns in the grid.
5565  * <br>Usage:<br>
5566  <pre><code>
5567  var colModel = new Roo.grid.ColumnModel([
5568         {header: "Ticker", width: 60, sortable: true, locked: true},
5569         {header: "Company Name", width: 150, sortable: true},
5570         {header: "Market Cap.", width: 100, sortable: true},
5571         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5572         {header: "Employees", width: 100, sortable: true, resizable: false}
5573  ]);
5574  </code></pre>
5575  * <p>
5576  
5577  * The config options listed for this class are options which may appear in each
5578  * individual column definition.
5579  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5580  * @constructor
5581  * @param {Object} config An Array of column config objects. See this class's
5582  * config objects for details.
5583 */
5584 Roo.grid.ColumnModel = function(config){
5585         /**
5586      * The config passed into the constructor
5587      */
5588     this.config = config;
5589     this.lookup = {};
5590
5591     // if no id, create one
5592     // if the column does not have a dataIndex mapping,
5593     // map it to the order it is in the config
5594     for(var i = 0, len = config.length; i < len; i++){
5595         var c = config[i];
5596         if(typeof c.dataIndex == "undefined"){
5597             c.dataIndex = i;
5598         }
5599         if(typeof c.renderer == "string"){
5600             c.renderer = Roo.util.Format[c.renderer];
5601         }
5602         if(typeof c.id == "undefined"){
5603             c.id = Roo.id();
5604         }
5605         if(c.editor && c.editor.xtype){
5606             c.editor  = Roo.factory(c.editor, Roo.grid);
5607         }
5608         if(c.editor && c.editor.isFormField){
5609             c.editor = new Roo.grid.GridEditor(c.editor);
5610         }
5611         this.lookup[c.id] = c;
5612     }
5613
5614     /**
5615      * The width of columns which have no width specified (defaults to 100)
5616      * @type Number
5617      */
5618     this.defaultWidth = 100;
5619
5620     /**
5621      * Default sortable of columns which have no sortable specified (defaults to false)
5622      * @type Boolean
5623      */
5624     this.defaultSortable = false;
5625
5626     this.addEvents({
5627         /**
5628              * @event widthchange
5629              * Fires when the width of a column changes.
5630              * @param {ColumnModel} this
5631              * @param {Number} columnIndex The column index
5632              * @param {Number} newWidth The new width
5633              */
5634             "widthchange": true,
5635         /**
5636              * @event headerchange
5637              * Fires when the text of a header changes.
5638              * @param {ColumnModel} this
5639              * @param {Number} columnIndex The column index
5640              * @param {Number} newText The new header text
5641              */
5642             "headerchange": true,
5643         /**
5644              * @event hiddenchange
5645              * Fires when a column is hidden or "unhidden".
5646              * @param {ColumnModel} this
5647              * @param {Number} columnIndex The column index
5648              * @param {Boolean} hidden true if hidden, false otherwise
5649              */
5650             "hiddenchange": true,
5651             /**
5652          * @event columnmoved
5653          * Fires when a column is moved.
5654          * @param {ColumnModel} this
5655          * @param {Number} oldIndex
5656          * @param {Number} newIndex
5657          */
5658         "columnmoved" : true,
5659         /**
5660          * @event columlockchange
5661          * Fires when a column's locked state is changed
5662          * @param {ColumnModel} this
5663          * @param {Number} colIndex
5664          * @param {Boolean} locked true if locked
5665          */
5666         "columnlockchange" : true
5667     });
5668     Roo.grid.ColumnModel.superclass.constructor.call(this);
5669 };
5670 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5671     /**
5672      * @cfg {String} header The header text to display in the Grid view.
5673      */
5674     /**
5675      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5676      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5677      * specified, the column's index is used as an index into the Record's data Array.
5678      */
5679     /**
5680      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5681      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5682      */
5683     /**
5684      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5685      * Defaults to the value of the {@link #defaultSortable} property.
5686      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5687      */
5688     /**
5689      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5690      */
5691     /**
5692      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5693      */
5694     /**
5695      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5696      */
5697     /**
5698      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5699      */
5700     /**
5701      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5702      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5703      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5704      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5705      */
5706        /**
5707      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5708      */
5709     /**
5710      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5711      */
5712     /**
5713      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5714      */
5715     /**
5716      * @cfg {String} cursor (Optional)
5717      */
5718     /**
5719      * @cfg {String} tooltip (Optional)
5720      */
5721     /**
5722      * @cfg {Number} xs (Optional)
5723      */
5724     /**
5725      * @cfg {Number} sm (Optional)
5726      */
5727     /**
5728      * @cfg {Number} md (Optional)
5729      */
5730     /**
5731      * @cfg {Number} lg (Optional)
5732      */
5733     /**
5734      * Returns the id of the column at the specified index.
5735      * @param {Number} index The column index
5736      * @return {String} the id
5737      */
5738     getColumnId : function(index){
5739         return this.config[index].id;
5740     },
5741
5742     /**
5743      * Returns the column for a specified id.
5744      * @param {String} id The column id
5745      * @return {Object} the column
5746      */
5747     getColumnById : function(id){
5748         return this.lookup[id];
5749     },
5750
5751     
5752     /**
5753      * Returns the column for a specified dataIndex.
5754      * @param {String} dataIndex The column dataIndex
5755      * @return {Object|Boolean} the column or false if not found
5756      */
5757     getColumnByDataIndex: function(dataIndex){
5758         var index = this.findColumnIndex(dataIndex);
5759         return index > -1 ? this.config[index] : false;
5760     },
5761     
5762     /**
5763      * Returns the index for a specified column id.
5764      * @param {String} id The column id
5765      * @return {Number} the index, or -1 if not found
5766      */
5767     getIndexById : function(id){
5768         for(var i = 0, len = this.config.length; i < len; i++){
5769             if(this.config[i].id == id){
5770                 return i;
5771             }
5772         }
5773         return -1;
5774     },
5775     
5776     /**
5777      * Returns the index for a specified column dataIndex.
5778      * @param {String} dataIndex The column dataIndex
5779      * @return {Number} the index, or -1 if not found
5780      */
5781     
5782     findColumnIndex : function(dataIndex){
5783         for(var i = 0, len = this.config.length; i < len; i++){
5784             if(this.config[i].dataIndex == dataIndex){
5785                 return i;
5786             }
5787         }
5788         return -1;
5789     },
5790     
5791     
5792     moveColumn : function(oldIndex, newIndex){
5793         var c = this.config[oldIndex];
5794         this.config.splice(oldIndex, 1);
5795         this.config.splice(newIndex, 0, c);
5796         this.dataMap = null;
5797         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5798     },
5799
5800     isLocked : function(colIndex){
5801         return this.config[colIndex].locked === true;
5802     },
5803
5804     setLocked : function(colIndex, value, suppressEvent){
5805         if(this.isLocked(colIndex) == value){
5806             return;
5807         }
5808         this.config[colIndex].locked = value;
5809         if(!suppressEvent){
5810             this.fireEvent("columnlockchange", this, colIndex, value);
5811         }
5812     },
5813
5814     getTotalLockedWidth : function(){
5815         var totalWidth = 0;
5816         for(var i = 0; i < this.config.length; i++){
5817             if(this.isLocked(i) && !this.isHidden(i)){
5818                 this.totalWidth += this.getColumnWidth(i);
5819             }
5820         }
5821         return totalWidth;
5822     },
5823
5824     getLockedCount : function(){
5825         for(var i = 0, len = this.config.length; i < len; i++){
5826             if(!this.isLocked(i)){
5827                 return i;
5828             }
5829         }
5830         
5831         return this.config.length;
5832     },
5833
5834     /**
5835      * Returns the number of columns.
5836      * @return {Number}
5837      */
5838     getColumnCount : function(visibleOnly){
5839         if(visibleOnly === true){
5840             var c = 0;
5841             for(var i = 0, len = this.config.length; i < len; i++){
5842                 if(!this.isHidden(i)){
5843                     c++;
5844                 }
5845             }
5846             return c;
5847         }
5848         return this.config.length;
5849     },
5850
5851     /**
5852      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5853      * @param {Function} fn
5854      * @param {Object} scope (optional)
5855      * @return {Array} result
5856      */
5857     getColumnsBy : function(fn, scope){
5858         var r = [];
5859         for(var i = 0, len = this.config.length; i < len; i++){
5860             var c = this.config[i];
5861             if(fn.call(scope||this, c, i) === true){
5862                 r[r.length] = c;
5863             }
5864         }
5865         return r;
5866     },
5867
5868     /**
5869      * Returns true if the specified column is sortable.
5870      * @param {Number} col The column index
5871      * @return {Boolean}
5872      */
5873     isSortable : function(col){
5874         if(typeof this.config[col].sortable == "undefined"){
5875             return this.defaultSortable;
5876         }
5877         return this.config[col].sortable;
5878     },
5879
5880     /**
5881      * Returns the rendering (formatting) function defined for the column.
5882      * @param {Number} col The column index.
5883      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5884      */
5885     getRenderer : function(col){
5886         if(!this.config[col].renderer){
5887             return Roo.grid.ColumnModel.defaultRenderer;
5888         }
5889         return this.config[col].renderer;
5890     },
5891
5892     /**
5893      * Sets the rendering (formatting) function for a column.
5894      * @param {Number} col The column index
5895      * @param {Function} fn The function to use to process the cell's raw data
5896      * to return HTML markup for the grid view. The render function is called with
5897      * the following parameters:<ul>
5898      * <li>Data value.</li>
5899      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5900      * <li>css A CSS style string to apply to the table cell.</li>
5901      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5902      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5903      * <li>Row index</li>
5904      * <li>Column index</li>
5905      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5906      */
5907     setRenderer : function(col, fn){
5908         this.config[col].renderer = fn;
5909     },
5910
5911     /**
5912      * Returns the width for the specified column.
5913      * @param {Number} col The column index
5914      * @return {Number}
5915      */
5916     getColumnWidth : function(col){
5917         return this.config[col].width * 1 || this.defaultWidth;
5918     },
5919
5920     /**
5921      * Sets the width for a column.
5922      * @param {Number} col The column index
5923      * @param {Number} width The new width
5924      */
5925     setColumnWidth : function(col, width, suppressEvent){
5926         this.config[col].width = width;
5927         this.totalWidth = null;
5928         if(!suppressEvent){
5929              this.fireEvent("widthchange", this, col, width);
5930         }
5931     },
5932
5933     /**
5934      * Returns the total width of all columns.
5935      * @param {Boolean} includeHidden True to include hidden column widths
5936      * @return {Number}
5937      */
5938     getTotalWidth : function(includeHidden){
5939         if(!this.totalWidth){
5940             this.totalWidth = 0;
5941             for(var i = 0, len = this.config.length; i < len; i++){
5942                 if(includeHidden || !this.isHidden(i)){
5943                     this.totalWidth += this.getColumnWidth(i);
5944                 }
5945             }
5946         }
5947         return this.totalWidth;
5948     },
5949
5950     /**
5951      * Returns the header for the specified column.
5952      * @param {Number} col The column index
5953      * @return {String}
5954      */
5955     getColumnHeader : function(col){
5956         return this.config[col].header;
5957     },
5958
5959     /**
5960      * Sets the header for a column.
5961      * @param {Number} col The column index
5962      * @param {String} header The new header
5963      */
5964     setColumnHeader : function(col, header){
5965         this.config[col].header = header;
5966         this.fireEvent("headerchange", this, col, header);
5967     },
5968
5969     /**
5970      * Returns the tooltip for the specified column.
5971      * @param {Number} col The column index
5972      * @return {String}
5973      */
5974     getColumnTooltip : function(col){
5975             return this.config[col].tooltip;
5976     },
5977     /**
5978      * Sets the tooltip for a column.
5979      * @param {Number} col The column index
5980      * @param {String} tooltip The new tooltip
5981      */
5982     setColumnTooltip : function(col, tooltip){
5983             this.config[col].tooltip = tooltip;
5984     },
5985
5986     /**
5987      * Returns the dataIndex for the specified column.
5988      * @param {Number} col The column index
5989      * @return {Number}
5990      */
5991     getDataIndex : function(col){
5992         return this.config[col].dataIndex;
5993     },
5994
5995     /**
5996      * Sets the dataIndex for a column.
5997      * @param {Number} col The column index
5998      * @param {Number} dataIndex The new dataIndex
5999      */
6000     setDataIndex : function(col, dataIndex){
6001         this.config[col].dataIndex = dataIndex;
6002     },
6003
6004     
6005     
6006     /**
6007      * Returns true if the cell is editable.
6008      * @param {Number} colIndex The column index
6009      * @param {Number} rowIndex The row index - this is nto actually used..?
6010      * @return {Boolean}
6011      */
6012     isCellEditable : function(colIndex, rowIndex){
6013         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
6014     },
6015
6016     /**
6017      * Returns the editor defined for the cell/column.
6018      * return false or null to disable editing.
6019      * @param {Number} colIndex The column index
6020      * @param {Number} rowIndex The row index
6021      * @return {Object}
6022      */
6023     getCellEditor : function(colIndex, rowIndex){
6024         return this.config[colIndex].editor;
6025     },
6026
6027     /**
6028      * Sets if a column is editable.
6029      * @param {Number} col The column index
6030      * @param {Boolean} editable True if the column is editable
6031      */
6032     setEditable : function(col, editable){
6033         this.config[col].editable = editable;
6034     },
6035
6036
6037     /**
6038      * Returns true if the column is hidden.
6039      * @param {Number} colIndex The column index
6040      * @return {Boolean}
6041      */
6042     isHidden : function(colIndex){
6043         return this.config[colIndex].hidden;
6044     },
6045
6046
6047     /**
6048      * Returns true if the column width cannot be changed
6049      */
6050     isFixed : function(colIndex){
6051         return this.config[colIndex].fixed;
6052     },
6053
6054     /**
6055      * Returns true if the column can be resized
6056      * @return {Boolean}
6057      */
6058     isResizable : function(colIndex){
6059         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
6060     },
6061     /**
6062      * Sets if a column is hidden.
6063      * @param {Number} colIndex The column index
6064      * @param {Boolean} hidden True if the column is hidden
6065      */
6066     setHidden : function(colIndex, hidden){
6067         this.config[colIndex].hidden = hidden;
6068         this.totalWidth = null;
6069         this.fireEvent("hiddenchange", this, colIndex, hidden);
6070     },
6071
6072     /**
6073      * Sets the editor for a column.
6074      * @param {Number} col The column index
6075      * @param {Object} editor The editor object
6076      */
6077     setEditor : function(col, editor){
6078         this.config[col].editor = editor;
6079     }
6080 });
6081
6082 Roo.grid.ColumnModel.defaultRenderer = function(value)
6083 {
6084     if(typeof value == "object") {
6085         return value;
6086     }
6087         if(typeof value == "string" && value.length < 1){
6088             return "&#160;";
6089         }
6090     
6091         return String.format("{0}", value);
6092 };
6093
6094 // Alias for backwards compatibility
6095 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
6096 /*
6097  * Based on:
6098  * Ext JS Library 1.1.1
6099  * Copyright(c) 2006-2007, Ext JS, LLC.
6100  *
6101  * Originally Released Under LGPL - original licence link has changed is not relivant.
6102  *
6103  * Fork - LGPL
6104  * <script type="text/javascript">
6105  */
6106  
6107 /**
6108  * @class Roo.LoadMask
6109  * A simple utility class for generically masking elements while loading data.  If the element being masked has
6110  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
6111  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
6112  * element's UpdateManager load indicator and will be destroyed after the initial load.
6113  * @constructor
6114  * Create a new LoadMask
6115  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
6116  * @param {Object} config The config object
6117  */
6118 Roo.LoadMask = function(el, config){
6119     this.el = Roo.get(el);
6120     Roo.apply(this, config);
6121     if(this.store){
6122         this.store.on('beforeload', this.onBeforeLoad, this);
6123         this.store.on('load', this.onLoad, this);
6124         this.store.on('loadexception', this.onLoadException, this);
6125         this.removeMask = false;
6126     }else{
6127         var um = this.el.getUpdateManager();
6128         um.showLoadIndicator = false; // disable the default indicator
6129         um.on('beforeupdate', this.onBeforeLoad, this);
6130         um.on('update', this.onLoad, this);
6131         um.on('failure', this.onLoad, this);
6132         this.removeMask = true;
6133     }
6134 };
6135
6136 Roo.LoadMask.prototype = {
6137     /**
6138      * @cfg {Boolean} removeMask
6139      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6140      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
6141      */
6142     /**
6143      * @cfg {String} msg
6144      * The text to display in a centered loading message box (defaults to 'Loading...')
6145      */
6146     msg : 'Loading...',
6147     /**
6148      * @cfg {String} msgCls
6149      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6150      */
6151     msgCls : 'x-mask-loading',
6152
6153     /**
6154      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6155      * @type Boolean
6156      */
6157     disabled: false,
6158
6159     /**
6160      * Disables the mask to prevent it from being displayed
6161      */
6162     disable : function(){
6163        this.disabled = true;
6164     },
6165
6166     /**
6167      * Enables the mask so that it can be displayed
6168      */
6169     enable : function(){
6170         this.disabled = false;
6171     },
6172     
6173     onLoadException : function()
6174     {
6175         Roo.log(arguments);
6176         
6177         if (typeof(arguments[3]) != 'undefined') {
6178             Roo.MessageBox.alert("Error loading",arguments[3]);
6179         } 
6180         /*
6181         try {
6182             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6183                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6184             }   
6185         } catch(e) {
6186             
6187         }
6188         */
6189     
6190         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6191     },
6192     // private
6193     onLoad : function()
6194     {
6195         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6196     },
6197
6198     // private
6199     onBeforeLoad : function(){
6200         if(!this.disabled){
6201             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6202         }
6203     },
6204
6205     // private
6206     destroy : function(){
6207         if(this.store){
6208             this.store.un('beforeload', this.onBeforeLoad, this);
6209             this.store.un('load', this.onLoad, this);
6210             this.store.un('loadexception', this.onLoadException, this);
6211         }else{
6212             var um = this.el.getUpdateManager();
6213             um.un('beforeupdate', this.onBeforeLoad, this);
6214             um.un('update', this.onLoad, this);
6215             um.un('failure', this.onLoad, this);
6216         }
6217     }
6218 };/*
6219  * - LGPL
6220  *
6221  * table
6222  * 
6223  */
6224
6225 /**
6226  * @class Roo.bootstrap.Table
6227  * @extends Roo.bootstrap.Component
6228  * Bootstrap Table class
6229  * @cfg {String} cls table class
6230  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6231  * @cfg {String} bgcolor Specifies the background color for a table
6232  * @cfg {Number} border Specifies whether the table cells should have borders or not
6233  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6234  * @cfg {Number} cellspacing Specifies the space between cells
6235  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6236  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6237  * @cfg {String} sortable Specifies that the table should be sortable
6238  * @cfg {String} summary Specifies a summary of the content of a table
6239  * @cfg {Number} width Specifies the width of a table
6240  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6241  * 
6242  * @cfg {boolean} striped Should the rows be alternative striped
6243  * @cfg {boolean} bordered Add borders to the table
6244  * @cfg {boolean} hover Add hover highlighting
6245  * @cfg {boolean} condensed Format condensed
6246  * @cfg {boolean} responsive Format condensed
6247  * @cfg {Boolean} loadMask (true|false) default false
6248  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6249  * @cfg {Boolean} headerShow (true|false) generate thead, default true
6250  * @cfg {Boolean} rowSelection (true|false) default false
6251  * @cfg {Boolean} cellSelection (true|false) default false
6252  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6253  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
6254  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
6255  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
6256  
6257  * 
6258  * @constructor
6259  * Create a new Table
6260  * @param {Object} config The config object
6261  */
6262
6263 Roo.bootstrap.Table = function(config){
6264     Roo.bootstrap.Table.superclass.constructor.call(this, config);
6265     
6266   
6267     
6268     // BC...
6269     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6270     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6271     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6272     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6273     
6274     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6275     if (this.sm) {
6276         this.sm.grid = this;
6277         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6278         this.sm = this.selModel;
6279         this.sm.xmodule = this.xmodule || false;
6280     }
6281     
6282     if (this.cm && typeof(this.cm.config) == 'undefined') {
6283         this.colModel = new Roo.grid.ColumnModel(this.cm);
6284         this.cm = this.colModel;
6285         this.cm.xmodule = this.xmodule || false;
6286     }
6287     if (this.store) {
6288         this.store= Roo.factory(this.store, Roo.data);
6289         this.ds = this.store;
6290         this.ds.xmodule = this.xmodule || false;
6291          
6292     }
6293     if (this.footer && this.store) {
6294         this.footer.dataSource = this.ds;
6295         this.footer = Roo.factory(this.footer);
6296     }
6297     
6298     /** @private */
6299     this.addEvents({
6300         /**
6301          * @event cellclick
6302          * Fires when a cell is clicked
6303          * @param {Roo.bootstrap.Table} this
6304          * @param {Roo.Element} el
6305          * @param {Number} rowIndex
6306          * @param {Number} columnIndex
6307          * @param {Roo.EventObject} e
6308          */
6309         "cellclick" : true,
6310         /**
6311          * @event celldblclick
6312          * Fires when a cell is double clicked
6313          * @param {Roo.bootstrap.Table} this
6314          * @param {Roo.Element} el
6315          * @param {Number} rowIndex
6316          * @param {Number} columnIndex
6317          * @param {Roo.EventObject} e
6318          */
6319         "celldblclick" : true,
6320         /**
6321          * @event rowclick
6322          * Fires when a row is clicked
6323          * @param {Roo.bootstrap.Table} this
6324          * @param {Roo.Element} el
6325          * @param {Number} rowIndex
6326          * @param {Roo.EventObject} e
6327          */
6328         "rowclick" : true,
6329         /**
6330          * @event rowdblclick
6331          * Fires when a row is double clicked
6332          * @param {Roo.bootstrap.Table} this
6333          * @param {Roo.Element} el
6334          * @param {Number} rowIndex
6335          * @param {Roo.EventObject} e
6336          */
6337         "rowdblclick" : true,
6338         /**
6339          * @event mouseover
6340          * Fires when a mouseover occur
6341          * @param {Roo.bootstrap.Table} this
6342          * @param {Roo.Element} el
6343          * @param {Number} rowIndex
6344          * @param {Number} columnIndex
6345          * @param {Roo.EventObject} e
6346          */
6347         "mouseover" : true,
6348         /**
6349          * @event mouseout
6350          * Fires when a mouseout occur
6351          * @param {Roo.bootstrap.Table} this
6352          * @param {Roo.Element} el
6353          * @param {Number} rowIndex
6354          * @param {Number} columnIndex
6355          * @param {Roo.EventObject} e
6356          */
6357         "mouseout" : true,
6358         /**
6359          * @event rowclass
6360          * Fires when a row is rendered, so you can change add a style to it.
6361          * @param {Roo.bootstrap.Table} this
6362          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6363          */
6364         'rowclass' : true,
6365           /**
6366          * @event rowsrendered
6367          * Fires when all the  rows have been rendered
6368          * @param {Roo.bootstrap.Table} this
6369          */
6370         'rowsrendered' : true,
6371         /**
6372          * @event contextmenu
6373          * The raw contextmenu event for the entire grid.
6374          * @param {Roo.EventObject} e
6375          */
6376         "contextmenu" : true,
6377         /**
6378          * @event rowcontextmenu
6379          * Fires when a row is right clicked
6380          * @param {Roo.bootstrap.Table} this
6381          * @param {Number} rowIndex
6382          * @param {Roo.EventObject} e
6383          */
6384         "rowcontextmenu" : true,
6385         /**
6386          * @event cellcontextmenu
6387          * Fires when a cell is right clicked
6388          * @param {Roo.bootstrap.Table} this
6389          * @param {Number} rowIndex
6390          * @param {Number} cellIndex
6391          * @param {Roo.EventObject} e
6392          */
6393          "cellcontextmenu" : true,
6394          /**
6395          * @event headercontextmenu
6396          * Fires when a header is right clicked
6397          * @param {Roo.bootstrap.Table} this
6398          * @param {Number} columnIndex
6399          * @param {Roo.EventObject} e
6400          */
6401         "headercontextmenu" : true
6402     });
6403 };
6404
6405 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6406     
6407     cls: false,
6408     align: false,
6409     bgcolor: false,
6410     border: false,
6411     cellpadding: false,
6412     cellspacing: false,
6413     frame: false,
6414     rules: false,
6415     sortable: false,
6416     summary: false,
6417     width: false,
6418     striped : false,
6419     scrollBody : false,
6420     bordered: false,
6421     hover:  false,
6422     condensed : false,
6423     responsive : false,
6424     sm : false,
6425     cm : false,
6426     store : false,
6427     loadMask : false,
6428     footerShow : true,
6429     headerShow : true,
6430   
6431     rowSelection : false,
6432     cellSelection : false,
6433     layout : false,
6434     
6435     // Roo.Element - the tbody
6436     mainBody: false,
6437     // Roo.Element - thead element
6438     mainHead: false,
6439     
6440     container: false, // used by gridpanel...
6441     
6442     lazyLoad : false,
6443     
6444     CSS : Roo.util.CSS,
6445     
6446     auto_hide_footer : false,
6447     
6448     getAutoCreate : function()
6449     {
6450         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6451         
6452         cfg = {
6453             tag: 'table',
6454             cls : 'table',
6455             cn : []
6456         };
6457         if (this.scrollBody) {
6458             cfg.cls += ' table-body-fixed';
6459         }    
6460         if (this.striped) {
6461             cfg.cls += ' table-striped';
6462         }
6463         
6464         if (this.hover) {
6465             cfg.cls += ' table-hover';
6466         }
6467         if (this.bordered) {
6468             cfg.cls += ' table-bordered';
6469         }
6470         if (this.condensed) {
6471             cfg.cls += ' table-condensed';
6472         }
6473         if (this.responsive) {
6474             cfg.cls += ' table-responsive';
6475         }
6476         
6477         if (this.cls) {
6478             cfg.cls+=  ' ' +this.cls;
6479         }
6480         
6481         // this lot should be simplifed...
6482         var _t = this;
6483         var cp = [
6484             'align',
6485             'bgcolor',
6486             'border',
6487             'cellpadding',
6488             'cellspacing',
6489             'frame',
6490             'rules',
6491             'sortable',
6492             'summary',
6493             'width'
6494         ].forEach(function(k) {
6495             if (_t[k]) {
6496                 cfg[k] = _t[k];
6497             }
6498         });
6499         
6500         
6501         if (this.layout) {
6502             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6503         }
6504         
6505         if(this.store || this.cm){
6506             if(this.headerShow){
6507                 cfg.cn.push(this.renderHeader());
6508             }
6509             
6510             cfg.cn.push(this.renderBody());
6511             
6512             if(this.footerShow){
6513                 cfg.cn.push(this.renderFooter());
6514             }
6515             // where does this come from?
6516             //cfg.cls+=  ' TableGrid';
6517         }
6518         
6519         return { cn : [ cfg ] };
6520     },
6521     
6522     initEvents : function()
6523     {   
6524         if(!this.store || !this.cm){
6525             return;
6526         }
6527         if (this.selModel) {
6528             this.selModel.initEvents();
6529         }
6530         
6531         
6532         //Roo.log('initEvents with ds!!!!');
6533         
6534         this.mainBody = this.el.select('tbody', true).first();
6535         this.mainHead = this.el.select('thead', true).first();
6536         this.mainFoot = this.el.select('tfoot', true).first();
6537         
6538         
6539         
6540         var _this = this;
6541         
6542         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6543             e.on('click', _this.sort, _this);
6544         });
6545         
6546         this.mainBody.on("click", this.onClick, this);
6547         this.mainBody.on("dblclick", this.onDblClick, this);
6548         
6549         // why is this done????? = it breaks dialogs??
6550         //this.parent().el.setStyle('position', 'relative');
6551         
6552         
6553         if (this.footer) {
6554             this.footer.parentId = this.id;
6555             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6556             
6557             if(this.lazyLoad){
6558                 this.el.select('tfoot tr td').first().addClass('hide');
6559             }
6560         } 
6561         
6562         if(this.loadMask) {
6563             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6564         }
6565         
6566         this.store.on('load', this.onLoad, this);
6567         this.store.on('beforeload', this.onBeforeLoad, this);
6568         this.store.on('update', this.onUpdate, this);
6569         this.store.on('add', this.onAdd, this);
6570         this.store.on("clear", this.clear, this);
6571         
6572         this.el.on("contextmenu", this.onContextMenu, this);
6573         
6574         this.mainBody.on('scroll', this.onBodyScroll, this);
6575         
6576         this.cm.on("headerchange", this.onHeaderChange, this);
6577         
6578         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6579         
6580     },
6581     
6582     onContextMenu : function(e, t)
6583     {
6584         this.processEvent("contextmenu", e);
6585     },
6586     
6587     processEvent : function(name, e)
6588     {
6589         if (name != 'touchstart' ) {
6590             this.fireEvent(name, e);    
6591         }
6592         
6593         var t = e.getTarget();
6594         
6595         var cell = Roo.get(t);
6596         
6597         if(!cell){
6598             return;
6599         }
6600         
6601         if(cell.findParent('tfoot', false, true)){
6602             return;
6603         }
6604         
6605         if(cell.findParent('thead', false, true)){
6606             
6607             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6608                 cell = Roo.get(t).findParent('th', false, true);
6609                 if (!cell) {
6610                     Roo.log("failed to find th in thead?");
6611                     Roo.log(e.getTarget());
6612                     return;
6613                 }
6614             }
6615             
6616             var cellIndex = cell.dom.cellIndex;
6617             
6618             var ename = name == 'touchstart' ? 'click' : name;
6619             this.fireEvent("header" + ename, this, cellIndex, e);
6620             
6621             return;
6622         }
6623         
6624         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6625             cell = Roo.get(t).findParent('td', false, true);
6626             if (!cell) {
6627                 Roo.log("failed to find th in tbody?");
6628                 Roo.log(e.getTarget());
6629                 return;
6630             }
6631         }
6632         
6633         var row = cell.findParent('tr', false, true);
6634         var cellIndex = cell.dom.cellIndex;
6635         var rowIndex = row.dom.rowIndex - 1;
6636         
6637         if(row !== false){
6638             
6639             this.fireEvent("row" + name, this, rowIndex, e);
6640             
6641             if(cell !== false){
6642             
6643                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6644             }
6645         }
6646         
6647     },
6648     
6649     onMouseover : function(e, el)
6650     {
6651         var cell = Roo.get(el);
6652         
6653         if(!cell){
6654             return;
6655         }
6656         
6657         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6658             cell = cell.findParent('td', false, true);
6659         }
6660         
6661         var row = cell.findParent('tr', false, true);
6662         var cellIndex = cell.dom.cellIndex;
6663         var rowIndex = row.dom.rowIndex - 1; // start from 0
6664         
6665         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6666         
6667     },
6668     
6669     onMouseout : function(e, el)
6670     {
6671         var cell = Roo.get(el);
6672         
6673         if(!cell){
6674             return;
6675         }
6676         
6677         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6678             cell = cell.findParent('td', false, true);
6679         }
6680         
6681         var row = cell.findParent('tr', false, true);
6682         var cellIndex = cell.dom.cellIndex;
6683         var rowIndex = row.dom.rowIndex - 1; // start from 0
6684         
6685         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6686         
6687     },
6688     
6689     onClick : function(e, el)
6690     {
6691         var cell = Roo.get(el);
6692         
6693         if(!cell || (!this.cellSelection && !this.rowSelection)){
6694             return;
6695         }
6696         
6697         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6698             cell = cell.findParent('td', false, true);
6699         }
6700         
6701         if(!cell || typeof(cell) == 'undefined'){
6702             return;
6703         }
6704         
6705         var row = cell.findParent('tr', false, true);
6706         
6707         if(!row || typeof(row) == 'undefined'){
6708             return;
6709         }
6710         
6711         var cellIndex = cell.dom.cellIndex;
6712         var rowIndex = this.getRowIndex(row);
6713         
6714         // why??? - should these not be based on SelectionModel?
6715         if(this.cellSelection){
6716             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6717         }
6718         
6719         if(this.rowSelection){
6720             this.fireEvent('rowclick', this, row, rowIndex, e);
6721         }
6722         
6723         
6724     },
6725         
6726     onDblClick : function(e,el)
6727     {
6728         var cell = Roo.get(el);
6729         
6730         if(!cell || (!this.cellSelection && !this.rowSelection)){
6731             return;
6732         }
6733         
6734         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6735             cell = cell.findParent('td', false, true);
6736         }
6737         
6738         if(!cell || typeof(cell) == 'undefined'){
6739             return;
6740         }
6741         
6742         var row = cell.findParent('tr', false, true);
6743         
6744         if(!row || typeof(row) == 'undefined'){
6745             return;
6746         }
6747         
6748         var cellIndex = cell.dom.cellIndex;
6749         var rowIndex = this.getRowIndex(row);
6750         
6751         if(this.cellSelection){
6752             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6753         }
6754         
6755         if(this.rowSelection){
6756             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6757         }
6758     },
6759     
6760     sort : function(e,el)
6761     {
6762         var col = Roo.get(el);
6763         
6764         if(!col.hasClass('sortable')){
6765             return;
6766         }
6767         
6768         var sort = col.attr('sort');
6769         var dir = 'ASC';
6770         
6771         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6772             dir = 'DESC';
6773         }
6774         
6775         this.store.sortInfo = {field : sort, direction : dir};
6776         
6777         if (this.footer) {
6778             Roo.log("calling footer first");
6779             this.footer.onClick('first');
6780         } else {
6781         
6782             this.store.load({ params : { start : 0 } });
6783         }
6784     },
6785     
6786     renderHeader : function()
6787     {
6788         var header = {
6789             tag: 'thead',
6790             cn : []
6791         };
6792         
6793         var cm = this.cm;
6794         this.totalWidth = 0;
6795         
6796         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6797             
6798             var config = cm.config[i];
6799             
6800             var c = {
6801                 tag: 'th',
6802                 cls : 'x-hcol-' + i,
6803                 style : '',
6804                 html: cm.getColumnHeader(i)
6805             };
6806             
6807             var hh = '';
6808             
6809             if(typeof(config.sortable) != 'undefined' && config.sortable){
6810                 c.cls = 'sortable';
6811                 c.html = '<i class="glyphicon"></i>' + c.html;
6812             }
6813             
6814             // could use BS4 hidden-..-down 
6815             
6816             if(typeof(config.lgHeader) != 'undefined'){
6817                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
6818             }
6819             
6820             if(typeof(config.mdHeader) != 'undefined'){
6821                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6822             }
6823             
6824             if(typeof(config.smHeader) != 'undefined'){
6825                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6826             }
6827             
6828             if(typeof(config.xsHeader) != 'undefined'){
6829                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6830             }
6831             
6832             if(hh.length){
6833                 c.html = hh;
6834             }
6835             
6836             if(typeof(config.tooltip) != 'undefined'){
6837                 c.tooltip = config.tooltip;
6838             }
6839             
6840             if(typeof(config.colspan) != 'undefined'){
6841                 c.colspan = config.colspan;
6842             }
6843             
6844             if(typeof(config.hidden) != 'undefined' && config.hidden){
6845                 c.style += ' display:none;';
6846             }
6847             
6848             if(typeof(config.dataIndex) != 'undefined'){
6849                 c.sort = config.dataIndex;
6850             }
6851             
6852            
6853             
6854             if(typeof(config.align) != 'undefined' && config.align.length){
6855                 c.style += ' text-align:' + config.align + ';';
6856             }
6857             
6858             if(typeof(config.width) != 'undefined'){
6859                 c.style += ' width:' + config.width + 'px;';
6860                 this.totalWidth += config.width;
6861             } else {
6862                 this.totalWidth += 100; // assume minimum of 100 per column?
6863             }
6864             
6865             if(typeof(config.cls) != 'undefined'){
6866                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6867             }
6868             
6869             ['xs','sm','md','lg'].map(function(size){
6870                 
6871                 if(typeof(config[size]) == 'undefined'){
6872                     return;
6873                 }
6874                  
6875                 if (!config[size]) { // 0 = hidden
6876                     // BS 4 '0' is treated as hide that column and below.
6877                     c.cls += ' hidden-' + size + ' hidden' + size + 'down';
6878                     return;
6879                 }
6880                 
6881                 c.cls += ' col-' + size + '-' + config[size] + (
6882                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
6883                 );
6884                 
6885                 
6886             });
6887             
6888             header.cn.push(c)
6889         }
6890         
6891         return header;
6892     },
6893     
6894     renderBody : function()
6895     {
6896         var body = {
6897             tag: 'tbody',
6898             cn : [
6899                 {
6900                     tag: 'tr',
6901                     cn : [
6902                         {
6903                             tag : 'td',
6904                             colspan :  this.cm.getColumnCount()
6905                         }
6906                     ]
6907                 }
6908             ]
6909         };
6910         
6911         return body;
6912     },
6913     
6914     renderFooter : function()
6915     {
6916         var footer = {
6917             tag: 'tfoot',
6918             cn : [
6919                 {
6920                     tag: 'tr',
6921                     cn : [
6922                         {
6923                             tag : 'td',
6924                             colspan :  this.cm.getColumnCount()
6925                         }
6926                     ]
6927                 }
6928             ]
6929         };
6930         
6931         return footer;
6932     },
6933     
6934     
6935     
6936     onLoad : function()
6937     {
6938 //        Roo.log('ds onload');
6939         this.clear();
6940         
6941         var _this = this;
6942         var cm = this.cm;
6943         var ds = this.store;
6944         
6945         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6946             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6947             if (_this.store.sortInfo) {
6948                     
6949                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6950                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6951                 }
6952                 
6953                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6954                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6955                 }
6956             }
6957         });
6958         
6959         var tbody =  this.mainBody;
6960               
6961         if(ds.getCount() > 0){
6962             ds.data.each(function(d,rowIndex){
6963                 var row =  this.renderRow(cm, ds, rowIndex);
6964                 
6965                 tbody.createChild(row);
6966                 
6967                 var _this = this;
6968                 
6969                 if(row.cellObjects.length){
6970                     Roo.each(row.cellObjects, function(r){
6971                         _this.renderCellObject(r);
6972                     })
6973                 }
6974                 
6975             }, this);
6976         }
6977         
6978         var tfoot = this.el.select('tfoot', true).first();
6979         
6980         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6981             
6982             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6983             
6984             var total = this.ds.getTotalCount();
6985             
6986             if(this.footer.pageSize < total){
6987                 this.mainFoot.show();
6988             }
6989         }
6990         
6991         Roo.each(this.el.select('tbody td', true).elements, function(e){
6992             e.on('mouseover', _this.onMouseover, _this);
6993         });
6994         
6995         Roo.each(this.el.select('tbody td', true).elements, function(e){
6996             e.on('mouseout', _this.onMouseout, _this);
6997         });
6998         this.fireEvent('rowsrendered', this);
6999         
7000         this.autoSize();
7001     },
7002     
7003     
7004     onUpdate : function(ds,record)
7005     {
7006         this.refreshRow(record);
7007         this.autoSize();
7008     },
7009     
7010     onRemove : function(ds, record, index, isUpdate){
7011         if(isUpdate !== true){
7012             this.fireEvent("beforerowremoved", this, index, record);
7013         }
7014         var bt = this.mainBody.dom;
7015         
7016         var rows = this.el.select('tbody > tr', true).elements;
7017         
7018         if(typeof(rows[index]) != 'undefined'){
7019             bt.removeChild(rows[index].dom);
7020         }
7021         
7022 //        if(bt.rows[index]){
7023 //            bt.removeChild(bt.rows[index]);
7024 //        }
7025         
7026         if(isUpdate !== true){
7027             //this.stripeRows(index);
7028             //this.syncRowHeights(index, index);
7029             //this.layout();
7030             this.fireEvent("rowremoved", this, index, record);
7031         }
7032     },
7033     
7034     onAdd : function(ds, records, rowIndex)
7035     {
7036         //Roo.log('on Add called');
7037         // - note this does not handle multiple adding very well..
7038         var bt = this.mainBody.dom;
7039         for (var i =0 ; i < records.length;i++) {
7040             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
7041             //Roo.log(records[i]);
7042             //Roo.log(this.store.getAt(rowIndex+i));
7043             this.insertRow(this.store, rowIndex + i, false);
7044             return;
7045         }
7046         
7047     },
7048     
7049     
7050     refreshRow : function(record){
7051         var ds = this.store, index;
7052         if(typeof record == 'number'){
7053             index = record;
7054             record = ds.getAt(index);
7055         }else{
7056             index = ds.indexOf(record);
7057         }
7058         this.insertRow(ds, index, true);
7059         this.autoSize();
7060         this.onRemove(ds, record, index+1, true);
7061         this.autoSize();
7062         //this.syncRowHeights(index, index);
7063         //this.layout();
7064         this.fireEvent("rowupdated", this, index, record);
7065     },
7066     
7067     insertRow : function(dm, rowIndex, isUpdate){
7068         
7069         if(!isUpdate){
7070             this.fireEvent("beforerowsinserted", this, rowIndex);
7071         }
7072             //var s = this.getScrollState();
7073         var row = this.renderRow(this.cm, this.store, rowIndex);
7074         // insert before rowIndex..
7075         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
7076         
7077         var _this = this;
7078                 
7079         if(row.cellObjects.length){
7080             Roo.each(row.cellObjects, function(r){
7081                 _this.renderCellObject(r);
7082             })
7083         }
7084             
7085         if(!isUpdate){
7086             this.fireEvent("rowsinserted", this, rowIndex);
7087             //this.syncRowHeights(firstRow, lastRow);
7088             //this.stripeRows(firstRow);
7089             //this.layout();
7090         }
7091         
7092     },
7093     
7094     
7095     getRowDom : function(rowIndex)
7096     {
7097         var rows = this.el.select('tbody > tr', true).elements;
7098         
7099         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
7100         
7101     },
7102     // returns the object tree for a tr..
7103   
7104     
7105     renderRow : function(cm, ds, rowIndex) 
7106     {
7107         var d = ds.getAt(rowIndex);
7108         
7109         var row = {
7110             tag : 'tr',
7111             cls : 'x-row-' + rowIndex,
7112             cn : []
7113         };
7114             
7115         var cellObjects = [];
7116         
7117         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7118             var config = cm.config[i];
7119             
7120             var renderer = cm.getRenderer(i);
7121             var value = '';
7122             var id = false;
7123             
7124             if(typeof(renderer) !== 'undefined'){
7125                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
7126             }
7127             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
7128             // and are rendered into the cells after the row is rendered - using the id for the element.
7129             
7130             if(typeof(value) === 'object'){
7131                 id = Roo.id();
7132                 cellObjects.push({
7133                     container : id,
7134                     cfg : value 
7135                 })
7136             }
7137             
7138             var rowcfg = {
7139                 record: d,
7140                 rowIndex : rowIndex,
7141                 colIndex : i,
7142                 rowClass : ''
7143             };
7144
7145             this.fireEvent('rowclass', this, rowcfg);
7146             
7147             var td = {
7148                 tag: 'td',
7149                 cls : rowcfg.rowClass + ' x-col-' + i,
7150                 style: '',
7151                 html: (typeof(value) === 'object') ? '' : value
7152             };
7153             
7154             if (id) {
7155                 td.id = id;
7156             }
7157             
7158             if(typeof(config.colspan) != 'undefined'){
7159                 td.colspan = config.colspan;
7160             }
7161             
7162             if(typeof(config.hidden) != 'undefined' && config.hidden){
7163                 td.style += ' display:none;';
7164             }
7165             
7166             if(typeof(config.align) != 'undefined' && config.align.length){
7167                 td.style += ' text-align:' + config.align + ';';
7168             }
7169             if(typeof(config.valign) != 'undefined' && config.valign.length){
7170                 td.style += ' vertical-align:' + config.valign + ';';
7171             }
7172             
7173             if(typeof(config.width) != 'undefined'){
7174                 td.style += ' width:' +  config.width + 'px;';
7175             }
7176             
7177             if(typeof(config.cursor) != 'undefined'){
7178                 td.style += ' cursor:' +  config.cursor + ';';
7179             }
7180             
7181             if(typeof(config.cls) != 'undefined'){
7182                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7183             }
7184             
7185             ['xs','sm','md','lg'].map(function(size){
7186                 
7187                 if(typeof(config[size]) == 'undefined'){
7188                     return;
7189                 }
7190                 
7191                 
7192                   
7193                 if (!config[size]) { // 0 = hidden
7194                     // BS 4 '0' is treated as hide that column and below.
7195                     td.cls += ' hidden-' + size + ' hidden' + size + 'down';
7196                     return;
7197                 }
7198                 
7199                 td.cls += ' col-' + size + '-' + config[size] + (
7200                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
7201                 );
7202                  
7203
7204             });
7205             
7206             row.cn.push(td);
7207            
7208         }
7209         
7210         row.cellObjects = cellObjects;
7211         
7212         return row;
7213           
7214     },
7215     
7216     
7217     
7218     onBeforeLoad : function()
7219     {
7220         
7221     },
7222      /**
7223      * Remove all rows
7224      */
7225     clear : function()
7226     {
7227         this.el.select('tbody', true).first().dom.innerHTML = '';
7228     },
7229     /**
7230      * Show or hide a row.
7231      * @param {Number} rowIndex to show or hide
7232      * @param {Boolean} state hide
7233      */
7234     setRowVisibility : function(rowIndex, state)
7235     {
7236         var bt = this.mainBody.dom;
7237         
7238         var rows = this.el.select('tbody > tr', true).elements;
7239         
7240         if(typeof(rows[rowIndex]) == 'undefined'){
7241             return;
7242         }
7243         rows[rowIndex].dom.style.display = state ? '' : 'none';
7244     },
7245     
7246     
7247     getSelectionModel : function(){
7248         if(!this.selModel){
7249             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7250         }
7251         return this.selModel;
7252     },
7253     /*
7254      * Render the Roo.bootstrap object from renderder
7255      */
7256     renderCellObject : function(r)
7257     {
7258         var _this = this;
7259         
7260         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7261         
7262         var t = r.cfg.render(r.container);
7263         
7264         if(r.cfg.cn){
7265             Roo.each(r.cfg.cn, function(c){
7266                 var child = {
7267                     container: t.getChildContainer(),
7268                     cfg: c
7269                 };
7270                 _this.renderCellObject(child);
7271             })
7272         }
7273     },
7274     
7275     getRowIndex : function(row)
7276     {
7277         var rowIndex = -1;
7278         
7279         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7280             if(el != row){
7281                 return;
7282             }
7283             
7284             rowIndex = index;
7285         });
7286         
7287         return rowIndex;
7288     },
7289      /**
7290      * Returns the grid's underlying element = used by panel.Grid
7291      * @return {Element} The element
7292      */
7293     getGridEl : function(){
7294         return this.el;
7295     },
7296      /**
7297      * Forces a resize - used by panel.Grid
7298      * @return {Element} The element
7299      */
7300     autoSize : function()
7301     {
7302         //var ctr = Roo.get(this.container.dom.parentElement);
7303         var ctr = Roo.get(this.el.dom);
7304         
7305         var thd = this.getGridEl().select('thead',true).first();
7306         var tbd = this.getGridEl().select('tbody', true).first();
7307         var tfd = this.getGridEl().select('tfoot', true).first();
7308         
7309         var cw = ctr.getWidth();
7310         
7311         if (tbd) {
7312             
7313             tbd.setSize(ctr.getWidth(),
7314                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7315             );
7316             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7317             cw -= barsize;
7318         }
7319         cw = Math.max(cw, this.totalWidth);
7320         this.getGridEl().select('tr',true).setWidth(cw);
7321         // resize 'expandable coloumn?
7322         
7323         return; // we doe not have a view in this design..
7324         
7325     },
7326     onBodyScroll: function()
7327     {
7328         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7329         if(this.mainHead){
7330             this.mainHead.setStyle({
7331                 'position' : 'relative',
7332                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7333             });
7334         }
7335         
7336         if(this.lazyLoad){
7337             
7338             var scrollHeight = this.mainBody.dom.scrollHeight;
7339             
7340             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7341             
7342             var height = this.mainBody.getHeight();
7343             
7344             if(scrollHeight - height == scrollTop) {
7345                 
7346                 var total = this.ds.getTotalCount();
7347                 
7348                 if(this.footer.cursor + this.footer.pageSize < total){
7349                     
7350                     this.footer.ds.load({
7351                         params : {
7352                             start : this.footer.cursor + this.footer.pageSize,
7353                             limit : this.footer.pageSize
7354                         },
7355                         add : true
7356                     });
7357                 }
7358             }
7359             
7360         }
7361     },
7362     
7363     onHeaderChange : function()
7364     {
7365         var header = this.renderHeader();
7366         var table = this.el.select('table', true).first();
7367         
7368         this.mainHead.remove();
7369         this.mainHead = table.createChild(header, this.mainBody, false);
7370     },
7371     
7372     onHiddenChange : function(colModel, colIndex, hidden)
7373     {
7374         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7375         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7376         
7377         this.CSS.updateRule(thSelector, "display", "");
7378         this.CSS.updateRule(tdSelector, "display", "");
7379         
7380         if(hidden){
7381             this.CSS.updateRule(thSelector, "display", "none");
7382             this.CSS.updateRule(tdSelector, "display", "none");
7383         }
7384         
7385         this.onHeaderChange();
7386         this.onLoad();
7387     },
7388     
7389     setColumnWidth: function(col_index, width)
7390     {
7391         // width = "md-2 xs-2..."
7392         if(!this.colModel.config[col_index]) {
7393             return;
7394         }
7395         
7396         var w = width.split(" ");
7397         
7398         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7399         
7400         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7401         
7402         
7403         for(var j = 0; j < w.length; j++) {
7404             
7405             if(!w[j]) {
7406                 continue;
7407             }
7408             
7409             var size_cls = w[j].split("-");
7410             
7411             if(!Number.isInteger(size_cls[1] * 1)) {
7412                 continue;
7413             }
7414             
7415             if(!this.colModel.config[col_index][size_cls[0]]) {
7416                 continue;
7417             }
7418             
7419             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7420                 continue;
7421             }
7422             
7423             h_row[0].classList.replace(
7424                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7425                 "col-"+size_cls[0]+"-"+size_cls[1]
7426             );
7427             
7428             for(var i = 0; i < rows.length; i++) {
7429                 
7430                 var size_cls = w[j].split("-");
7431                 
7432                 if(!Number.isInteger(size_cls[1] * 1)) {
7433                     continue;
7434                 }
7435                 
7436                 if(!this.colModel.config[col_index][size_cls[0]]) {
7437                     continue;
7438                 }
7439                 
7440                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7441                     continue;
7442                 }
7443                 
7444                 rows[i].classList.replace(
7445                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7446                     "col-"+size_cls[0]+"-"+size_cls[1]
7447                 );
7448             }
7449             
7450             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7451         }
7452     }
7453 });
7454
7455  
7456
7457  /*
7458  * - LGPL
7459  *
7460  * table cell
7461  * 
7462  */
7463
7464 /**
7465  * @class Roo.bootstrap.TableCell
7466  * @extends Roo.bootstrap.Component
7467  * Bootstrap TableCell class
7468  * @cfg {String} html cell contain text
7469  * @cfg {String} cls cell class
7470  * @cfg {String} tag cell tag (td|th) default td
7471  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7472  * @cfg {String} align Aligns the content in a cell
7473  * @cfg {String} axis Categorizes cells
7474  * @cfg {String} bgcolor Specifies the background color of a cell
7475  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7476  * @cfg {Number} colspan Specifies the number of columns a cell should span
7477  * @cfg {String} headers Specifies one or more header cells a cell is related to
7478  * @cfg {Number} height Sets the height of a cell
7479  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7480  * @cfg {Number} rowspan Sets the number of rows a cell should span
7481  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7482  * @cfg {String} valign Vertical aligns the content in a cell
7483  * @cfg {Number} width Specifies the width of a cell
7484  * 
7485  * @constructor
7486  * Create a new TableCell
7487  * @param {Object} config The config object
7488  */
7489
7490 Roo.bootstrap.TableCell = function(config){
7491     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7492 };
7493
7494 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7495     
7496     html: false,
7497     cls: false,
7498     tag: false,
7499     abbr: false,
7500     align: false,
7501     axis: false,
7502     bgcolor: false,
7503     charoff: false,
7504     colspan: false,
7505     headers: false,
7506     height: false,
7507     nowrap: false,
7508     rowspan: false,
7509     scope: false,
7510     valign: false,
7511     width: false,
7512     
7513     
7514     getAutoCreate : function(){
7515         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7516         
7517         cfg = {
7518             tag: 'td'
7519         };
7520         
7521         if(this.tag){
7522             cfg.tag = this.tag;
7523         }
7524         
7525         if (this.html) {
7526             cfg.html=this.html
7527         }
7528         if (this.cls) {
7529             cfg.cls=this.cls
7530         }
7531         if (this.abbr) {
7532             cfg.abbr=this.abbr
7533         }
7534         if (this.align) {
7535             cfg.align=this.align
7536         }
7537         if (this.axis) {
7538             cfg.axis=this.axis
7539         }
7540         if (this.bgcolor) {
7541             cfg.bgcolor=this.bgcolor
7542         }
7543         if (this.charoff) {
7544             cfg.charoff=this.charoff
7545         }
7546         if (this.colspan) {
7547             cfg.colspan=this.colspan
7548         }
7549         if (this.headers) {
7550             cfg.headers=this.headers
7551         }
7552         if (this.height) {
7553             cfg.height=this.height
7554         }
7555         if (this.nowrap) {
7556             cfg.nowrap=this.nowrap
7557         }
7558         if (this.rowspan) {
7559             cfg.rowspan=this.rowspan
7560         }
7561         if (this.scope) {
7562             cfg.scope=this.scope
7563         }
7564         if (this.valign) {
7565             cfg.valign=this.valign
7566         }
7567         if (this.width) {
7568             cfg.width=this.width
7569         }
7570         
7571         
7572         return cfg;
7573     }
7574    
7575 });
7576
7577  
7578
7579  /*
7580  * - LGPL
7581  *
7582  * table row
7583  * 
7584  */
7585
7586 /**
7587  * @class Roo.bootstrap.TableRow
7588  * @extends Roo.bootstrap.Component
7589  * Bootstrap TableRow class
7590  * @cfg {String} cls row class
7591  * @cfg {String} align Aligns the content in a table row
7592  * @cfg {String} bgcolor Specifies a background color for a table row
7593  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7594  * @cfg {String} valign Vertical aligns the content in a table row
7595  * 
7596  * @constructor
7597  * Create a new TableRow
7598  * @param {Object} config The config object
7599  */
7600
7601 Roo.bootstrap.TableRow = function(config){
7602     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7603 };
7604
7605 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7606     
7607     cls: false,
7608     align: false,
7609     bgcolor: false,
7610     charoff: false,
7611     valign: false,
7612     
7613     getAutoCreate : function(){
7614         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7615         
7616         cfg = {
7617             tag: 'tr'
7618         };
7619             
7620         if(this.cls){
7621             cfg.cls = this.cls;
7622         }
7623         if(this.align){
7624             cfg.align = this.align;
7625         }
7626         if(this.bgcolor){
7627             cfg.bgcolor = this.bgcolor;
7628         }
7629         if(this.charoff){
7630             cfg.charoff = this.charoff;
7631         }
7632         if(this.valign){
7633             cfg.valign = this.valign;
7634         }
7635         
7636         return cfg;
7637     }
7638    
7639 });
7640
7641  
7642
7643  /*
7644  * - LGPL
7645  *
7646  * table body
7647  * 
7648  */
7649
7650 /**
7651  * @class Roo.bootstrap.TableBody
7652  * @extends Roo.bootstrap.Component
7653  * Bootstrap TableBody class
7654  * @cfg {String} cls element class
7655  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7656  * @cfg {String} align Aligns the content inside the element
7657  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7658  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7659  * 
7660  * @constructor
7661  * Create a new TableBody
7662  * @param {Object} config The config object
7663  */
7664
7665 Roo.bootstrap.TableBody = function(config){
7666     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7667 };
7668
7669 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7670     
7671     cls: false,
7672     tag: false,
7673     align: false,
7674     charoff: false,
7675     valign: false,
7676     
7677     getAutoCreate : function(){
7678         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7679         
7680         cfg = {
7681             tag: 'tbody'
7682         };
7683             
7684         if (this.cls) {
7685             cfg.cls=this.cls
7686         }
7687         if(this.tag){
7688             cfg.tag = this.tag;
7689         }
7690         
7691         if(this.align){
7692             cfg.align = this.align;
7693         }
7694         if(this.charoff){
7695             cfg.charoff = this.charoff;
7696         }
7697         if(this.valign){
7698             cfg.valign = this.valign;
7699         }
7700         
7701         return cfg;
7702     }
7703     
7704     
7705 //    initEvents : function()
7706 //    {
7707 //        
7708 //        if(!this.store){
7709 //            return;
7710 //        }
7711 //        
7712 //        this.store = Roo.factory(this.store, Roo.data);
7713 //        this.store.on('load', this.onLoad, this);
7714 //        
7715 //        this.store.load();
7716 //        
7717 //    },
7718 //    
7719 //    onLoad: function () 
7720 //    {   
7721 //        this.fireEvent('load', this);
7722 //    }
7723 //    
7724 //   
7725 });
7726
7727  
7728
7729  /*
7730  * Based on:
7731  * Ext JS Library 1.1.1
7732  * Copyright(c) 2006-2007, Ext JS, LLC.
7733  *
7734  * Originally Released Under LGPL - original licence link has changed is not relivant.
7735  *
7736  * Fork - LGPL
7737  * <script type="text/javascript">
7738  */
7739
7740 // as we use this in bootstrap.
7741 Roo.namespace('Roo.form');
7742  /**
7743  * @class Roo.form.Action
7744  * Internal Class used to handle form actions
7745  * @constructor
7746  * @param {Roo.form.BasicForm} el The form element or its id
7747  * @param {Object} config Configuration options
7748  */
7749
7750  
7751  
7752 // define the action interface
7753 Roo.form.Action = function(form, options){
7754     this.form = form;
7755     this.options = options || {};
7756 };
7757 /**
7758  * Client Validation Failed
7759  * @const 
7760  */
7761 Roo.form.Action.CLIENT_INVALID = 'client';
7762 /**
7763  * Server Validation Failed
7764  * @const 
7765  */
7766 Roo.form.Action.SERVER_INVALID = 'server';
7767  /**
7768  * Connect to Server Failed
7769  * @const 
7770  */
7771 Roo.form.Action.CONNECT_FAILURE = 'connect';
7772 /**
7773  * Reading Data from Server Failed
7774  * @const 
7775  */
7776 Roo.form.Action.LOAD_FAILURE = 'load';
7777
7778 Roo.form.Action.prototype = {
7779     type : 'default',
7780     failureType : undefined,
7781     response : undefined,
7782     result : undefined,
7783
7784     // interface method
7785     run : function(options){
7786
7787     },
7788
7789     // interface method
7790     success : function(response){
7791
7792     },
7793
7794     // interface method
7795     handleResponse : function(response){
7796
7797     },
7798
7799     // default connection failure
7800     failure : function(response){
7801         
7802         this.response = response;
7803         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7804         this.form.afterAction(this, false);
7805     },
7806
7807     processResponse : function(response){
7808         this.response = response;
7809         if(!response.responseText){
7810             return true;
7811         }
7812         this.result = this.handleResponse(response);
7813         return this.result;
7814     },
7815
7816     // utility functions used internally
7817     getUrl : function(appendParams){
7818         var url = this.options.url || this.form.url || this.form.el.dom.action;
7819         if(appendParams){
7820             var p = this.getParams();
7821             if(p){
7822                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7823             }
7824         }
7825         return url;
7826     },
7827
7828     getMethod : function(){
7829         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7830     },
7831
7832     getParams : function(){
7833         var bp = this.form.baseParams;
7834         var p = this.options.params;
7835         if(p){
7836             if(typeof p == "object"){
7837                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7838             }else if(typeof p == 'string' && bp){
7839                 p += '&' + Roo.urlEncode(bp);
7840             }
7841         }else if(bp){
7842             p = Roo.urlEncode(bp);
7843         }
7844         return p;
7845     },
7846
7847     createCallback : function(){
7848         return {
7849             success: this.success,
7850             failure: this.failure,
7851             scope: this,
7852             timeout: (this.form.timeout*1000),
7853             upload: this.form.fileUpload ? this.success : undefined
7854         };
7855     }
7856 };
7857
7858 Roo.form.Action.Submit = function(form, options){
7859     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7860 };
7861
7862 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7863     type : 'submit',
7864
7865     haveProgress : false,
7866     uploadComplete : false,
7867     
7868     // uploadProgress indicator.
7869     uploadProgress : function()
7870     {
7871         if (!this.form.progressUrl) {
7872             return;
7873         }
7874         
7875         if (!this.haveProgress) {
7876             Roo.MessageBox.progress("Uploading", "Uploading");
7877         }
7878         if (this.uploadComplete) {
7879            Roo.MessageBox.hide();
7880            return;
7881         }
7882         
7883         this.haveProgress = true;
7884    
7885         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7886         
7887         var c = new Roo.data.Connection();
7888         c.request({
7889             url : this.form.progressUrl,
7890             params: {
7891                 id : uid
7892             },
7893             method: 'GET',
7894             success : function(req){
7895                //console.log(data);
7896                 var rdata = false;
7897                 var edata;
7898                 try  {
7899                    rdata = Roo.decode(req.responseText)
7900                 } catch (e) {
7901                     Roo.log("Invalid data from server..");
7902                     Roo.log(edata);
7903                     return;
7904                 }
7905                 if (!rdata || !rdata.success) {
7906                     Roo.log(rdata);
7907                     Roo.MessageBox.alert(Roo.encode(rdata));
7908                     return;
7909                 }
7910                 var data = rdata.data;
7911                 
7912                 if (this.uploadComplete) {
7913                    Roo.MessageBox.hide();
7914                    return;
7915                 }
7916                    
7917                 if (data){
7918                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7919                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7920                     );
7921                 }
7922                 this.uploadProgress.defer(2000,this);
7923             },
7924        
7925             failure: function(data) {
7926                 Roo.log('progress url failed ');
7927                 Roo.log(data);
7928             },
7929             scope : this
7930         });
7931            
7932     },
7933     
7934     
7935     run : function()
7936     {
7937         // run get Values on the form, so it syncs any secondary forms.
7938         this.form.getValues();
7939         
7940         var o = this.options;
7941         var method = this.getMethod();
7942         var isPost = method == 'POST';
7943         if(o.clientValidation === false || this.form.isValid()){
7944             
7945             if (this.form.progressUrl) {
7946                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7947                     (new Date() * 1) + '' + Math.random());
7948                     
7949             } 
7950             
7951             
7952             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7953                 form:this.form.el.dom,
7954                 url:this.getUrl(!isPost),
7955                 method: method,
7956                 params:isPost ? this.getParams() : null,
7957                 isUpload: this.form.fileUpload,
7958                 formData : this.form.formData
7959             }));
7960             
7961             this.uploadProgress();
7962
7963         }else if (o.clientValidation !== false){ // client validation failed
7964             this.failureType = Roo.form.Action.CLIENT_INVALID;
7965             this.form.afterAction(this, false);
7966         }
7967     },
7968
7969     success : function(response)
7970     {
7971         this.uploadComplete= true;
7972         if (this.haveProgress) {
7973             Roo.MessageBox.hide();
7974         }
7975         
7976         
7977         var result = this.processResponse(response);
7978         if(result === true || result.success){
7979             this.form.afterAction(this, true);
7980             return;
7981         }
7982         if(result.errors){
7983             this.form.markInvalid(result.errors);
7984             this.failureType = Roo.form.Action.SERVER_INVALID;
7985         }
7986         this.form.afterAction(this, false);
7987     },
7988     failure : function(response)
7989     {
7990         this.uploadComplete= true;
7991         if (this.haveProgress) {
7992             Roo.MessageBox.hide();
7993         }
7994         
7995         this.response = response;
7996         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7997         this.form.afterAction(this, false);
7998     },
7999     
8000     handleResponse : function(response){
8001         if(this.form.errorReader){
8002             var rs = this.form.errorReader.read(response);
8003             var errors = [];
8004             if(rs.records){
8005                 for(var i = 0, len = rs.records.length; i < len; i++) {
8006                     var r = rs.records[i];
8007                     errors[i] = r.data;
8008                 }
8009             }
8010             if(errors.length < 1){
8011                 errors = null;
8012             }
8013             return {
8014                 success : rs.success,
8015                 errors : errors
8016             };
8017         }
8018         var ret = false;
8019         try {
8020             ret = Roo.decode(response.responseText);
8021         } catch (e) {
8022             ret = {
8023                 success: false,
8024                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
8025                 errors : []
8026             };
8027         }
8028         return ret;
8029         
8030     }
8031 });
8032
8033
8034 Roo.form.Action.Load = function(form, options){
8035     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
8036     this.reader = this.form.reader;
8037 };
8038
8039 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
8040     type : 'load',
8041
8042     run : function(){
8043         
8044         Roo.Ajax.request(Roo.apply(
8045                 this.createCallback(), {
8046                     method:this.getMethod(),
8047                     url:this.getUrl(false),
8048                     params:this.getParams()
8049         }));
8050     },
8051
8052     success : function(response){
8053         
8054         var result = this.processResponse(response);
8055         if(result === true || !result.success || !result.data){
8056             this.failureType = Roo.form.Action.LOAD_FAILURE;
8057             this.form.afterAction(this, false);
8058             return;
8059         }
8060         this.form.clearInvalid();
8061         this.form.setValues(result.data);
8062         this.form.afterAction(this, true);
8063     },
8064
8065     handleResponse : function(response){
8066         if(this.form.reader){
8067             var rs = this.form.reader.read(response);
8068             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
8069             return {
8070                 success : rs.success,
8071                 data : data
8072             };
8073         }
8074         return Roo.decode(response.responseText);
8075     }
8076 });
8077
8078 Roo.form.Action.ACTION_TYPES = {
8079     'load' : Roo.form.Action.Load,
8080     'submit' : Roo.form.Action.Submit
8081 };/*
8082  * - LGPL
8083  *
8084  * form
8085  *
8086  */
8087
8088 /**
8089  * @class Roo.bootstrap.Form
8090  * @extends Roo.bootstrap.Component
8091  * Bootstrap Form class
8092  * @cfg {String} method  GET | POST (default POST)
8093  * @cfg {String} labelAlign top | left (default top)
8094  * @cfg {String} align left  | right - for navbars
8095  * @cfg {Boolean} loadMask load mask when submit (default true)
8096
8097  *
8098  * @constructor
8099  * Create a new Form
8100  * @param {Object} config The config object
8101  */
8102
8103
8104 Roo.bootstrap.Form = function(config){
8105     
8106     Roo.bootstrap.Form.superclass.constructor.call(this, config);
8107     
8108     Roo.bootstrap.Form.popover.apply();
8109     
8110     this.addEvents({
8111         /**
8112          * @event clientvalidation
8113          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
8114          * @param {Form} this
8115          * @param {Boolean} valid true if the form has passed client-side validation
8116          */
8117         clientvalidation: true,
8118         /**
8119          * @event beforeaction
8120          * Fires before any action is performed. Return false to cancel the action.
8121          * @param {Form} this
8122          * @param {Action} action The action to be performed
8123          */
8124         beforeaction: true,
8125         /**
8126          * @event actionfailed
8127          * Fires when an action fails.
8128          * @param {Form} this
8129          * @param {Action} action The action that failed
8130          */
8131         actionfailed : true,
8132         /**
8133          * @event actioncomplete
8134          * Fires when an action is completed.
8135          * @param {Form} this
8136          * @param {Action} action The action that completed
8137          */
8138         actioncomplete : true
8139     });
8140 };
8141
8142 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
8143
8144      /**
8145      * @cfg {String} method
8146      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8147      */
8148     method : 'POST',
8149     /**
8150      * @cfg {String} url
8151      * The URL to use for form actions if one isn't supplied in the action options.
8152      */
8153     /**
8154      * @cfg {Boolean} fileUpload
8155      * Set to true if this form is a file upload.
8156      */
8157
8158     /**
8159      * @cfg {Object} baseParams
8160      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8161      */
8162
8163     /**
8164      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8165      */
8166     timeout: 30,
8167     /**
8168      * @cfg {Sting} align (left|right) for navbar forms
8169      */
8170     align : 'left',
8171
8172     // private
8173     activeAction : null,
8174
8175     /**
8176      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8177      * element by passing it or its id or mask the form itself by passing in true.
8178      * @type Mixed
8179      */
8180     waitMsgTarget : false,
8181
8182     loadMask : true,
8183     
8184     /**
8185      * @cfg {Boolean} errorMask (true|false) default false
8186      */
8187     errorMask : false,
8188     
8189     /**
8190      * @cfg {Number} maskOffset Default 100
8191      */
8192     maskOffset : 100,
8193     
8194     /**
8195      * @cfg {Boolean} maskBody
8196      */
8197     maskBody : false,
8198
8199     getAutoCreate : function(){
8200
8201         var cfg = {
8202             tag: 'form',
8203             method : this.method || 'POST',
8204             id : this.id || Roo.id(),
8205             cls : ''
8206         };
8207         if (this.parent().xtype.match(/^Nav/)) {
8208             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8209
8210         }
8211
8212         if (this.labelAlign == 'left' ) {
8213             cfg.cls += ' form-horizontal';
8214         }
8215
8216
8217         return cfg;
8218     },
8219     initEvents : function()
8220     {
8221         this.el.on('submit', this.onSubmit, this);
8222         // this was added as random key presses on the form where triggering form submit.
8223         this.el.on('keypress', function(e) {
8224             if (e.getCharCode() != 13) {
8225                 return true;
8226             }
8227             // we might need to allow it for textareas.. and some other items.
8228             // check e.getTarget().
8229
8230             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8231                 return true;
8232             }
8233
8234             Roo.log("keypress blocked");
8235
8236             e.preventDefault();
8237             return false;
8238         });
8239         
8240     },
8241     // private
8242     onSubmit : function(e){
8243         e.stopEvent();
8244     },
8245
8246      /**
8247      * Returns true if client-side validation on the form is successful.
8248      * @return Boolean
8249      */
8250     isValid : function(){
8251         var items = this.getItems();
8252         var valid = true;
8253         var target = false;
8254         
8255         items.each(function(f){
8256             
8257             if(f.validate()){
8258                 return;
8259             }
8260             
8261             Roo.log('invalid field: ' + f.name);
8262             
8263             valid = false;
8264
8265             if(!target && f.el.isVisible(true)){
8266                 target = f;
8267             }
8268            
8269         });
8270         
8271         if(this.errorMask && !valid){
8272             Roo.bootstrap.Form.popover.mask(this, target);
8273         }
8274         
8275         return valid;
8276     },
8277     
8278     /**
8279      * Returns true if any fields in this form have changed since their original load.
8280      * @return Boolean
8281      */
8282     isDirty : function(){
8283         var dirty = false;
8284         var items = this.getItems();
8285         items.each(function(f){
8286            if(f.isDirty()){
8287                dirty = true;
8288                return false;
8289            }
8290            return true;
8291         });
8292         return dirty;
8293     },
8294      /**
8295      * Performs a predefined action (submit or load) or custom actions you define on this form.
8296      * @param {String} actionName The name of the action type
8297      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8298      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8299      * accept other config options):
8300      * <pre>
8301 Property          Type             Description
8302 ----------------  ---------------  ----------------------------------------------------------------------------------
8303 url               String           The url for the action (defaults to the form's url)
8304 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8305 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8306 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8307                                    validate the form on the client (defaults to false)
8308      * </pre>
8309      * @return {BasicForm} this
8310      */
8311     doAction : function(action, options){
8312         if(typeof action == 'string'){
8313             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8314         }
8315         if(this.fireEvent('beforeaction', this, action) !== false){
8316             this.beforeAction(action);
8317             action.run.defer(100, action);
8318         }
8319         return this;
8320     },
8321
8322     // private
8323     beforeAction : function(action){
8324         var o = action.options;
8325         
8326         if(this.loadMask){
8327             
8328             if(this.maskBody){
8329                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8330             } else {
8331                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8332             }
8333         }
8334         // not really supported yet.. ??
8335
8336         //if(this.waitMsgTarget === true){
8337         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8338         //}else if(this.waitMsgTarget){
8339         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8340         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8341         //}else {
8342         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8343        // }
8344
8345     },
8346
8347     // private
8348     afterAction : function(action, success){
8349         this.activeAction = null;
8350         var o = action.options;
8351
8352         if(this.loadMask){
8353             
8354             if(this.maskBody){
8355                 Roo.get(document.body).unmask();
8356             } else {
8357                 this.el.unmask();
8358             }
8359         }
8360         
8361         //if(this.waitMsgTarget === true){
8362 //            this.el.unmask();
8363         //}else if(this.waitMsgTarget){
8364         //    this.waitMsgTarget.unmask();
8365         //}else{
8366         //    Roo.MessageBox.updateProgress(1);
8367         //    Roo.MessageBox.hide();
8368        // }
8369         //
8370         if(success){
8371             if(o.reset){
8372                 this.reset();
8373             }
8374             Roo.callback(o.success, o.scope, [this, action]);
8375             this.fireEvent('actioncomplete', this, action);
8376
8377         }else{
8378
8379             // failure condition..
8380             // we have a scenario where updates need confirming.
8381             // eg. if a locking scenario exists..
8382             // we look for { errors : { needs_confirm : true }} in the response.
8383             if (
8384                 (typeof(action.result) != 'undefined')  &&
8385                 (typeof(action.result.errors) != 'undefined')  &&
8386                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8387            ){
8388                 var _t = this;
8389                 Roo.log("not supported yet");
8390                  /*
8391
8392                 Roo.MessageBox.confirm(
8393                     "Change requires confirmation",
8394                     action.result.errorMsg,
8395                     function(r) {
8396                         if (r != 'yes') {
8397                             return;
8398                         }
8399                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8400                     }
8401
8402                 );
8403                 */
8404
8405
8406                 return;
8407             }
8408
8409             Roo.callback(o.failure, o.scope, [this, action]);
8410             // show an error message if no failed handler is set..
8411             if (!this.hasListener('actionfailed')) {
8412                 Roo.log("need to add dialog support");
8413                 /*
8414                 Roo.MessageBox.alert("Error",
8415                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8416                         action.result.errorMsg :
8417                         "Saving Failed, please check your entries or try again"
8418                 );
8419                 */
8420             }
8421
8422             this.fireEvent('actionfailed', this, action);
8423         }
8424
8425     },
8426     /**
8427      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8428      * @param {String} id The value to search for
8429      * @return Field
8430      */
8431     findField : function(id){
8432         var items = this.getItems();
8433         var field = items.get(id);
8434         if(!field){
8435              items.each(function(f){
8436                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8437                     field = f;
8438                     return false;
8439                 }
8440                 return true;
8441             });
8442         }
8443         return field || null;
8444     },
8445      /**
8446      * Mark fields in this form invalid in bulk.
8447      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8448      * @return {BasicForm} this
8449      */
8450     markInvalid : function(errors){
8451         if(errors instanceof Array){
8452             for(var i = 0, len = errors.length; i < len; i++){
8453                 var fieldError = errors[i];
8454                 var f = this.findField(fieldError.id);
8455                 if(f){
8456                     f.markInvalid(fieldError.msg);
8457                 }
8458             }
8459         }else{
8460             var field, id;
8461             for(id in errors){
8462                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8463                     field.markInvalid(errors[id]);
8464                 }
8465             }
8466         }
8467         //Roo.each(this.childForms || [], function (f) {
8468         //    f.markInvalid(errors);
8469         //});
8470
8471         return this;
8472     },
8473
8474     /**
8475      * Set values for fields in this form in bulk.
8476      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8477      * @return {BasicForm} this
8478      */
8479     setValues : function(values){
8480         if(values instanceof Array){ // array of objects
8481             for(var i = 0, len = values.length; i < len; i++){
8482                 var v = values[i];
8483                 var f = this.findField(v.id);
8484                 if(f){
8485                     f.setValue(v.value);
8486                     if(this.trackResetOnLoad){
8487                         f.originalValue = f.getValue();
8488                     }
8489                 }
8490             }
8491         }else{ // object hash
8492             var field, id;
8493             for(id in values){
8494                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8495
8496                     if (field.setFromData &&
8497                         field.valueField &&
8498                         field.displayField &&
8499                         // combos' with local stores can
8500                         // be queried via setValue()
8501                         // to set their value..
8502                         (field.store && !field.store.isLocal)
8503                         ) {
8504                         // it's a combo
8505                         var sd = { };
8506                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8507                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8508                         field.setFromData(sd);
8509
8510                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8511                         
8512                         field.setFromData(values);
8513                         
8514                     } else {
8515                         field.setValue(values[id]);
8516                     }
8517
8518
8519                     if(this.trackResetOnLoad){
8520                         field.originalValue = field.getValue();
8521                     }
8522                 }
8523             }
8524         }
8525
8526         //Roo.each(this.childForms || [], function (f) {
8527         //    f.setValues(values);
8528         //});
8529
8530         return this;
8531     },
8532
8533     /**
8534      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8535      * they are returned as an array.
8536      * @param {Boolean} asString
8537      * @return {Object}
8538      */
8539     getValues : function(asString){
8540         //if (this.childForms) {
8541             // copy values from the child forms
8542         //    Roo.each(this.childForms, function (f) {
8543         //        this.setValues(f.getValues());
8544         //    }, this);
8545         //}
8546
8547
8548
8549         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8550         if(asString === true){
8551             return fs;
8552         }
8553         return Roo.urlDecode(fs);
8554     },
8555
8556     /**
8557      * Returns the fields in this form as an object with key/value pairs.
8558      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8559      * @return {Object}
8560      */
8561     getFieldValues : function(with_hidden)
8562     {
8563         var items = this.getItems();
8564         var ret = {};
8565         items.each(function(f){
8566             
8567             if (!f.getName()) {
8568                 return;
8569             }
8570             
8571             var v = f.getValue();
8572             
8573             if (f.inputType =='radio') {
8574                 if (typeof(ret[f.getName()]) == 'undefined') {
8575                     ret[f.getName()] = ''; // empty..
8576                 }
8577
8578                 if (!f.el.dom.checked) {
8579                     return;
8580
8581                 }
8582                 v = f.el.dom.value;
8583
8584             }
8585             
8586             if(f.xtype == 'MoneyField'){
8587                 ret[f.currencyName] = f.getCurrency();
8588             }
8589
8590             // not sure if this supported any more..
8591             if ((typeof(v) == 'object') && f.getRawValue) {
8592                 v = f.getRawValue() ; // dates..
8593             }
8594             // combo boxes where name != hiddenName...
8595             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8596                 ret[f.name] = f.getRawValue();
8597             }
8598             ret[f.getName()] = v;
8599         });
8600
8601         return ret;
8602     },
8603
8604     /**
8605      * Clears all invalid messages in this form.
8606      * @return {BasicForm} this
8607      */
8608     clearInvalid : function(){
8609         var items = this.getItems();
8610
8611         items.each(function(f){
8612            f.clearInvalid();
8613         });
8614
8615         return this;
8616     },
8617
8618     /**
8619      * Resets this form.
8620      * @return {BasicForm} this
8621      */
8622     reset : function(){
8623         var items = this.getItems();
8624         items.each(function(f){
8625             f.reset();
8626         });
8627
8628         Roo.each(this.childForms || [], function (f) {
8629             f.reset();
8630         });
8631
8632
8633         return this;
8634     },
8635     
8636     getItems : function()
8637     {
8638         var r=new Roo.util.MixedCollection(false, function(o){
8639             return o.id || (o.id = Roo.id());
8640         });
8641         var iter = function(el) {
8642             if (el.inputEl) {
8643                 r.add(el);
8644             }
8645             if (!el.items) {
8646                 return;
8647             }
8648             Roo.each(el.items,function(e) {
8649                 iter(e);
8650             });
8651         };
8652
8653         iter(this);
8654         return r;
8655     },
8656     
8657     hideFields : function(items)
8658     {
8659         Roo.each(items, function(i){
8660             
8661             var f = this.findField(i);
8662             
8663             if(!f){
8664                 return;
8665             }
8666             
8667             f.hide();
8668             
8669         }, this);
8670     },
8671     
8672     showFields : function(items)
8673     {
8674         Roo.each(items, function(i){
8675             
8676             var f = this.findField(i);
8677             
8678             if(!f){
8679                 return;
8680             }
8681             
8682             f.show();
8683             
8684         }, this);
8685     }
8686
8687 });
8688
8689 Roo.apply(Roo.bootstrap.Form, {
8690     
8691     popover : {
8692         
8693         padding : 5,
8694         
8695         isApplied : false,
8696         
8697         isMasked : false,
8698         
8699         form : false,
8700         
8701         target : false,
8702         
8703         toolTip : false,
8704         
8705         intervalID : false,
8706         
8707         maskEl : false,
8708         
8709         apply : function()
8710         {
8711             if(this.isApplied){
8712                 return;
8713             }
8714             
8715             this.maskEl = {
8716                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8717                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8718                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8719                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8720             };
8721             
8722             this.maskEl.top.enableDisplayMode("block");
8723             this.maskEl.left.enableDisplayMode("block");
8724             this.maskEl.bottom.enableDisplayMode("block");
8725             this.maskEl.right.enableDisplayMode("block");
8726             
8727             this.toolTip = new Roo.bootstrap.Tooltip({
8728                 cls : 'roo-form-error-popover',
8729                 alignment : {
8730                     'left' : ['r-l', [-2,0], 'right'],
8731                     'right' : ['l-r', [2,0], 'left'],
8732                     'bottom' : ['tl-bl', [0,2], 'top'],
8733                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8734                 }
8735             });
8736             
8737             this.toolTip.render(Roo.get(document.body));
8738
8739             this.toolTip.el.enableDisplayMode("block");
8740             
8741             Roo.get(document.body).on('click', function(){
8742                 this.unmask();
8743             }, this);
8744             
8745             Roo.get(document.body).on('touchstart', function(){
8746                 this.unmask();
8747             }, this);
8748             
8749             this.isApplied = true
8750         },
8751         
8752         mask : function(form, target)
8753         {
8754             this.form = form;
8755             
8756             this.target = target;
8757             
8758             if(!this.form.errorMask || !target.el){
8759                 return;
8760             }
8761             
8762             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8763             
8764             Roo.log(scrollable);
8765             
8766             var ot = this.target.el.calcOffsetsTo(scrollable);
8767             
8768             var scrollTo = ot[1] - this.form.maskOffset;
8769             
8770             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8771             
8772             scrollable.scrollTo('top', scrollTo);
8773             
8774             var box = this.target.el.getBox();
8775             Roo.log(box);
8776             var zIndex = Roo.bootstrap.Modal.zIndex++;
8777
8778             
8779             this.maskEl.top.setStyle('position', 'absolute');
8780             this.maskEl.top.setStyle('z-index', zIndex);
8781             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8782             this.maskEl.top.setLeft(0);
8783             this.maskEl.top.setTop(0);
8784             this.maskEl.top.show();
8785             
8786             this.maskEl.left.setStyle('position', 'absolute');
8787             this.maskEl.left.setStyle('z-index', zIndex);
8788             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8789             this.maskEl.left.setLeft(0);
8790             this.maskEl.left.setTop(box.y - this.padding);
8791             this.maskEl.left.show();
8792
8793             this.maskEl.bottom.setStyle('position', 'absolute');
8794             this.maskEl.bottom.setStyle('z-index', zIndex);
8795             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8796             this.maskEl.bottom.setLeft(0);
8797             this.maskEl.bottom.setTop(box.bottom + this.padding);
8798             this.maskEl.bottom.show();
8799
8800             this.maskEl.right.setStyle('position', 'absolute');
8801             this.maskEl.right.setStyle('z-index', zIndex);
8802             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8803             this.maskEl.right.setLeft(box.right + this.padding);
8804             this.maskEl.right.setTop(box.y - this.padding);
8805             this.maskEl.right.show();
8806
8807             this.toolTip.bindEl = this.target.el;
8808
8809             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8810
8811             var tip = this.target.blankText;
8812
8813             if(this.target.getValue() !== '' ) {
8814                 
8815                 if (this.target.invalidText.length) {
8816                     tip = this.target.invalidText;
8817                 } else if (this.target.regexText.length){
8818                     tip = this.target.regexText;
8819                 }
8820             }
8821
8822             this.toolTip.show(tip);
8823
8824             this.intervalID = window.setInterval(function() {
8825                 Roo.bootstrap.Form.popover.unmask();
8826             }, 10000);
8827
8828             window.onwheel = function(){ return false;};
8829             
8830             (function(){ this.isMasked = true; }).defer(500, this);
8831             
8832         },
8833         
8834         unmask : function()
8835         {
8836             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8837                 return;
8838             }
8839             
8840             this.maskEl.top.setStyle('position', 'absolute');
8841             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8842             this.maskEl.top.hide();
8843
8844             this.maskEl.left.setStyle('position', 'absolute');
8845             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8846             this.maskEl.left.hide();
8847
8848             this.maskEl.bottom.setStyle('position', 'absolute');
8849             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8850             this.maskEl.bottom.hide();
8851
8852             this.maskEl.right.setStyle('position', 'absolute');
8853             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8854             this.maskEl.right.hide();
8855             
8856             this.toolTip.hide();
8857             
8858             this.toolTip.el.hide();
8859             
8860             window.onwheel = function(){ return true;};
8861             
8862             if(this.intervalID){
8863                 window.clearInterval(this.intervalID);
8864                 this.intervalID = false;
8865             }
8866             
8867             this.isMasked = false;
8868             
8869         }
8870         
8871     }
8872     
8873 });
8874
8875 /*
8876  * Based on:
8877  * Ext JS Library 1.1.1
8878  * Copyright(c) 2006-2007, Ext JS, LLC.
8879  *
8880  * Originally Released Under LGPL - original licence link has changed is not relivant.
8881  *
8882  * Fork - LGPL
8883  * <script type="text/javascript">
8884  */
8885 /**
8886  * @class Roo.form.VTypes
8887  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8888  * @singleton
8889  */
8890 Roo.form.VTypes = function(){
8891     // closure these in so they are only created once.
8892     var alpha = /^[a-zA-Z_]+$/;
8893     var alphanum = /^[a-zA-Z0-9_]+$/;
8894     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8895     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8896
8897     // All these messages and functions are configurable
8898     return {
8899         /**
8900          * The function used to validate email addresses
8901          * @param {String} value The email address
8902          */
8903         'email' : function(v){
8904             return email.test(v);
8905         },
8906         /**
8907          * The error text to display when the email validation function returns false
8908          * @type String
8909          */
8910         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8911         /**
8912          * The keystroke filter mask to be applied on email input
8913          * @type RegExp
8914          */
8915         'emailMask' : /[a-z0-9_\.\-@]/i,
8916
8917         /**
8918          * The function used to validate URLs
8919          * @param {String} value The URL
8920          */
8921         'url' : function(v){
8922             return url.test(v);
8923         },
8924         /**
8925          * The error text to display when the url validation function returns false
8926          * @type String
8927          */
8928         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8929         
8930         /**
8931          * The function used to validate alpha values
8932          * @param {String} value The value
8933          */
8934         'alpha' : function(v){
8935             return alpha.test(v);
8936         },
8937         /**
8938          * The error text to display when the alpha validation function returns false
8939          * @type String
8940          */
8941         'alphaText' : 'This field should only contain letters and _',
8942         /**
8943          * The keystroke filter mask to be applied on alpha input
8944          * @type RegExp
8945          */
8946         'alphaMask' : /[a-z_]/i,
8947
8948         /**
8949          * The function used to validate alphanumeric values
8950          * @param {String} value The value
8951          */
8952         'alphanum' : function(v){
8953             return alphanum.test(v);
8954         },
8955         /**
8956          * The error text to display when the alphanumeric validation function returns false
8957          * @type String
8958          */
8959         'alphanumText' : 'This field should only contain letters, numbers and _',
8960         /**
8961          * The keystroke filter mask to be applied on alphanumeric input
8962          * @type RegExp
8963          */
8964         'alphanumMask' : /[a-z0-9_]/i
8965     };
8966 }();/*
8967  * - LGPL
8968  *
8969  * Input
8970  * 
8971  */
8972
8973 /**
8974  * @class Roo.bootstrap.Input
8975  * @extends Roo.bootstrap.Component
8976  * Bootstrap Input class
8977  * @cfg {Boolean} disabled is it disabled
8978  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8979  * @cfg {String} name name of the input
8980  * @cfg {string} fieldLabel - the label associated
8981  * @cfg {string} placeholder - placeholder to put in text.
8982  * @cfg {string}  before - input group add on before
8983  * @cfg {string} after - input group add on after
8984  * @cfg {string} size - (lg|sm) or leave empty..
8985  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8986  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8987  * @cfg {Number} md colspan out of 12 for computer-sized screens
8988  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8989  * @cfg {string} value default value of the input
8990  * @cfg {Number} labelWidth set the width of label 
8991  * @cfg {Number} labellg set the width of label (1-12)
8992  * @cfg {Number} labelmd set the width of label (1-12)
8993  * @cfg {Number} labelsm set the width of label (1-12)
8994  * @cfg {Number} labelxs set the width of label (1-12)
8995  * @cfg {String} labelAlign (top|left)
8996  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8997  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8998  * @cfg {String} indicatorpos (left|right) default left
8999  * @cfg {String} capture (user|camera) use for file input only. (default empty)
9000  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
9001
9002  * @cfg {String} align (left|center|right) Default left
9003  * @cfg {Boolean} forceFeedback (true|false) Default false
9004  * 
9005  * @constructor
9006  * Create a new Input
9007  * @param {Object} config The config object
9008  */
9009
9010 Roo.bootstrap.Input = function(config){
9011     
9012     Roo.bootstrap.Input.superclass.constructor.call(this, config);
9013     
9014     this.addEvents({
9015         /**
9016          * @event focus
9017          * Fires when this field receives input focus.
9018          * @param {Roo.form.Field} this
9019          */
9020         focus : true,
9021         /**
9022          * @event blur
9023          * Fires when this field loses input focus.
9024          * @param {Roo.form.Field} this
9025          */
9026         blur : true,
9027         /**
9028          * @event specialkey
9029          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
9030          * {@link Roo.EventObject#getKey} to determine which key was pressed.
9031          * @param {Roo.form.Field} this
9032          * @param {Roo.EventObject} e The event object
9033          */
9034         specialkey : true,
9035         /**
9036          * @event change
9037          * Fires just before the field blurs if the field value has changed.
9038          * @param {Roo.form.Field} this
9039          * @param {Mixed} newValue The new value
9040          * @param {Mixed} oldValue The original value
9041          */
9042         change : true,
9043         /**
9044          * @event invalid
9045          * Fires after the field has been marked as invalid.
9046          * @param {Roo.form.Field} this
9047          * @param {String} msg The validation message
9048          */
9049         invalid : true,
9050         /**
9051          * @event valid
9052          * Fires after the field has been validated with no errors.
9053          * @param {Roo.form.Field} this
9054          */
9055         valid : true,
9056          /**
9057          * @event keyup
9058          * Fires after the key up
9059          * @param {Roo.form.Field} this
9060          * @param {Roo.EventObject}  e The event Object
9061          */
9062         keyup : true
9063     });
9064 };
9065
9066 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
9067      /**
9068      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
9069       automatic validation (defaults to "keyup").
9070      */
9071     validationEvent : "keyup",
9072      /**
9073      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
9074      */
9075     validateOnBlur : true,
9076     /**
9077      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
9078      */
9079     validationDelay : 250,
9080      /**
9081      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
9082      */
9083     focusClass : "x-form-focus",  // not needed???
9084     
9085        
9086     /**
9087      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9088      */
9089     invalidClass : "has-warning",
9090     
9091     /**
9092      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9093      */
9094     validClass : "has-success",
9095     
9096     /**
9097      * @cfg {Boolean} hasFeedback (true|false) default true
9098      */
9099     hasFeedback : true,
9100     
9101     /**
9102      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9103      */
9104     invalidFeedbackClass : "glyphicon-warning-sign",
9105     
9106     /**
9107      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9108      */
9109     validFeedbackClass : "glyphicon-ok",
9110     
9111     /**
9112      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
9113      */
9114     selectOnFocus : false,
9115     
9116      /**
9117      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
9118      */
9119     maskRe : null,
9120        /**
9121      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
9122      */
9123     vtype : null,
9124     
9125       /**
9126      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
9127      */
9128     disableKeyFilter : false,
9129     
9130        /**
9131      * @cfg {Boolean} disabled True to disable the field (defaults to false).
9132      */
9133     disabled : false,
9134      /**
9135      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
9136      */
9137     allowBlank : true,
9138     /**
9139      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9140      */
9141     blankText : "Please complete this mandatory field",
9142     
9143      /**
9144      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9145      */
9146     minLength : 0,
9147     /**
9148      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9149      */
9150     maxLength : Number.MAX_VALUE,
9151     /**
9152      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9153      */
9154     minLengthText : "The minimum length for this field is {0}",
9155     /**
9156      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9157      */
9158     maxLengthText : "The maximum length for this field is {0}",
9159   
9160     
9161     /**
9162      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9163      * If available, this function will be called only after the basic validators all return true, and will be passed the
9164      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9165      */
9166     validator : null,
9167     /**
9168      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9169      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9170      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
9171      */
9172     regex : null,
9173     /**
9174      * @cfg {String} regexText -- Depricated - use Invalid Text
9175      */
9176     regexText : "",
9177     
9178     /**
9179      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9180      */
9181     invalidText : "",
9182     
9183     
9184     
9185     autocomplete: false,
9186     
9187     
9188     fieldLabel : '',
9189     inputType : 'text',
9190     
9191     name : false,
9192     placeholder: false,
9193     before : false,
9194     after : false,
9195     size : false,
9196     hasFocus : false,
9197     preventMark: false,
9198     isFormField : true,
9199     value : '',
9200     labelWidth : 2,
9201     labelAlign : false,
9202     readOnly : false,
9203     align : false,
9204     formatedValue : false,
9205     forceFeedback : false,
9206     
9207     indicatorpos : 'left',
9208     
9209     labellg : 0,
9210     labelmd : 0,
9211     labelsm : 0,
9212     labelxs : 0,
9213     
9214     capture : '',
9215     accept : '',
9216     
9217     parentLabelAlign : function()
9218     {
9219         var parent = this;
9220         while (parent.parent()) {
9221             parent = parent.parent();
9222             if (typeof(parent.labelAlign) !='undefined') {
9223                 return parent.labelAlign;
9224             }
9225         }
9226         return 'left';
9227         
9228     },
9229     
9230     getAutoCreate : function()
9231     {
9232         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9233         
9234         var id = Roo.id();
9235         
9236         var cfg = {};
9237         
9238         if(this.inputType != 'hidden'){
9239             cfg.cls = 'form-group' //input-group
9240         }
9241         
9242         var input =  {
9243             tag: 'input',
9244             id : id,
9245             type : this.inputType,
9246             value : this.value,
9247             cls : 'form-control',
9248             placeholder : this.placeholder || '',
9249             autocomplete : this.autocomplete || 'new-password'
9250         };
9251         
9252         if(this.capture.length){
9253             input.capture = this.capture;
9254         }
9255         
9256         if(this.accept.length){
9257             input.accept = this.accept + "/*";
9258         }
9259         
9260         if(this.align){
9261             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9262         }
9263         
9264         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9265             input.maxLength = this.maxLength;
9266         }
9267         
9268         if (this.disabled) {
9269             input.disabled=true;
9270         }
9271         
9272         if (this.readOnly) {
9273             input.readonly=true;
9274         }
9275         
9276         if (this.name) {
9277             input.name = this.name;
9278         }
9279         
9280         if (this.size) {
9281             input.cls += ' input-' + this.size;
9282         }
9283         
9284         var settings=this;
9285         ['xs','sm','md','lg'].map(function(size){
9286             if (settings[size]) {
9287                 cfg.cls += ' col-' + size + '-' + settings[size];
9288             }
9289         });
9290         
9291         var inputblock = input;
9292         
9293         var feedback = {
9294             tag: 'span',
9295             cls: 'glyphicon form-control-feedback'
9296         };
9297             
9298         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9299             
9300             inputblock = {
9301                 cls : 'has-feedback',
9302                 cn :  [
9303                     input,
9304                     feedback
9305                 ] 
9306             };  
9307         }
9308         
9309         if (this.before || this.after) {
9310             
9311             inputblock = {
9312                 cls : 'input-group',
9313                 cn :  [] 
9314             };
9315             
9316             if (this.before && typeof(this.before) == 'string') {
9317                 
9318                 inputblock.cn.push({
9319                     tag :'span',
9320                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9321                     html : this.before
9322                 });
9323             }
9324             if (this.before && typeof(this.before) == 'object') {
9325                 this.before = Roo.factory(this.before);
9326                 
9327                 inputblock.cn.push({
9328                     tag :'span',
9329                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9330                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9331                 });
9332             }
9333             
9334             inputblock.cn.push(input);
9335             
9336             if (this.after && typeof(this.after) == 'string') {
9337                 inputblock.cn.push({
9338                     tag :'span',
9339                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9340                     html : this.after
9341                 });
9342             }
9343             if (this.after && typeof(this.after) == 'object') {
9344                 this.after = Roo.factory(this.after);
9345                 
9346                 inputblock.cn.push({
9347                     tag :'span',
9348                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
9349                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9350                 });
9351             }
9352             
9353             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9354                 inputblock.cls += ' has-feedback';
9355                 inputblock.cn.push(feedback);
9356             }
9357         };
9358         var indicator = {
9359             tag : 'i',
9360             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9361             tooltip : 'This field is required'
9362         };
9363         if (Roo.bootstrap.version == 4) {
9364             indicator = {
9365                 tag : 'i',
9366                 style : 'display-none'
9367             };
9368         }
9369         if (align ==='left' && this.fieldLabel.length) {
9370             
9371             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
9372             
9373             cfg.cn = [
9374                 indicator,
9375                 {
9376                     tag: 'label',
9377                     'for' :  id,
9378                     cls : 'control-label col-form-label',
9379                     html : this.fieldLabel
9380
9381                 },
9382                 {
9383                     cls : "", 
9384                     cn: [
9385                         inputblock
9386                     ]
9387                 }
9388             ];
9389             
9390             var labelCfg = cfg.cn[1];
9391             var contentCfg = cfg.cn[2];
9392             
9393             if(this.indicatorpos == 'right'){
9394                 cfg.cn = [
9395                     {
9396                         tag: 'label',
9397                         'for' :  id,
9398                         cls : 'control-label col-form-label',
9399                         cn : [
9400                             {
9401                                 tag : 'span',
9402                                 html : this.fieldLabel
9403                             },
9404                             indicator
9405                         ]
9406                     },
9407                     {
9408                         cls : "",
9409                         cn: [
9410                             inputblock
9411                         ]
9412                     }
9413
9414                 ];
9415                 
9416                 labelCfg = cfg.cn[0];
9417                 contentCfg = cfg.cn[1];
9418             
9419             }
9420             
9421             if(this.labelWidth > 12){
9422                 labelCfg.style = "width: " + this.labelWidth + 'px';
9423             }
9424             
9425             if(this.labelWidth < 13 && this.labelmd == 0){
9426                 this.labelmd = this.labelWidth;
9427             }
9428             
9429             if(this.labellg > 0){
9430                 labelCfg.cls += ' col-lg-' + this.labellg;
9431                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9432             }
9433             
9434             if(this.labelmd > 0){
9435                 labelCfg.cls += ' col-md-' + this.labelmd;
9436                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9437             }
9438             
9439             if(this.labelsm > 0){
9440                 labelCfg.cls += ' col-sm-' + this.labelsm;
9441                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9442             }
9443             
9444             if(this.labelxs > 0){
9445                 labelCfg.cls += ' col-xs-' + this.labelxs;
9446                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9447             }
9448             
9449             
9450         } else if ( this.fieldLabel.length) {
9451                 
9452             cfg.cn = [
9453                 {
9454                     tag : 'i',
9455                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9456                     tooltip : 'This field is required'
9457                 },
9458                 {
9459                     tag: 'label',
9460                    //cls : 'input-group-addon',
9461                     html : this.fieldLabel
9462
9463                 },
9464
9465                inputblock
9466
9467            ];
9468            
9469            if(this.indicatorpos == 'right'){
9470                 
9471                 cfg.cn = [
9472                     {
9473                         tag: 'label',
9474                        //cls : 'input-group-addon',
9475                         html : this.fieldLabel
9476
9477                     },
9478                     {
9479                         tag : 'i',
9480                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9481                         tooltip : 'This field is required'
9482                     },
9483
9484                    inputblock
9485
9486                ];
9487
9488             }
9489
9490         } else {
9491             
9492             cfg.cn = [
9493
9494                     inputblock
9495
9496             ];
9497                 
9498                 
9499         };
9500         
9501         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9502            cfg.cls += ' navbar-form';
9503         }
9504         
9505         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
9506             // on BS4 we do this only if not form 
9507             cfg.cls += ' navbar-form';
9508             cfg.tag = 'li';
9509         }
9510         
9511         return cfg;
9512         
9513     },
9514     /**
9515      * return the real input element.
9516      */
9517     inputEl: function ()
9518     {
9519         return this.el.select('input.form-control',true).first();
9520     },
9521     
9522     tooltipEl : function()
9523     {
9524         return this.inputEl();
9525     },
9526     
9527     indicatorEl : function()
9528     {
9529         if (Roo.bootstrap.version == 4) {
9530             return false; // not enabled in v4 yet.
9531         }
9532         
9533         var indicator = this.el.select('i.roo-required-indicator',true).first();
9534         
9535         if(!indicator){
9536             return false;
9537         }
9538         
9539         return indicator;
9540         
9541     },
9542     
9543     setDisabled : function(v)
9544     {
9545         var i  = this.inputEl().dom;
9546         if (!v) {
9547             i.removeAttribute('disabled');
9548             return;
9549             
9550         }
9551         i.setAttribute('disabled','true');
9552     },
9553     initEvents : function()
9554     {
9555           
9556         this.inputEl().on("keydown" , this.fireKey,  this);
9557         this.inputEl().on("focus", this.onFocus,  this);
9558         this.inputEl().on("blur", this.onBlur,  this);
9559         
9560         this.inputEl().relayEvent('keyup', this);
9561         
9562         this.indicator = this.indicatorEl();
9563         
9564         if(this.indicator){
9565             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9566         }
9567  
9568         // reference to original value for reset
9569         this.originalValue = this.getValue();
9570         //Roo.form.TextField.superclass.initEvents.call(this);
9571         if(this.validationEvent == 'keyup'){
9572             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9573             this.inputEl().on('keyup', this.filterValidation, this);
9574         }
9575         else if(this.validationEvent !== false){
9576             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9577         }
9578         
9579         if(this.selectOnFocus){
9580             this.on("focus", this.preFocus, this);
9581             
9582         }
9583         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9584             this.inputEl().on("keypress", this.filterKeys, this);
9585         } else {
9586             this.inputEl().relayEvent('keypress', this);
9587         }
9588        /* if(this.grow){
9589             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9590             this.el.on("click", this.autoSize,  this);
9591         }
9592         */
9593         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9594             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9595         }
9596         
9597         if (typeof(this.before) == 'object') {
9598             this.before.render(this.el.select('.roo-input-before',true).first());
9599         }
9600         if (typeof(this.after) == 'object') {
9601             this.after.render(this.el.select('.roo-input-after',true).first());
9602         }
9603         
9604         this.inputEl().on('change', this.onChange, this);
9605         
9606     },
9607     filterValidation : function(e){
9608         if(!e.isNavKeyPress()){
9609             this.validationTask.delay(this.validationDelay);
9610         }
9611     },
9612      /**
9613      * Validates the field value
9614      * @return {Boolean} True if the value is valid, else false
9615      */
9616     validate : function(){
9617         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9618         if(this.disabled || this.validateValue(this.getRawValue())){
9619             this.markValid();
9620             return true;
9621         }
9622         
9623         this.markInvalid();
9624         return false;
9625     },
9626     
9627     
9628     /**
9629      * Validates a value according to the field's validation rules and marks the field as invalid
9630      * if the validation fails
9631      * @param {Mixed} value The value to validate
9632      * @return {Boolean} True if the value is valid, else false
9633      */
9634     validateValue : function(value)
9635     {
9636         if(this.getVisibilityEl().hasClass('hidden')){
9637             return true;
9638         }
9639         
9640         if(value.length < 1)  { // if it's blank
9641             if(this.allowBlank){
9642                 return true;
9643             }
9644             return false;
9645         }
9646         
9647         if(value.length < this.minLength){
9648             return false;
9649         }
9650         if(value.length > this.maxLength){
9651             return false;
9652         }
9653         if(this.vtype){
9654             var vt = Roo.form.VTypes;
9655             if(!vt[this.vtype](value, this)){
9656                 return false;
9657             }
9658         }
9659         if(typeof this.validator == "function"){
9660             var msg = this.validator(value);
9661             if(msg !== true){
9662                 return false;
9663             }
9664             if (typeof(msg) == 'string') {
9665                 this.invalidText = msg;
9666             }
9667         }
9668         
9669         if(this.regex && !this.regex.test(value)){
9670             return false;
9671         }
9672         
9673         return true;
9674     },
9675     
9676      // private
9677     fireKey : function(e){
9678         //Roo.log('field ' + e.getKey());
9679         if(e.isNavKeyPress()){
9680             this.fireEvent("specialkey", this, e);
9681         }
9682     },
9683     focus : function (selectText){
9684         if(this.rendered){
9685             this.inputEl().focus();
9686             if(selectText === true){
9687                 this.inputEl().dom.select();
9688             }
9689         }
9690         return this;
9691     } ,
9692     
9693     onFocus : function(){
9694         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9695            // this.el.addClass(this.focusClass);
9696         }
9697         if(!this.hasFocus){
9698             this.hasFocus = true;
9699             this.startValue = this.getValue();
9700             this.fireEvent("focus", this);
9701         }
9702     },
9703     
9704     beforeBlur : Roo.emptyFn,
9705
9706     
9707     // private
9708     onBlur : function(){
9709         this.beforeBlur();
9710         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9711             //this.el.removeClass(this.focusClass);
9712         }
9713         this.hasFocus = false;
9714         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9715             this.validate();
9716         }
9717         var v = this.getValue();
9718         if(String(v) !== String(this.startValue)){
9719             this.fireEvent('change', this, v, this.startValue);
9720         }
9721         this.fireEvent("blur", this);
9722     },
9723     
9724     onChange : function(e)
9725     {
9726         var v = this.getValue();
9727         if(String(v) !== String(this.startValue)){
9728             this.fireEvent('change', this, v, this.startValue);
9729         }
9730         
9731     },
9732     
9733     /**
9734      * Resets the current field value to the originally loaded value and clears any validation messages
9735      */
9736     reset : function(){
9737         this.setValue(this.originalValue);
9738         this.validate();
9739     },
9740      /**
9741      * Returns the name of the field
9742      * @return {Mixed} name The name field
9743      */
9744     getName: function(){
9745         return this.name;
9746     },
9747      /**
9748      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9749      * @return {Mixed} value The field value
9750      */
9751     getValue : function(){
9752         
9753         var v = this.inputEl().getValue();
9754         
9755         return v;
9756     },
9757     /**
9758      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9759      * @return {Mixed} value The field value
9760      */
9761     getRawValue : function(){
9762         var v = this.inputEl().getValue();
9763         
9764         return v;
9765     },
9766     
9767     /**
9768      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9769      * @param {Mixed} value The value to set
9770      */
9771     setRawValue : function(v){
9772         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9773     },
9774     
9775     selectText : function(start, end){
9776         var v = this.getRawValue();
9777         if(v.length > 0){
9778             start = start === undefined ? 0 : start;
9779             end = end === undefined ? v.length : end;
9780             var d = this.inputEl().dom;
9781             if(d.setSelectionRange){
9782                 d.setSelectionRange(start, end);
9783             }else if(d.createTextRange){
9784                 var range = d.createTextRange();
9785                 range.moveStart("character", start);
9786                 range.moveEnd("character", v.length-end);
9787                 range.select();
9788             }
9789         }
9790     },
9791     
9792     /**
9793      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9794      * @param {Mixed} value The value to set
9795      */
9796     setValue : function(v){
9797         this.value = v;
9798         if(this.rendered){
9799             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9800             this.validate();
9801         }
9802     },
9803     
9804     /*
9805     processValue : function(value){
9806         if(this.stripCharsRe){
9807             var newValue = value.replace(this.stripCharsRe, '');
9808             if(newValue !== value){
9809                 this.setRawValue(newValue);
9810                 return newValue;
9811             }
9812         }
9813         return value;
9814     },
9815   */
9816     preFocus : function(){
9817         
9818         if(this.selectOnFocus){
9819             this.inputEl().dom.select();
9820         }
9821     },
9822     filterKeys : function(e){
9823         var k = e.getKey();
9824         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9825             return;
9826         }
9827         var c = e.getCharCode(), cc = String.fromCharCode(c);
9828         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9829             return;
9830         }
9831         if(!this.maskRe.test(cc)){
9832             e.stopEvent();
9833         }
9834     },
9835      /**
9836      * Clear any invalid styles/messages for this field
9837      */
9838     clearInvalid : function(){
9839         
9840         if(!this.el || this.preventMark){ // not rendered
9841             return;
9842         }
9843         
9844         
9845         this.el.removeClass([this.invalidClass, 'is-invalid']);
9846         
9847         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9848             
9849             var feedback = this.el.select('.form-control-feedback', true).first();
9850             
9851             if(feedback){
9852                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9853             }
9854             
9855         }
9856         
9857         if(this.indicator){
9858             this.indicator.removeClass('visible');
9859             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9860         }
9861         
9862         this.fireEvent('valid', this);
9863     },
9864     
9865      /**
9866      * Mark this field as valid
9867      */
9868     markValid : function()
9869     {
9870         if(!this.el  || this.preventMark){ // not rendered...
9871             return;
9872         }
9873         
9874         this.el.removeClass([this.invalidClass, this.validClass]);
9875         this.inputEl().removeClass(['is-valid', 'is-invalid']);
9876
9877         var feedback = this.el.select('.form-control-feedback', true).first();
9878             
9879         if(feedback){
9880             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9881         }
9882         
9883         if(this.indicator){
9884             this.indicator.removeClass('visible');
9885             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9886         }
9887         
9888         if(this.disabled){
9889             return;
9890         }
9891         
9892         if(this.allowBlank && !this.getRawValue().length){
9893             return;
9894         }
9895         if (Roo.bootstrap.version == 3) {
9896             this.el.addClass(this.validClass);
9897         } else {
9898             this.inputEl().addClass('is-valid');
9899         }
9900
9901         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9902             
9903             var feedback = this.el.select('.form-control-feedback', true).first();
9904             
9905             if(feedback){
9906                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9907                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9908             }
9909             
9910         }
9911         
9912         this.fireEvent('valid', this);
9913     },
9914     
9915      /**
9916      * Mark this field as invalid
9917      * @param {String} msg The validation message
9918      */
9919     markInvalid : function(msg)
9920     {
9921         if(!this.el  || this.preventMark){ // not rendered
9922             return;
9923         }
9924         
9925         this.el.removeClass([this.invalidClass, this.validClass]);
9926         this.inputEl().removeClass(['is-valid', 'is-invalid']);
9927         
9928         var feedback = this.el.select('.form-control-feedback', true).first();
9929             
9930         if(feedback){
9931             this.el.select('.form-control-feedback', true).first().removeClass(
9932                     [this.invalidFeedbackClass, this.validFeedbackClass]);
9933         }
9934
9935         if(this.disabled){
9936             return;
9937         }
9938         
9939         if(this.allowBlank && !this.getRawValue().length){
9940             return;
9941         }
9942         
9943         if(this.indicator){
9944             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9945             this.indicator.addClass('visible');
9946         }
9947         if (Roo.bootstrap.version == 3) {
9948             this.el.addClass(this.invalidClass);
9949         } else {
9950             this.inputEl().addClass('is-invalid');
9951         }
9952         
9953         
9954         
9955         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9956             
9957             var feedback = this.el.select('.form-control-feedback', true).first();
9958             
9959             if(feedback){
9960                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9961                 
9962                 if(this.getValue().length || this.forceFeedback){
9963                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9964                 }
9965                 
9966             }
9967             
9968         }
9969         
9970         this.fireEvent('invalid', this, msg);
9971     },
9972     // private
9973     SafariOnKeyDown : function(event)
9974     {
9975         // this is a workaround for a password hang bug on chrome/ webkit.
9976         if (this.inputEl().dom.type != 'password') {
9977             return;
9978         }
9979         
9980         var isSelectAll = false;
9981         
9982         if(this.inputEl().dom.selectionEnd > 0){
9983             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9984         }
9985         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9986             event.preventDefault();
9987             this.setValue('');
9988             return;
9989         }
9990         
9991         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9992             
9993             event.preventDefault();
9994             // this is very hacky as keydown always get's upper case.
9995             //
9996             var cc = String.fromCharCode(event.getCharCode());
9997             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9998             
9999         }
10000     },
10001     adjustWidth : function(tag, w){
10002         tag = tag.toLowerCase();
10003         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
10004             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
10005                 if(tag == 'input'){
10006                     return w + 2;
10007                 }
10008                 if(tag == 'textarea'){
10009                     return w-2;
10010                 }
10011             }else if(Roo.isOpera){
10012                 if(tag == 'input'){
10013                     return w + 2;
10014                 }
10015                 if(tag == 'textarea'){
10016                     return w-2;
10017                 }
10018             }
10019         }
10020         return w;
10021     },
10022     
10023     setFieldLabel : function(v)
10024     {
10025         if(!this.rendered){
10026             return;
10027         }
10028         
10029         if(this.indicatorEl()){
10030             var ar = this.el.select('label > span',true);
10031             
10032             if (ar.elements.length) {
10033                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10034                 this.fieldLabel = v;
10035                 return;
10036             }
10037             
10038             var br = this.el.select('label',true);
10039             
10040             if(br.elements.length) {
10041                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10042                 this.fieldLabel = v;
10043                 return;
10044             }
10045             
10046             Roo.log('Cannot Found any of label > span || label in input');
10047             return;
10048         }
10049         
10050         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10051         this.fieldLabel = v;
10052         
10053         
10054     }
10055 });
10056
10057  
10058 /*
10059  * - LGPL
10060  *
10061  * Input
10062  * 
10063  */
10064
10065 /**
10066  * @class Roo.bootstrap.TextArea
10067  * @extends Roo.bootstrap.Input
10068  * Bootstrap TextArea class
10069  * @cfg {Number} cols Specifies the visible width of a text area
10070  * @cfg {Number} rows Specifies the visible number of lines in a text area
10071  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
10072  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
10073  * @cfg {string} html text
10074  * 
10075  * @constructor
10076  * Create a new TextArea
10077  * @param {Object} config The config object
10078  */
10079
10080 Roo.bootstrap.TextArea = function(config){
10081     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
10082    
10083 };
10084
10085 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
10086      
10087     cols : false,
10088     rows : 5,
10089     readOnly : false,
10090     warp : 'soft',
10091     resize : false,
10092     value: false,
10093     html: false,
10094     
10095     getAutoCreate : function(){
10096         
10097         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10098         
10099         var id = Roo.id();
10100         
10101         var cfg = {};
10102         
10103         if(this.inputType != 'hidden'){
10104             cfg.cls = 'form-group' //input-group
10105         }
10106         
10107         var input =  {
10108             tag: 'textarea',
10109             id : id,
10110             warp : this.warp,
10111             rows : this.rows,
10112             value : this.value || '',
10113             html: this.html || '',
10114             cls : 'form-control',
10115             placeholder : this.placeholder || '' 
10116             
10117         };
10118         
10119         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10120             input.maxLength = this.maxLength;
10121         }
10122         
10123         if(this.resize){
10124             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
10125         }
10126         
10127         if(this.cols){
10128             input.cols = this.cols;
10129         }
10130         
10131         if (this.readOnly) {
10132             input.readonly = true;
10133         }
10134         
10135         if (this.name) {
10136             input.name = this.name;
10137         }
10138         
10139         if (this.size) {
10140             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
10141         }
10142         
10143         var settings=this;
10144         ['xs','sm','md','lg'].map(function(size){
10145             if (settings[size]) {
10146                 cfg.cls += ' col-' + size + '-' + settings[size];
10147             }
10148         });
10149         
10150         var inputblock = input;
10151         
10152         if(this.hasFeedback && !this.allowBlank){
10153             
10154             var feedback = {
10155                 tag: 'span',
10156                 cls: 'glyphicon form-control-feedback'
10157             };
10158
10159             inputblock = {
10160                 cls : 'has-feedback',
10161                 cn :  [
10162                     input,
10163                     feedback
10164                 ] 
10165             };  
10166         }
10167         
10168         
10169         if (this.before || this.after) {
10170             
10171             inputblock = {
10172                 cls : 'input-group',
10173                 cn :  [] 
10174             };
10175             if (this.before) {
10176                 inputblock.cn.push({
10177                     tag :'span',
10178                     cls : 'input-group-addon',
10179                     html : this.before
10180                 });
10181             }
10182             
10183             inputblock.cn.push(input);
10184             
10185             if(this.hasFeedback && !this.allowBlank){
10186                 inputblock.cls += ' has-feedback';
10187                 inputblock.cn.push(feedback);
10188             }
10189             
10190             if (this.after) {
10191                 inputblock.cn.push({
10192                     tag :'span',
10193                     cls : 'input-group-addon',
10194                     html : this.after
10195                 });
10196             }
10197             
10198         }
10199         
10200         if (align ==='left' && this.fieldLabel.length) {
10201             cfg.cn = [
10202                 {
10203                     tag: 'label',
10204                     'for' :  id,
10205                     cls : 'control-label',
10206                     html : this.fieldLabel
10207                 },
10208                 {
10209                     cls : "",
10210                     cn: [
10211                         inputblock
10212                     ]
10213                 }
10214
10215             ];
10216             
10217             if(this.labelWidth > 12){
10218                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10219             }
10220
10221             if(this.labelWidth < 13 && this.labelmd == 0){
10222                 this.labelmd = this.labelWidth;
10223             }
10224
10225             if(this.labellg > 0){
10226                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10227                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10228             }
10229
10230             if(this.labelmd > 0){
10231                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10232                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10233             }
10234
10235             if(this.labelsm > 0){
10236                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10237                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10238             }
10239
10240             if(this.labelxs > 0){
10241                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10242                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10243             }
10244             
10245         } else if ( this.fieldLabel.length) {
10246             cfg.cn = [
10247
10248                {
10249                    tag: 'label',
10250                    //cls : 'input-group-addon',
10251                    html : this.fieldLabel
10252
10253                },
10254
10255                inputblock
10256
10257            ];
10258
10259         } else {
10260
10261             cfg.cn = [
10262
10263                 inputblock
10264
10265             ];
10266                 
10267         }
10268         
10269         if (this.disabled) {
10270             input.disabled=true;
10271         }
10272         
10273         return cfg;
10274         
10275     },
10276     /**
10277      * return the real textarea element.
10278      */
10279     inputEl: function ()
10280     {
10281         return this.el.select('textarea.form-control',true).first();
10282     },
10283     
10284     /**
10285      * Clear any invalid styles/messages for this field
10286      */
10287     clearInvalid : function()
10288     {
10289         
10290         if(!this.el || this.preventMark){ // not rendered
10291             return;
10292         }
10293         
10294         var label = this.el.select('label', true).first();
10295         var icon = this.el.select('i.fa-star', true).first();
10296         
10297         if(label && icon){
10298             icon.remove();
10299         }
10300         this.el.removeClass( this.validClass);
10301         this.inputEl().removeClass('is-invalid');
10302          
10303         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10304             
10305             var feedback = this.el.select('.form-control-feedback', true).first();
10306             
10307             if(feedback){
10308                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10309             }
10310             
10311         }
10312         
10313         this.fireEvent('valid', this);
10314     },
10315     
10316      /**
10317      * Mark this field as valid
10318      */
10319     markValid : function()
10320     {
10321         if(!this.el  || this.preventMark){ // not rendered
10322             return;
10323         }
10324         
10325         this.el.removeClass([this.invalidClass, this.validClass]);
10326         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10327         
10328         var feedback = this.el.select('.form-control-feedback', true).first();
10329             
10330         if(feedback){
10331             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10332         }
10333
10334         if(this.disabled || this.allowBlank){
10335             return;
10336         }
10337         
10338         var label = this.el.select('label', true).first();
10339         var icon = this.el.select('i.fa-star', true).first();
10340         
10341         if(label && icon){
10342             icon.remove();
10343         }
10344         if (Roo.bootstrap.version == 3) {
10345             this.el.addClass(this.validClass);
10346         } else {
10347             this.inputEl().addClass('is-valid');
10348         }
10349         
10350         
10351         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10352             
10353             var feedback = this.el.select('.form-control-feedback', true).first();
10354             
10355             if(feedback){
10356                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10357                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10358             }
10359             
10360         }
10361         
10362         this.fireEvent('valid', this);
10363     },
10364     
10365      /**
10366      * Mark this field as invalid
10367      * @param {String} msg The validation message
10368      */
10369     markInvalid : function(msg)
10370     {
10371         if(!this.el  || this.preventMark){ // not rendered
10372             return;
10373         }
10374         
10375         this.el.removeClass([this.invalidClass, this.validClass]);
10376         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10377         
10378         var feedback = this.el.select('.form-control-feedback', true).first();
10379             
10380         if(feedback){
10381             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10382         }
10383
10384         if(this.disabled || this.allowBlank){
10385             return;
10386         }
10387         
10388         var label = this.el.select('label', true).first();
10389         var icon = this.el.select('i.fa-star', true).first();
10390         
10391         if(!this.getValue().length && label && !icon){
10392             this.el.createChild({
10393                 tag : 'i',
10394                 cls : 'text-danger fa fa-lg fa-star',
10395                 tooltip : 'This field is required',
10396                 style : 'margin-right:5px;'
10397             }, label, true);
10398         }
10399         
10400         if (Roo.bootstrap.version == 3) {
10401             this.el.addClass(this.invalidClass);
10402         } else {
10403             this.inputEl().addClass('is-invalid');
10404         }
10405         
10406         // fixme ... this may be depricated need to test..
10407         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10408             
10409             var feedback = this.el.select('.form-control-feedback', true).first();
10410             
10411             if(feedback){
10412                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10413                 
10414                 if(this.getValue().length || this.forceFeedback){
10415                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10416                 }
10417                 
10418             }
10419             
10420         }
10421         
10422         this.fireEvent('invalid', this, msg);
10423     }
10424 });
10425
10426  
10427 /*
10428  * - LGPL
10429  *
10430  * trigger field - base class for combo..
10431  * 
10432  */
10433  
10434 /**
10435  * @class Roo.bootstrap.TriggerField
10436  * @extends Roo.bootstrap.Input
10437  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10438  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10439  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10440  * for which you can provide a custom implementation.  For example:
10441  * <pre><code>
10442 var trigger = new Roo.bootstrap.TriggerField();
10443 trigger.onTriggerClick = myTriggerFn;
10444 trigger.applyTo('my-field');
10445 </code></pre>
10446  *
10447  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10448  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10449  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10450  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10451  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10452
10453  * @constructor
10454  * Create a new TriggerField.
10455  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10456  * to the base TextField)
10457  */
10458 Roo.bootstrap.TriggerField = function(config){
10459     this.mimicing = false;
10460     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10461 };
10462
10463 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10464     /**
10465      * @cfg {String} triggerClass A CSS class to apply to the trigger
10466      */
10467      /**
10468      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10469      */
10470     hideTrigger:false,
10471
10472     /**
10473      * @cfg {Boolean} removable (true|false) special filter default false
10474      */
10475     removable : false,
10476     
10477     /** @cfg {Boolean} grow @hide */
10478     /** @cfg {Number} growMin @hide */
10479     /** @cfg {Number} growMax @hide */
10480
10481     /**
10482      * @hide 
10483      * @method
10484      */
10485     autoSize: Roo.emptyFn,
10486     // private
10487     monitorTab : true,
10488     // private
10489     deferHeight : true,
10490
10491     
10492     actionMode : 'wrap',
10493     
10494     caret : false,
10495     
10496     
10497     getAutoCreate : function(){
10498        
10499         var align = this.labelAlign || this.parentLabelAlign();
10500         
10501         var id = Roo.id();
10502         
10503         var cfg = {
10504             cls: 'form-group' //input-group
10505         };
10506         
10507         
10508         var input =  {
10509             tag: 'input',
10510             id : id,
10511             type : this.inputType,
10512             cls : 'form-control',
10513             autocomplete: 'new-password',
10514             placeholder : this.placeholder || '' 
10515             
10516         };
10517         if (this.name) {
10518             input.name = this.name;
10519         }
10520         if (this.size) {
10521             input.cls += ' input-' + this.size;
10522         }
10523         
10524         if (this.disabled) {
10525             input.disabled=true;
10526         }
10527         
10528         var inputblock = input;
10529         
10530         if(this.hasFeedback && !this.allowBlank){
10531             
10532             var feedback = {
10533                 tag: 'span',
10534                 cls: 'glyphicon form-control-feedback'
10535             };
10536             
10537             if(this.removable && !this.editable && !this.tickable){
10538                 inputblock = {
10539                     cls : 'has-feedback',
10540                     cn :  [
10541                         inputblock,
10542                         {
10543                             tag: 'button',
10544                             html : 'x',
10545                             cls : 'roo-combo-removable-btn close'
10546                         },
10547                         feedback
10548                     ] 
10549                 };
10550             } else {
10551                 inputblock = {
10552                     cls : 'has-feedback',
10553                     cn :  [
10554                         inputblock,
10555                         feedback
10556                     ] 
10557                 };
10558             }
10559
10560         } else {
10561             if(this.removable && !this.editable && !this.tickable){
10562                 inputblock = {
10563                     cls : 'roo-removable',
10564                     cn :  [
10565                         inputblock,
10566                         {
10567                             tag: 'button',
10568                             html : 'x',
10569                             cls : 'roo-combo-removable-btn close'
10570                         }
10571                     ] 
10572                 };
10573             }
10574         }
10575         
10576         if (this.before || this.after) {
10577             
10578             inputblock = {
10579                 cls : 'input-group',
10580                 cn :  [] 
10581             };
10582             if (this.before) {
10583                 inputblock.cn.push({
10584                     tag :'span',
10585                     cls : 'input-group-addon input-group-prepend input-group-text',
10586                     html : this.before
10587                 });
10588             }
10589             
10590             inputblock.cn.push(input);
10591             
10592             if(this.hasFeedback && !this.allowBlank){
10593                 inputblock.cls += ' has-feedback';
10594                 inputblock.cn.push(feedback);
10595             }
10596             
10597             if (this.after) {
10598                 inputblock.cn.push({
10599                     tag :'span',
10600                     cls : 'input-group-addon input-group-append input-group-text',
10601                     html : this.after
10602                 });
10603             }
10604             
10605         };
10606         
10607       
10608         
10609         var ibwrap = inputblock;
10610         
10611         if(this.multiple){
10612             ibwrap = {
10613                 tag: 'ul',
10614                 cls: 'roo-select2-choices',
10615                 cn:[
10616                     {
10617                         tag: 'li',
10618                         cls: 'roo-select2-search-field',
10619                         cn: [
10620
10621                             inputblock
10622                         ]
10623                     }
10624                 ]
10625             };
10626                 
10627         }
10628         
10629         var combobox = {
10630             cls: 'roo-select2-container input-group',
10631             cn: [
10632                  {
10633                     tag: 'input',
10634                     type : 'hidden',
10635                     cls: 'form-hidden-field'
10636                 },
10637                 ibwrap
10638             ]
10639         };
10640         
10641         if(!this.multiple && this.showToggleBtn){
10642             
10643             var caret = {
10644                         tag: 'span',
10645                         cls: 'caret'
10646              };
10647             if (this.caret != false) {
10648                 caret = {
10649                      tag: 'i',
10650                      cls: 'fa fa-' + this.caret
10651                 };
10652                 
10653             }
10654             
10655             combobox.cn.push({
10656                 tag :'span',
10657                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10658                 cn : [
10659                     caret,
10660                     {
10661                         tag: 'span',
10662                         cls: 'combobox-clear',
10663                         cn  : [
10664                             {
10665                                 tag : 'i',
10666                                 cls: 'icon-remove'
10667                             }
10668                         ]
10669                     }
10670                 ]
10671
10672             })
10673         }
10674         
10675         if(this.multiple){
10676             combobox.cls += ' roo-select2-container-multi';
10677         }
10678          var indicator = {
10679             tag : 'i',
10680             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10681             tooltip : 'This field is required'
10682         };
10683         if (Roo.bootstrap.version == 4) {
10684             indicator = {
10685                 tag : 'i',
10686                 style : 'display:none'
10687             };
10688         }
10689         
10690         
10691         if (align ==='left' && this.fieldLabel.length) {
10692             
10693             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10694
10695             cfg.cn = [
10696                 indicator,
10697                 {
10698                     tag: 'label',
10699                     'for' :  id,
10700                     cls : 'control-label',
10701                     html : this.fieldLabel
10702
10703                 },
10704                 {
10705                     cls : "", 
10706                     cn: [
10707                         combobox
10708                     ]
10709                 }
10710
10711             ];
10712             
10713             var labelCfg = cfg.cn[1];
10714             var contentCfg = cfg.cn[2];
10715             
10716             if(this.indicatorpos == 'right'){
10717                 cfg.cn = [
10718                     {
10719                         tag: 'label',
10720                         'for' :  id,
10721                         cls : 'control-label',
10722                         cn : [
10723                             {
10724                                 tag : 'span',
10725                                 html : this.fieldLabel
10726                             },
10727                             indicator
10728                         ]
10729                     },
10730                     {
10731                         cls : "", 
10732                         cn: [
10733                             combobox
10734                         ]
10735                     }
10736
10737                 ];
10738                 
10739                 labelCfg = cfg.cn[0];
10740                 contentCfg = cfg.cn[1];
10741             }
10742             
10743             if(this.labelWidth > 12){
10744                 labelCfg.style = "width: " + this.labelWidth + 'px';
10745             }
10746             
10747             if(this.labelWidth < 13 && this.labelmd == 0){
10748                 this.labelmd = this.labelWidth;
10749             }
10750             
10751             if(this.labellg > 0){
10752                 labelCfg.cls += ' col-lg-' + this.labellg;
10753                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10754             }
10755             
10756             if(this.labelmd > 0){
10757                 labelCfg.cls += ' col-md-' + this.labelmd;
10758                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10759             }
10760             
10761             if(this.labelsm > 0){
10762                 labelCfg.cls += ' col-sm-' + this.labelsm;
10763                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10764             }
10765             
10766             if(this.labelxs > 0){
10767                 labelCfg.cls += ' col-xs-' + this.labelxs;
10768                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10769             }
10770             
10771         } else if ( this.fieldLabel.length) {
10772 //                Roo.log(" label");
10773             cfg.cn = [
10774                 indicator,
10775                {
10776                    tag: 'label',
10777                    //cls : 'input-group-addon',
10778                    html : this.fieldLabel
10779
10780                },
10781
10782                combobox
10783
10784             ];
10785             
10786             if(this.indicatorpos == 'right'){
10787                 
10788                 cfg.cn = [
10789                     {
10790                        tag: 'label',
10791                        cn : [
10792                            {
10793                                tag : 'span',
10794                                html : this.fieldLabel
10795                            },
10796                            indicator
10797                        ]
10798
10799                     },
10800                     combobox
10801
10802                 ];
10803
10804             }
10805
10806         } else {
10807             
10808 //                Roo.log(" no label && no align");
10809                 cfg = combobox
10810                      
10811                 
10812         }
10813         
10814         var settings=this;
10815         ['xs','sm','md','lg'].map(function(size){
10816             if (settings[size]) {
10817                 cfg.cls += ' col-' + size + '-' + settings[size];
10818             }
10819         });
10820         
10821         return cfg;
10822         
10823     },
10824     
10825     
10826     
10827     // private
10828     onResize : function(w, h){
10829 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10830 //        if(typeof w == 'number'){
10831 //            var x = w - this.trigger.getWidth();
10832 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10833 //            this.trigger.setStyle('left', x+'px');
10834 //        }
10835     },
10836
10837     // private
10838     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10839
10840     // private
10841     getResizeEl : function(){
10842         return this.inputEl();
10843     },
10844
10845     // private
10846     getPositionEl : function(){
10847         return this.inputEl();
10848     },
10849
10850     // private
10851     alignErrorIcon : function(){
10852         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10853     },
10854
10855     // private
10856     initEvents : function(){
10857         
10858         this.createList();
10859         
10860         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10861         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10862         if(!this.multiple && this.showToggleBtn){
10863             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10864             if(this.hideTrigger){
10865                 this.trigger.setDisplayed(false);
10866             }
10867             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10868         }
10869         
10870         if(this.multiple){
10871             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10872         }
10873         
10874         if(this.removable && !this.editable && !this.tickable){
10875             var close = this.closeTriggerEl();
10876             
10877             if(close){
10878                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10879                 close.on('click', this.removeBtnClick, this, close);
10880             }
10881         }
10882         
10883         //this.trigger.addClassOnOver('x-form-trigger-over');
10884         //this.trigger.addClassOnClick('x-form-trigger-click');
10885         
10886         //if(!this.width){
10887         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10888         //}
10889     },
10890     
10891     closeTriggerEl : function()
10892     {
10893         var close = this.el.select('.roo-combo-removable-btn', true).first();
10894         return close ? close : false;
10895     },
10896     
10897     removeBtnClick : function(e, h, el)
10898     {
10899         e.preventDefault();
10900         
10901         if(this.fireEvent("remove", this) !== false){
10902             this.reset();
10903             this.fireEvent("afterremove", this)
10904         }
10905     },
10906     
10907     createList : function()
10908     {
10909         this.list = Roo.get(document.body).createChild({
10910             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10911             cls: 'typeahead typeahead-long dropdown-menu',
10912             style: 'display:none'
10913         });
10914         
10915         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10916         
10917     },
10918
10919     // private
10920     initTrigger : function(){
10921        
10922     },
10923
10924     // private
10925     onDestroy : function(){
10926         if(this.trigger){
10927             this.trigger.removeAllListeners();
10928           //  this.trigger.remove();
10929         }
10930         //if(this.wrap){
10931         //    this.wrap.remove();
10932         //}
10933         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10934     },
10935
10936     // private
10937     onFocus : function(){
10938         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10939         /*
10940         if(!this.mimicing){
10941             this.wrap.addClass('x-trigger-wrap-focus');
10942             this.mimicing = true;
10943             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10944             if(this.monitorTab){
10945                 this.el.on("keydown", this.checkTab, this);
10946             }
10947         }
10948         */
10949     },
10950
10951     // private
10952     checkTab : function(e){
10953         if(e.getKey() == e.TAB){
10954             this.triggerBlur();
10955         }
10956     },
10957
10958     // private
10959     onBlur : function(){
10960         // do nothing
10961     },
10962
10963     // private
10964     mimicBlur : function(e, t){
10965         /*
10966         if(!this.wrap.contains(t) && this.validateBlur()){
10967             this.triggerBlur();
10968         }
10969         */
10970     },
10971
10972     // private
10973     triggerBlur : function(){
10974         this.mimicing = false;
10975         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10976         if(this.monitorTab){
10977             this.el.un("keydown", this.checkTab, this);
10978         }
10979         //this.wrap.removeClass('x-trigger-wrap-focus');
10980         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10981     },
10982
10983     // private
10984     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10985     validateBlur : function(e, t){
10986         return true;
10987     },
10988
10989     // private
10990     onDisable : function(){
10991         this.inputEl().dom.disabled = true;
10992         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10993         //if(this.wrap){
10994         //    this.wrap.addClass('x-item-disabled');
10995         //}
10996     },
10997
10998     // private
10999     onEnable : function(){
11000         this.inputEl().dom.disabled = false;
11001         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
11002         //if(this.wrap){
11003         //    this.el.removeClass('x-item-disabled');
11004         //}
11005     },
11006
11007     // private
11008     onShow : function(){
11009         var ae = this.getActionEl();
11010         
11011         if(ae){
11012             ae.dom.style.display = '';
11013             ae.dom.style.visibility = 'visible';
11014         }
11015     },
11016
11017     // private
11018     
11019     onHide : function(){
11020         var ae = this.getActionEl();
11021         ae.dom.style.display = 'none';
11022     },
11023
11024     /**
11025      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
11026      * by an implementing function.
11027      * @method
11028      * @param {EventObject} e
11029      */
11030     onTriggerClick : Roo.emptyFn
11031 });
11032  /*
11033  * Based on:
11034  * Ext JS Library 1.1.1
11035  * Copyright(c) 2006-2007, Ext JS, LLC.
11036  *
11037  * Originally Released Under LGPL - original licence link has changed is not relivant.
11038  *
11039  * Fork - LGPL
11040  * <script type="text/javascript">
11041  */
11042
11043
11044 /**
11045  * @class Roo.data.SortTypes
11046  * @singleton
11047  * Defines the default sorting (casting?) comparison functions used when sorting data.
11048  */
11049 Roo.data.SortTypes = {
11050     /**
11051      * Default sort that does nothing
11052      * @param {Mixed} s The value being converted
11053      * @return {Mixed} The comparison value
11054      */
11055     none : function(s){
11056         return s;
11057     },
11058     
11059     /**
11060      * The regular expression used to strip tags
11061      * @type {RegExp}
11062      * @property
11063      */
11064     stripTagsRE : /<\/?[^>]+>/gi,
11065     
11066     /**
11067      * Strips all HTML tags to sort on text only
11068      * @param {Mixed} s The value being converted
11069      * @return {String} The comparison value
11070      */
11071     asText : function(s){
11072         return String(s).replace(this.stripTagsRE, "");
11073     },
11074     
11075     /**
11076      * Strips all HTML tags to sort on text only - Case insensitive
11077      * @param {Mixed} s The value being converted
11078      * @return {String} The comparison value
11079      */
11080     asUCText : function(s){
11081         return String(s).toUpperCase().replace(this.stripTagsRE, "");
11082     },
11083     
11084     /**
11085      * Case insensitive string
11086      * @param {Mixed} s The value being converted
11087      * @return {String} The comparison value
11088      */
11089     asUCString : function(s) {
11090         return String(s).toUpperCase();
11091     },
11092     
11093     /**
11094      * Date sorting
11095      * @param {Mixed} s The value being converted
11096      * @return {Number} The comparison value
11097      */
11098     asDate : function(s) {
11099         if(!s){
11100             return 0;
11101         }
11102         if(s instanceof Date){
11103             return s.getTime();
11104         }
11105         return Date.parse(String(s));
11106     },
11107     
11108     /**
11109      * Float sorting
11110      * @param {Mixed} s The value being converted
11111      * @return {Float} The comparison value
11112      */
11113     asFloat : function(s) {
11114         var val = parseFloat(String(s).replace(/,/g, ""));
11115         if(isNaN(val)) {
11116             val = 0;
11117         }
11118         return val;
11119     },
11120     
11121     /**
11122      * Integer sorting
11123      * @param {Mixed} s The value being converted
11124      * @return {Number} The comparison value
11125      */
11126     asInt : function(s) {
11127         var val = parseInt(String(s).replace(/,/g, ""));
11128         if(isNaN(val)) {
11129             val = 0;
11130         }
11131         return val;
11132     }
11133 };/*
11134  * Based on:
11135  * Ext JS Library 1.1.1
11136  * Copyright(c) 2006-2007, Ext JS, LLC.
11137  *
11138  * Originally Released Under LGPL - original licence link has changed is not relivant.
11139  *
11140  * Fork - LGPL
11141  * <script type="text/javascript">
11142  */
11143
11144 /**
11145 * @class Roo.data.Record
11146  * Instances of this class encapsulate both record <em>definition</em> information, and record
11147  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
11148  * to access Records cached in an {@link Roo.data.Store} object.<br>
11149  * <p>
11150  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
11151  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
11152  * objects.<br>
11153  * <p>
11154  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
11155  * @constructor
11156  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
11157  * {@link #create}. The parameters are the same.
11158  * @param {Array} data An associative Array of data values keyed by the field name.
11159  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
11160  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
11161  * not specified an integer id is generated.
11162  */
11163 Roo.data.Record = function(data, id){
11164     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11165     this.data = data;
11166 };
11167
11168 /**
11169  * Generate a constructor for a specific record layout.
11170  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11171  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11172  * Each field definition object may contain the following properties: <ul>
11173  * <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,
11174  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11175  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11176  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11177  * is being used, then this is a string containing the javascript expression to reference the data relative to 
11178  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11179  * to the data item relative to the record element. If the mapping expression is the same as the field name,
11180  * this may be omitted.</p></li>
11181  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11182  * <ul><li>auto (Default, implies no conversion)</li>
11183  * <li>string</li>
11184  * <li>int</li>
11185  * <li>float</li>
11186  * <li>boolean</li>
11187  * <li>date</li></ul></p></li>
11188  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11189  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11190  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11191  * by the Reader into an object that will be stored in the Record. It is passed the
11192  * following parameters:<ul>
11193  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11194  * </ul></p></li>
11195  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11196  * </ul>
11197  * <br>usage:<br><pre><code>
11198 var TopicRecord = Roo.data.Record.create(
11199     {name: 'title', mapping: 'topic_title'},
11200     {name: 'author', mapping: 'username'},
11201     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11202     {name: 'lastPost', mapping: 'post_time', type: 'date'},
11203     {name: 'lastPoster', mapping: 'user2'},
11204     {name: 'excerpt', mapping: 'post_text'}
11205 );
11206
11207 var myNewRecord = new TopicRecord({
11208     title: 'Do my job please',
11209     author: 'noobie',
11210     totalPosts: 1,
11211     lastPost: new Date(),
11212     lastPoster: 'Animal',
11213     excerpt: 'No way dude!'
11214 });
11215 myStore.add(myNewRecord);
11216 </code></pre>
11217  * @method create
11218  * @static
11219  */
11220 Roo.data.Record.create = function(o){
11221     var f = function(){
11222         f.superclass.constructor.apply(this, arguments);
11223     };
11224     Roo.extend(f, Roo.data.Record);
11225     var p = f.prototype;
11226     p.fields = new Roo.util.MixedCollection(false, function(field){
11227         return field.name;
11228     });
11229     for(var i = 0, len = o.length; i < len; i++){
11230         p.fields.add(new Roo.data.Field(o[i]));
11231     }
11232     f.getField = function(name){
11233         return p.fields.get(name);  
11234     };
11235     return f;
11236 };
11237
11238 Roo.data.Record.AUTO_ID = 1000;
11239 Roo.data.Record.EDIT = 'edit';
11240 Roo.data.Record.REJECT = 'reject';
11241 Roo.data.Record.COMMIT = 'commit';
11242
11243 Roo.data.Record.prototype = {
11244     /**
11245      * Readonly flag - true if this record has been modified.
11246      * @type Boolean
11247      */
11248     dirty : false,
11249     editing : false,
11250     error: null,
11251     modified: null,
11252
11253     // private
11254     join : function(store){
11255         this.store = store;
11256     },
11257
11258     /**
11259      * Set the named field to the specified value.
11260      * @param {String} name The name of the field to set.
11261      * @param {Object} value The value to set the field to.
11262      */
11263     set : function(name, value){
11264         if(this.data[name] == value){
11265             return;
11266         }
11267         this.dirty = true;
11268         if(!this.modified){
11269             this.modified = {};
11270         }
11271         if(typeof this.modified[name] == 'undefined'){
11272             this.modified[name] = this.data[name];
11273         }
11274         this.data[name] = value;
11275         if(!this.editing && this.store){
11276             this.store.afterEdit(this);
11277         }       
11278     },
11279
11280     /**
11281      * Get the value of the named field.
11282      * @param {String} name The name of the field to get the value of.
11283      * @return {Object} The value of the field.
11284      */
11285     get : function(name){
11286         return this.data[name]; 
11287     },
11288
11289     // private
11290     beginEdit : function(){
11291         this.editing = true;
11292         this.modified = {}; 
11293     },
11294
11295     // private
11296     cancelEdit : function(){
11297         this.editing = false;
11298         delete this.modified;
11299     },
11300
11301     // private
11302     endEdit : function(){
11303         this.editing = false;
11304         if(this.dirty && this.store){
11305             this.store.afterEdit(this);
11306         }
11307     },
11308
11309     /**
11310      * Usually called by the {@link Roo.data.Store} which owns the Record.
11311      * Rejects all changes made to the Record since either creation, or the last commit operation.
11312      * Modified fields are reverted to their original values.
11313      * <p>
11314      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11315      * of reject operations.
11316      */
11317     reject : function(){
11318         var m = this.modified;
11319         for(var n in m){
11320             if(typeof m[n] != "function"){
11321                 this.data[n] = m[n];
11322             }
11323         }
11324         this.dirty = false;
11325         delete this.modified;
11326         this.editing = false;
11327         if(this.store){
11328             this.store.afterReject(this);
11329         }
11330     },
11331
11332     /**
11333      * Usually called by the {@link Roo.data.Store} which owns the Record.
11334      * Commits all changes made to the Record since either creation, or the last commit operation.
11335      * <p>
11336      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11337      * of commit operations.
11338      */
11339     commit : function(){
11340         this.dirty = false;
11341         delete this.modified;
11342         this.editing = false;
11343         if(this.store){
11344             this.store.afterCommit(this);
11345         }
11346     },
11347
11348     // private
11349     hasError : function(){
11350         return this.error != null;
11351     },
11352
11353     // private
11354     clearError : function(){
11355         this.error = null;
11356     },
11357
11358     /**
11359      * Creates a copy of this record.
11360      * @param {String} id (optional) A new record id if you don't want to use this record's id
11361      * @return {Record}
11362      */
11363     copy : function(newId) {
11364         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11365     }
11366 };/*
11367  * Based on:
11368  * Ext JS Library 1.1.1
11369  * Copyright(c) 2006-2007, Ext JS, LLC.
11370  *
11371  * Originally Released Under LGPL - original licence link has changed is not relivant.
11372  *
11373  * Fork - LGPL
11374  * <script type="text/javascript">
11375  */
11376
11377
11378
11379 /**
11380  * @class Roo.data.Store
11381  * @extends Roo.util.Observable
11382  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11383  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11384  * <p>
11385  * 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
11386  * has no knowledge of the format of the data returned by the Proxy.<br>
11387  * <p>
11388  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11389  * instances from the data object. These records are cached and made available through accessor functions.
11390  * @constructor
11391  * Creates a new Store.
11392  * @param {Object} config A config object containing the objects needed for the Store to access data,
11393  * and read the data into Records.
11394  */
11395 Roo.data.Store = function(config){
11396     this.data = new Roo.util.MixedCollection(false);
11397     this.data.getKey = function(o){
11398         return o.id;
11399     };
11400     this.baseParams = {};
11401     // private
11402     this.paramNames = {
11403         "start" : "start",
11404         "limit" : "limit",
11405         "sort" : "sort",
11406         "dir" : "dir",
11407         "multisort" : "_multisort"
11408     };
11409
11410     if(config && config.data){
11411         this.inlineData = config.data;
11412         delete config.data;
11413     }
11414
11415     Roo.apply(this, config);
11416     
11417     if(this.reader){ // reader passed
11418         this.reader = Roo.factory(this.reader, Roo.data);
11419         this.reader.xmodule = this.xmodule || false;
11420         if(!this.recordType){
11421             this.recordType = this.reader.recordType;
11422         }
11423         if(this.reader.onMetaChange){
11424             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11425         }
11426     }
11427
11428     if(this.recordType){
11429         this.fields = this.recordType.prototype.fields;
11430     }
11431     this.modified = [];
11432
11433     this.addEvents({
11434         /**
11435          * @event datachanged
11436          * Fires when the data cache has changed, and a widget which is using this Store
11437          * as a Record cache should refresh its view.
11438          * @param {Store} this
11439          */
11440         datachanged : true,
11441         /**
11442          * @event metachange
11443          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11444          * @param {Store} this
11445          * @param {Object} meta The JSON metadata
11446          */
11447         metachange : true,
11448         /**
11449          * @event add
11450          * Fires when Records have been added to the Store
11451          * @param {Store} this
11452          * @param {Roo.data.Record[]} records The array of Records added
11453          * @param {Number} index The index at which the record(s) were added
11454          */
11455         add : true,
11456         /**
11457          * @event remove
11458          * Fires when a Record has been removed from the Store
11459          * @param {Store} this
11460          * @param {Roo.data.Record} record The Record that was removed
11461          * @param {Number} index The index at which the record was removed
11462          */
11463         remove : true,
11464         /**
11465          * @event update
11466          * Fires when a Record has been updated
11467          * @param {Store} this
11468          * @param {Roo.data.Record} record The Record that was updated
11469          * @param {String} operation The update operation being performed.  Value may be one of:
11470          * <pre><code>
11471  Roo.data.Record.EDIT
11472  Roo.data.Record.REJECT
11473  Roo.data.Record.COMMIT
11474          * </code></pre>
11475          */
11476         update : true,
11477         /**
11478          * @event clear
11479          * Fires when the data cache has been cleared.
11480          * @param {Store} this
11481          */
11482         clear : true,
11483         /**
11484          * @event beforeload
11485          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11486          * the load action will be canceled.
11487          * @param {Store} this
11488          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11489          */
11490         beforeload : true,
11491         /**
11492          * @event beforeloadadd
11493          * Fires after a new set of Records has been loaded.
11494          * @param {Store} this
11495          * @param {Roo.data.Record[]} records The Records that were loaded
11496          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11497          */
11498         beforeloadadd : true,
11499         /**
11500          * @event load
11501          * Fires after a new set of Records has been loaded, before they are added to the store.
11502          * @param {Store} this
11503          * @param {Roo.data.Record[]} records The Records that were loaded
11504          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11505          * @params {Object} return from reader
11506          */
11507         load : true,
11508         /**
11509          * @event loadexception
11510          * Fires if an exception occurs in the Proxy during loading.
11511          * Called with the signature of the Proxy's "loadexception" event.
11512          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11513          * 
11514          * @param {Proxy} 
11515          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11516          * @param {Object} load options 
11517          * @param {Object} jsonData from your request (normally this contains the Exception)
11518          */
11519         loadexception : true
11520     });
11521     
11522     if(this.proxy){
11523         this.proxy = Roo.factory(this.proxy, Roo.data);
11524         this.proxy.xmodule = this.xmodule || false;
11525         this.relayEvents(this.proxy,  ["loadexception"]);
11526     }
11527     this.sortToggle = {};
11528     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11529
11530     Roo.data.Store.superclass.constructor.call(this);
11531
11532     if(this.inlineData){
11533         this.loadData(this.inlineData);
11534         delete this.inlineData;
11535     }
11536 };
11537
11538 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11539      /**
11540     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11541     * without a remote query - used by combo/forms at present.
11542     */
11543     
11544     /**
11545     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11546     */
11547     /**
11548     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11549     */
11550     /**
11551     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11552     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11553     */
11554     /**
11555     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11556     * on any HTTP request
11557     */
11558     /**
11559     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11560     */
11561     /**
11562     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11563     */
11564     multiSort: false,
11565     /**
11566     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11567     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11568     */
11569     remoteSort : false,
11570
11571     /**
11572     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11573      * loaded or when a record is removed. (defaults to false).
11574     */
11575     pruneModifiedRecords : false,
11576
11577     // private
11578     lastOptions : null,
11579
11580     /**
11581      * Add Records to the Store and fires the add event.
11582      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11583      */
11584     add : function(records){
11585         records = [].concat(records);
11586         for(var i = 0, len = records.length; i < len; i++){
11587             records[i].join(this);
11588         }
11589         var index = this.data.length;
11590         this.data.addAll(records);
11591         this.fireEvent("add", this, records, index);
11592     },
11593
11594     /**
11595      * Remove a Record from the Store and fires the remove event.
11596      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11597      */
11598     remove : function(record){
11599         var index = this.data.indexOf(record);
11600         this.data.removeAt(index);
11601  
11602         if(this.pruneModifiedRecords){
11603             this.modified.remove(record);
11604         }
11605         this.fireEvent("remove", this, record, index);
11606     },
11607
11608     /**
11609      * Remove all Records from the Store and fires the clear event.
11610      */
11611     removeAll : function(){
11612         this.data.clear();
11613         if(this.pruneModifiedRecords){
11614             this.modified = [];
11615         }
11616         this.fireEvent("clear", this);
11617     },
11618
11619     /**
11620      * Inserts Records to the Store at the given index and fires the add event.
11621      * @param {Number} index The start index at which to insert the passed Records.
11622      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11623      */
11624     insert : function(index, records){
11625         records = [].concat(records);
11626         for(var i = 0, len = records.length; i < len; i++){
11627             this.data.insert(index, records[i]);
11628             records[i].join(this);
11629         }
11630         this.fireEvent("add", this, records, index);
11631     },
11632
11633     /**
11634      * Get the index within the cache of the passed Record.
11635      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11636      * @return {Number} The index of the passed Record. Returns -1 if not found.
11637      */
11638     indexOf : function(record){
11639         return this.data.indexOf(record);
11640     },
11641
11642     /**
11643      * Get the index within the cache of the Record with the passed id.
11644      * @param {String} id The id of the Record to find.
11645      * @return {Number} The index of the Record. Returns -1 if not found.
11646      */
11647     indexOfId : function(id){
11648         return this.data.indexOfKey(id);
11649     },
11650
11651     /**
11652      * Get the Record with the specified id.
11653      * @param {String} id The id of the Record to find.
11654      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11655      */
11656     getById : function(id){
11657         return this.data.key(id);
11658     },
11659
11660     /**
11661      * Get the Record at the specified index.
11662      * @param {Number} index The index of the Record to find.
11663      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11664      */
11665     getAt : function(index){
11666         return this.data.itemAt(index);
11667     },
11668
11669     /**
11670      * Returns a range of Records between specified indices.
11671      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11672      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11673      * @return {Roo.data.Record[]} An array of Records
11674      */
11675     getRange : function(start, end){
11676         return this.data.getRange(start, end);
11677     },
11678
11679     // private
11680     storeOptions : function(o){
11681         o = Roo.apply({}, o);
11682         delete o.callback;
11683         delete o.scope;
11684         this.lastOptions = o;
11685     },
11686
11687     /**
11688      * Loads the Record cache from the configured Proxy using the configured Reader.
11689      * <p>
11690      * If using remote paging, then the first load call must specify the <em>start</em>
11691      * and <em>limit</em> properties in the options.params property to establish the initial
11692      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11693      * <p>
11694      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11695      * and this call will return before the new data has been loaded. Perform any post-processing
11696      * in a callback function, or in a "load" event handler.</strong>
11697      * <p>
11698      * @param {Object} options An object containing properties which control loading options:<ul>
11699      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11700      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11701      * passed the following arguments:<ul>
11702      * <li>r : Roo.data.Record[]</li>
11703      * <li>options: Options object from the load call</li>
11704      * <li>success: Boolean success indicator</li></ul></li>
11705      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11706      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11707      * </ul>
11708      */
11709     load : function(options){
11710         options = options || {};
11711         if(this.fireEvent("beforeload", this, options) !== false){
11712             this.storeOptions(options);
11713             var p = Roo.apply(options.params || {}, this.baseParams);
11714             // if meta was not loaded from remote source.. try requesting it.
11715             if (!this.reader.metaFromRemote) {
11716                 p._requestMeta = 1;
11717             }
11718             if(this.sortInfo && this.remoteSort){
11719                 var pn = this.paramNames;
11720                 p[pn["sort"]] = this.sortInfo.field;
11721                 p[pn["dir"]] = this.sortInfo.direction;
11722             }
11723             if (this.multiSort) {
11724                 var pn = this.paramNames;
11725                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11726             }
11727             
11728             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11729         }
11730     },
11731
11732     /**
11733      * Reloads the Record cache from the configured Proxy using the configured Reader and
11734      * the options from the last load operation performed.
11735      * @param {Object} options (optional) An object containing properties which may override the options
11736      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11737      * the most recently used options are reused).
11738      */
11739     reload : function(options){
11740         this.load(Roo.applyIf(options||{}, this.lastOptions));
11741     },
11742
11743     // private
11744     // Called as a callback by the Reader during a load operation.
11745     loadRecords : function(o, options, success){
11746         if(!o || success === false){
11747             if(success !== false){
11748                 this.fireEvent("load", this, [], options, o);
11749             }
11750             if(options.callback){
11751                 options.callback.call(options.scope || this, [], options, false);
11752             }
11753             return;
11754         }
11755         // if data returned failure - throw an exception.
11756         if (o.success === false) {
11757             // show a message if no listener is registered.
11758             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11759                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11760             }
11761             // loadmask wil be hooked into this..
11762             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11763             return;
11764         }
11765         var r = o.records, t = o.totalRecords || r.length;
11766         
11767         this.fireEvent("beforeloadadd", this, r, options, o);
11768         
11769         if(!options || options.add !== true){
11770             if(this.pruneModifiedRecords){
11771                 this.modified = [];
11772             }
11773             for(var i = 0, len = r.length; i < len; i++){
11774                 r[i].join(this);
11775             }
11776             if(this.snapshot){
11777                 this.data = this.snapshot;
11778                 delete this.snapshot;
11779             }
11780             this.data.clear();
11781             this.data.addAll(r);
11782             this.totalLength = t;
11783             this.applySort();
11784             this.fireEvent("datachanged", this);
11785         }else{
11786             this.totalLength = Math.max(t, this.data.length+r.length);
11787             this.add(r);
11788         }
11789         
11790         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11791                 
11792             var e = new Roo.data.Record({});
11793
11794             e.set(this.parent.displayField, this.parent.emptyTitle);
11795             e.set(this.parent.valueField, '');
11796
11797             this.insert(0, e);
11798         }
11799             
11800         this.fireEvent("load", this, r, options, o);
11801         if(options.callback){
11802             options.callback.call(options.scope || this, r, options, true);
11803         }
11804     },
11805
11806
11807     /**
11808      * Loads data from a passed data block. A Reader which understands the format of the data
11809      * must have been configured in the constructor.
11810      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11811      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11812      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11813      */
11814     loadData : function(o, append){
11815         var r = this.reader.readRecords(o);
11816         this.loadRecords(r, {add: append}, true);
11817     },
11818
11819     /**
11820      * Gets the number of cached records.
11821      * <p>
11822      * <em>If using paging, this may not be the total size of the dataset. If the data object
11823      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11824      * the data set size</em>
11825      */
11826     getCount : function(){
11827         return this.data.length || 0;
11828     },
11829
11830     /**
11831      * Gets the total number of records in the dataset as returned by the server.
11832      * <p>
11833      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11834      * the dataset size</em>
11835      */
11836     getTotalCount : function(){
11837         return this.totalLength || 0;
11838     },
11839
11840     /**
11841      * Returns the sort state of the Store as an object with two properties:
11842      * <pre><code>
11843  field {String} The name of the field by which the Records are sorted
11844  direction {String} The sort order, "ASC" or "DESC"
11845      * </code></pre>
11846      */
11847     getSortState : function(){
11848         return this.sortInfo;
11849     },
11850
11851     // private
11852     applySort : function(){
11853         if(this.sortInfo && !this.remoteSort){
11854             var s = this.sortInfo, f = s.field;
11855             var st = this.fields.get(f).sortType;
11856             var fn = function(r1, r2){
11857                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11858                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11859             };
11860             this.data.sort(s.direction, fn);
11861             if(this.snapshot && this.snapshot != this.data){
11862                 this.snapshot.sort(s.direction, fn);
11863             }
11864         }
11865     },
11866
11867     /**
11868      * Sets the default sort column and order to be used by the next load operation.
11869      * @param {String} fieldName The name of the field to sort by.
11870      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11871      */
11872     setDefaultSort : function(field, dir){
11873         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11874     },
11875
11876     /**
11877      * Sort the Records.
11878      * If remote sorting is used, the sort is performed on the server, and the cache is
11879      * reloaded. If local sorting is used, the cache is sorted internally.
11880      * @param {String} fieldName The name of the field to sort by.
11881      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11882      */
11883     sort : function(fieldName, dir){
11884         var f = this.fields.get(fieldName);
11885         if(!dir){
11886             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11887             
11888             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11889                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11890             }else{
11891                 dir = f.sortDir;
11892             }
11893         }
11894         this.sortToggle[f.name] = dir;
11895         this.sortInfo = {field: f.name, direction: dir};
11896         if(!this.remoteSort){
11897             this.applySort();
11898             this.fireEvent("datachanged", this);
11899         }else{
11900             this.load(this.lastOptions);
11901         }
11902     },
11903
11904     /**
11905      * Calls the specified function for each of the Records in the cache.
11906      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11907      * Returning <em>false</em> aborts and exits the iteration.
11908      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11909      */
11910     each : function(fn, scope){
11911         this.data.each(fn, scope);
11912     },
11913
11914     /**
11915      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11916      * (e.g., during paging).
11917      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11918      */
11919     getModifiedRecords : function(){
11920         return this.modified;
11921     },
11922
11923     // private
11924     createFilterFn : function(property, value, anyMatch){
11925         if(!value.exec){ // not a regex
11926             value = String(value);
11927             if(value.length == 0){
11928                 return false;
11929             }
11930             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11931         }
11932         return function(r){
11933             return value.test(r.data[property]);
11934         };
11935     },
11936
11937     /**
11938      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11939      * @param {String} property A field on your records
11940      * @param {Number} start The record index to start at (defaults to 0)
11941      * @param {Number} end The last record index to include (defaults to length - 1)
11942      * @return {Number} The sum
11943      */
11944     sum : function(property, start, end){
11945         var rs = this.data.items, v = 0;
11946         start = start || 0;
11947         end = (end || end === 0) ? end : rs.length-1;
11948
11949         for(var i = start; i <= end; i++){
11950             v += (rs[i].data[property] || 0);
11951         }
11952         return v;
11953     },
11954
11955     /**
11956      * Filter the records by a specified property.
11957      * @param {String} field A field on your records
11958      * @param {String/RegExp} value Either a string that the field
11959      * should start with or a RegExp to test against the field
11960      * @param {Boolean} anyMatch True to match any part not just the beginning
11961      */
11962     filter : function(property, value, anyMatch){
11963         var fn = this.createFilterFn(property, value, anyMatch);
11964         return fn ? this.filterBy(fn) : this.clearFilter();
11965     },
11966
11967     /**
11968      * Filter by a function. The specified function will be called with each
11969      * record in this data source. If the function returns true the record is included,
11970      * otherwise it is filtered.
11971      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11972      * @param {Object} scope (optional) The scope of the function (defaults to this)
11973      */
11974     filterBy : function(fn, scope){
11975         this.snapshot = this.snapshot || this.data;
11976         this.data = this.queryBy(fn, scope||this);
11977         this.fireEvent("datachanged", this);
11978     },
11979
11980     /**
11981      * Query the records by a specified property.
11982      * @param {String} field A field on your records
11983      * @param {String/RegExp} value Either a string that the field
11984      * should start with or a RegExp to test against the field
11985      * @param {Boolean} anyMatch True to match any part not just the beginning
11986      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11987      */
11988     query : function(property, value, anyMatch){
11989         var fn = this.createFilterFn(property, value, anyMatch);
11990         return fn ? this.queryBy(fn) : this.data.clone();
11991     },
11992
11993     /**
11994      * Query by a function. The specified function will be called with each
11995      * record in this data source. If the function returns true the record is included
11996      * in the results.
11997      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11998      * @param {Object} scope (optional) The scope of the function (defaults to this)
11999       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12000      **/
12001     queryBy : function(fn, scope){
12002         var data = this.snapshot || this.data;
12003         return data.filterBy(fn, scope||this);
12004     },
12005
12006     /**
12007      * Collects unique values for a particular dataIndex from this store.
12008      * @param {String} dataIndex The property to collect
12009      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
12010      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
12011      * @return {Array} An array of the unique values
12012      **/
12013     collect : function(dataIndex, allowNull, bypassFilter){
12014         var d = (bypassFilter === true && this.snapshot) ?
12015                 this.snapshot.items : this.data.items;
12016         var v, sv, r = [], l = {};
12017         for(var i = 0, len = d.length; i < len; i++){
12018             v = d[i].data[dataIndex];
12019             sv = String(v);
12020             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
12021                 l[sv] = true;
12022                 r[r.length] = v;
12023             }
12024         }
12025         return r;
12026     },
12027
12028     /**
12029      * Revert to a view of the Record cache with no filtering applied.
12030      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
12031      */
12032     clearFilter : function(suppressEvent){
12033         if(this.snapshot && this.snapshot != this.data){
12034             this.data = this.snapshot;
12035             delete this.snapshot;
12036             if(suppressEvent !== true){
12037                 this.fireEvent("datachanged", this);
12038             }
12039         }
12040     },
12041
12042     // private
12043     afterEdit : function(record){
12044         if(this.modified.indexOf(record) == -1){
12045             this.modified.push(record);
12046         }
12047         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
12048     },
12049     
12050     // private
12051     afterReject : function(record){
12052         this.modified.remove(record);
12053         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
12054     },
12055
12056     // private
12057     afterCommit : function(record){
12058         this.modified.remove(record);
12059         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
12060     },
12061
12062     /**
12063      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
12064      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
12065      */
12066     commitChanges : function(){
12067         var m = this.modified.slice(0);
12068         this.modified = [];
12069         for(var i = 0, len = m.length; i < len; i++){
12070             m[i].commit();
12071         }
12072     },
12073
12074     /**
12075      * Cancel outstanding changes on all changed records.
12076      */
12077     rejectChanges : function(){
12078         var m = this.modified.slice(0);
12079         this.modified = [];
12080         for(var i = 0, len = m.length; i < len; i++){
12081             m[i].reject();
12082         }
12083     },
12084
12085     onMetaChange : function(meta, rtype, o){
12086         this.recordType = rtype;
12087         this.fields = rtype.prototype.fields;
12088         delete this.snapshot;
12089         this.sortInfo = meta.sortInfo || this.sortInfo;
12090         this.modified = [];
12091         this.fireEvent('metachange', this, this.reader.meta);
12092     },
12093     
12094     moveIndex : function(data, type)
12095     {
12096         var index = this.indexOf(data);
12097         
12098         var newIndex = index + type;
12099         
12100         this.remove(data);
12101         
12102         this.insert(newIndex, data);
12103         
12104     }
12105 });/*
12106  * Based on:
12107  * Ext JS Library 1.1.1
12108  * Copyright(c) 2006-2007, Ext JS, LLC.
12109  *
12110  * Originally Released Under LGPL - original licence link has changed is not relivant.
12111  *
12112  * Fork - LGPL
12113  * <script type="text/javascript">
12114  */
12115
12116 /**
12117  * @class Roo.data.SimpleStore
12118  * @extends Roo.data.Store
12119  * Small helper class to make creating Stores from Array data easier.
12120  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
12121  * @cfg {Array} fields An array of field definition objects, or field name strings.
12122  * @cfg {Array} data The multi-dimensional array of data
12123  * @constructor
12124  * @param {Object} config
12125  */
12126 Roo.data.SimpleStore = function(config){
12127     Roo.data.SimpleStore.superclass.constructor.call(this, {
12128         isLocal : true,
12129         reader: new Roo.data.ArrayReader({
12130                 id: config.id
12131             },
12132             Roo.data.Record.create(config.fields)
12133         ),
12134         proxy : new Roo.data.MemoryProxy(config.data)
12135     });
12136     this.load();
12137 };
12138 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
12139  * Based on:
12140  * Ext JS Library 1.1.1
12141  * Copyright(c) 2006-2007, Ext JS, LLC.
12142  *
12143  * Originally Released Under LGPL - original licence link has changed is not relivant.
12144  *
12145  * Fork - LGPL
12146  * <script type="text/javascript">
12147  */
12148
12149 /**
12150 /**
12151  * @extends Roo.data.Store
12152  * @class Roo.data.JsonStore
12153  * Small helper class to make creating Stores for JSON data easier. <br/>
12154 <pre><code>
12155 var store = new Roo.data.JsonStore({
12156     url: 'get-images.php',
12157     root: 'images',
12158     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
12159 });
12160 </code></pre>
12161  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12162  * JsonReader and HttpProxy (unless inline data is provided).</b>
12163  * @cfg {Array} fields An array of field definition objects, or field name strings.
12164  * @constructor
12165  * @param {Object} config
12166  */
12167 Roo.data.JsonStore = function(c){
12168     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12169         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12170         reader: new Roo.data.JsonReader(c, c.fields)
12171     }));
12172 };
12173 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12174  * Based on:
12175  * Ext JS Library 1.1.1
12176  * Copyright(c) 2006-2007, Ext JS, LLC.
12177  *
12178  * Originally Released Under LGPL - original licence link has changed is not relivant.
12179  *
12180  * Fork - LGPL
12181  * <script type="text/javascript">
12182  */
12183
12184  
12185 Roo.data.Field = function(config){
12186     if(typeof config == "string"){
12187         config = {name: config};
12188     }
12189     Roo.apply(this, config);
12190     
12191     if(!this.type){
12192         this.type = "auto";
12193     }
12194     
12195     var st = Roo.data.SortTypes;
12196     // named sortTypes are supported, here we look them up
12197     if(typeof this.sortType == "string"){
12198         this.sortType = st[this.sortType];
12199     }
12200     
12201     // set default sortType for strings and dates
12202     if(!this.sortType){
12203         switch(this.type){
12204             case "string":
12205                 this.sortType = st.asUCString;
12206                 break;
12207             case "date":
12208                 this.sortType = st.asDate;
12209                 break;
12210             default:
12211                 this.sortType = st.none;
12212         }
12213     }
12214
12215     // define once
12216     var stripRe = /[\$,%]/g;
12217
12218     // prebuilt conversion function for this field, instead of
12219     // switching every time we're reading a value
12220     if(!this.convert){
12221         var cv, dateFormat = this.dateFormat;
12222         switch(this.type){
12223             case "":
12224             case "auto":
12225             case undefined:
12226                 cv = function(v){ return v; };
12227                 break;
12228             case "string":
12229                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12230                 break;
12231             case "int":
12232                 cv = function(v){
12233                     return v !== undefined && v !== null && v !== '' ?
12234                            parseInt(String(v).replace(stripRe, ""), 10) : '';
12235                     };
12236                 break;
12237             case "float":
12238                 cv = function(v){
12239                     return v !== undefined && v !== null && v !== '' ?
12240                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
12241                     };
12242                 break;
12243             case "bool":
12244             case "boolean":
12245                 cv = function(v){ return v === true || v === "true" || v == 1; };
12246                 break;
12247             case "date":
12248                 cv = function(v){
12249                     if(!v){
12250                         return '';
12251                     }
12252                     if(v instanceof Date){
12253                         return v;
12254                     }
12255                     if(dateFormat){
12256                         if(dateFormat == "timestamp"){
12257                             return new Date(v*1000);
12258                         }
12259                         return Date.parseDate(v, dateFormat);
12260                     }
12261                     var parsed = Date.parse(v);
12262                     return parsed ? new Date(parsed) : null;
12263                 };
12264              break;
12265             
12266         }
12267         this.convert = cv;
12268     }
12269 };
12270
12271 Roo.data.Field.prototype = {
12272     dateFormat: null,
12273     defaultValue: "",
12274     mapping: null,
12275     sortType : null,
12276     sortDir : "ASC"
12277 };/*
12278  * Based on:
12279  * Ext JS Library 1.1.1
12280  * Copyright(c) 2006-2007, Ext JS, LLC.
12281  *
12282  * Originally Released Under LGPL - original licence link has changed is not relivant.
12283  *
12284  * Fork - LGPL
12285  * <script type="text/javascript">
12286  */
12287  
12288 // Base class for reading structured data from a data source.  This class is intended to be
12289 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12290
12291 /**
12292  * @class Roo.data.DataReader
12293  * Base class for reading structured data from a data source.  This class is intended to be
12294  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12295  */
12296
12297 Roo.data.DataReader = function(meta, recordType){
12298     
12299     this.meta = meta;
12300     
12301     this.recordType = recordType instanceof Array ? 
12302         Roo.data.Record.create(recordType) : recordType;
12303 };
12304
12305 Roo.data.DataReader.prototype = {
12306      /**
12307      * Create an empty record
12308      * @param {Object} data (optional) - overlay some values
12309      * @return {Roo.data.Record} record created.
12310      */
12311     newRow :  function(d) {
12312         var da =  {};
12313         this.recordType.prototype.fields.each(function(c) {
12314             switch( c.type) {
12315                 case 'int' : da[c.name] = 0; break;
12316                 case 'date' : da[c.name] = new Date(); break;
12317                 case 'float' : da[c.name] = 0.0; break;
12318                 case 'boolean' : da[c.name] = false; break;
12319                 default : da[c.name] = ""; break;
12320             }
12321             
12322         });
12323         return new this.recordType(Roo.apply(da, d));
12324     }
12325     
12326 };/*
12327  * Based on:
12328  * Ext JS Library 1.1.1
12329  * Copyright(c) 2006-2007, Ext JS, LLC.
12330  *
12331  * Originally Released Under LGPL - original licence link has changed is not relivant.
12332  *
12333  * Fork - LGPL
12334  * <script type="text/javascript">
12335  */
12336
12337 /**
12338  * @class Roo.data.DataProxy
12339  * @extends Roo.data.Observable
12340  * This class is an abstract base class for implementations which provide retrieval of
12341  * unformatted data objects.<br>
12342  * <p>
12343  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12344  * (of the appropriate type which knows how to parse the data object) to provide a block of
12345  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12346  * <p>
12347  * Custom implementations must implement the load method as described in
12348  * {@link Roo.data.HttpProxy#load}.
12349  */
12350 Roo.data.DataProxy = function(){
12351     this.addEvents({
12352         /**
12353          * @event beforeload
12354          * Fires before a network request is made to retrieve a data object.
12355          * @param {Object} This DataProxy object.
12356          * @param {Object} params The params parameter to the load function.
12357          */
12358         beforeload : true,
12359         /**
12360          * @event load
12361          * Fires before the load method's callback is called.
12362          * @param {Object} This DataProxy object.
12363          * @param {Object} o The data object.
12364          * @param {Object} arg The callback argument object passed to the load function.
12365          */
12366         load : true,
12367         /**
12368          * @event loadexception
12369          * Fires if an Exception occurs during data retrieval.
12370          * @param {Object} This DataProxy object.
12371          * @param {Object} o The data object.
12372          * @param {Object} arg The callback argument object passed to the load function.
12373          * @param {Object} e The Exception.
12374          */
12375         loadexception : true
12376     });
12377     Roo.data.DataProxy.superclass.constructor.call(this);
12378 };
12379
12380 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12381
12382     /**
12383      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12384      */
12385 /*
12386  * Based on:
12387  * Ext JS Library 1.1.1
12388  * Copyright(c) 2006-2007, Ext JS, LLC.
12389  *
12390  * Originally Released Under LGPL - original licence link has changed is not relivant.
12391  *
12392  * Fork - LGPL
12393  * <script type="text/javascript">
12394  */
12395 /**
12396  * @class Roo.data.MemoryProxy
12397  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12398  * to the Reader when its load method is called.
12399  * @constructor
12400  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12401  */
12402 Roo.data.MemoryProxy = function(data){
12403     if (data.data) {
12404         data = data.data;
12405     }
12406     Roo.data.MemoryProxy.superclass.constructor.call(this);
12407     this.data = data;
12408 };
12409
12410 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12411     
12412     /**
12413      * Load data from the requested source (in this case an in-memory
12414      * data object passed to the constructor), read the data object into
12415      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12416      * process that block using the passed callback.
12417      * @param {Object} params This parameter is not used by the MemoryProxy class.
12418      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12419      * object into a block of Roo.data.Records.
12420      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12421      * The function must be passed <ul>
12422      * <li>The Record block object</li>
12423      * <li>The "arg" argument from the load function</li>
12424      * <li>A boolean success indicator</li>
12425      * </ul>
12426      * @param {Object} scope The scope in which to call the callback
12427      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12428      */
12429     load : function(params, reader, callback, scope, arg){
12430         params = params || {};
12431         var result;
12432         try {
12433             result = reader.readRecords(params.data ? params.data :this.data);
12434         }catch(e){
12435             this.fireEvent("loadexception", this, arg, null, e);
12436             callback.call(scope, null, arg, false);
12437             return;
12438         }
12439         callback.call(scope, result, arg, true);
12440     },
12441     
12442     // private
12443     update : function(params, records){
12444         
12445     }
12446 });/*
12447  * Based on:
12448  * Ext JS Library 1.1.1
12449  * Copyright(c) 2006-2007, Ext JS, LLC.
12450  *
12451  * Originally Released Under LGPL - original licence link has changed is not relivant.
12452  *
12453  * Fork - LGPL
12454  * <script type="text/javascript">
12455  */
12456 /**
12457  * @class Roo.data.HttpProxy
12458  * @extends Roo.data.DataProxy
12459  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12460  * configured to reference a certain URL.<br><br>
12461  * <p>
12462  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12463  * from which the running page was served.<br><br>
12464  * <p>
12465  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12466  * <p>
12467  * Be aware that to enable the browser to parse an XML document, the server must set
12468  * the Content-Type header in the HTTP response to "text/xml".
12469  * @constructor
12470  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12471  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12472  * will be used to make the request.
12473  */
12474 Roo.data.HttpProxy = function(conn){
12475     Roo.data.HttpProxy.superclass.constructor.call(this);
12476     // is conn a conn config or a real conn?
12477     this.conn = conn;
12478     this.useAjax = !conn || !conn.events;
12479   
12480 };
12481
12482 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12483     // thse are take from connection...
12484     
12485     /**
12486      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12487      */
12488     /**
12489      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12490      * extra parameters to each request made by this object. (defaults to undefined)
12491      */
12492     /**
12493      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12494      *  to each request made by this object. (defaults to undefined)
12495      */
12496     /**
12497      * @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)
12498      */
12499     /**
12500      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12501      */
12502      /**
12503      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12504      * @type Boolean
12505      */
12506   
12507
12508     /**
12509      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12510      * @type Boolean
12511      */
12512     /**
12513      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12514      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12515      * a finer-grained basis than the DataProxy events.
12516      */
12517     getConnection : function(){
12518         return this.useAjax ? Roo.Ajax : this.conn;
12519     },
12520
12521     /**
12522      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12523      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12524      * process that block using the passed callback.
12525      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12526      * for the request to the remote server.
12527      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12528      * object into a block of Roo.data.Records.
12529      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12530      * The function must be passed <ul>
12531      * <li>The Record block object</li>
12532      * <li>The "arg" argument from the load function</li>
12533      * <li>A boolean success indicator</li>
12534      * </ul>
12535      * @param {Object} scope The scope in which to call the callback
12536      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12537      */
12538     load : function(params, reader, callback, scope, arg){
12539         if(this.fireEvent("beforeload", this, params) !== false){
12540             var  o = {
12541                 params : params || {},
12542                 request: {
12543                     callback : callback,
12544                     scope : scope,
12545                     arg : arg
12546                 },
12547                 reader: reader,
12548                 callback : this.loadResponse,
12549                 scope: this
12550             };
12551             if(this.useAjax){
12552                 Roo.applyIf(o, this.conn);
12553                 if(this.activeRequest){
12554                     Roo.Ajax.abort(this.activeRequest);
12555                 }
12556                 this.activeRequest = Roo.Ajax.request(o);
12557             }else{
12558                 this.conn.request(o);
12559             }
12560         }else{
12561             callback.call(scope||this, null, arg, false);
12562         }
12563     },
12564
12565     // private
12566     loadResponse : function(o, success, response){
12567         delete this.activeRequest;
12568         if(!success){
12569             this.fireEvent("loadexception", this, o, response);
12570             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12571             return;
12572         }
12573         var result;
12574         try {
12575             result = o.reader.read(response);
12576         }catch(e){
12577             this.fireEvent("loadexception", this, o, response, e);
12578             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12579             return;
12580         }
12581         
12582         this.fireEvent("load", this, o, o.request.arg);
12583         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12584     },
12585
12586     // private
12587     update : function(dataSet){
12588
12589     },
12590
12591     // private
12592     updateResponse : function(dataSet){
12593
12594     }
12595 });/*
12596  * Based on:
12597  * Ext JS Library 1.1.1
12598  * Copyright(c) 2006-2007, Ext JS, LLC.
12599  *
12600  * Originally Released Under LGPL - original licence link has changed is not relivant.
12601  *
12602  * Fork - LGPL
12603  * <script type="text/javascript">
12604  */
12605
12606 /**
12607  * @class Roo.data.ScriptTagProxy
12608  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12609  * other than the originating domain of the running page.<br><br>
12610  * <p>
12611  * <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
12612  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12613  * <p>
12614  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12615  * source code that is used as the source inside a &lt;script> tag.<br><br>
12616  * <p>
12617  * In order for the browser to process the returned data, the server must wrap the data object
12618  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12619  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12620  * depending on whether the callback name was passed:
12621  * <p>
12622  * <pre><code>
12623 boolean scriptTag = false;
12624 String cb = request.getParameter("callback");
12625 if (cb != null) {
12626     scriptTag = true;
12627     response.setContentType("text/javascript");
12628 } else {
12629     response.setContentType("application/x-json");
12630 }
12631 Writer out = response.getWriter();
12632 if (scriptTag) {
12633     out.write(cb + "(");
12634 }
12635 out.print(dataBlock.toJsonString());
12636 if (scriptTag) {
12637     out.write(");");
12638 }
12639 </pre></code>
12640  *
12641  * @constructor
12642  * @param {Object} config A configuration object.
12643  */
12644 Roo.data.ScriptTagProxy = function(config){
12645     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12646     Roo.apply(this, config);
12647     this.head = document.getElementsByTagName("head")[0];
12648 };
12649
12650 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12651
12652 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12653     /**
12654      * @cfg {String} url The URL from which to request the data object.
12655      */
12656     /**
12657      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12658      */
12659     timeout : 30000,
12660     /**
12661      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12662      * the server the name of the callback function set up by the load call to process the returned data object.
12663      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12664      * javascript output which calls this named function passing the data object as its only parameter.
12665      */
12666     callbackParam : "callback",
12667     /**
12668      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12669      * name to the request.
12670      */
12671     nocache : true,
12672
12673     /**
12674      * Load data from the configured URL, read the data object into
12675      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12676      * process that block using the passed callback.
12677      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12678      * for the request to the remote server.
12679      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12680      * object into a block of Roo.data.Records.
12681      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12682      * The function must be passed <ul>
12683      * <li>The Record block object</li>
12684      * <li>The "arg" argument from the load function</li>
12685      * <li>A boolean success indicator</li>
12686      * </ul>
12687      * @param {Object} scope The scope in which to call the callback
12688      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12689      */
12690     load : function(params, reader, callback, scope, arg){
12691         if(this.fireEvent("beforeload", this, params) !== false){
12692
12693             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12694
12695             var url = this.url;
12696             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12697             if(this.nocache){
12698                 url += "&_dc=" + (new Date().getTime());
12699             }
12700             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12701             var trans = {
12702                 id : transId,
12703                 cb : "stcCallback"+transId,
12704                 scriptId : "stcScript"+transId,
12705                 params : params,
12706                 arg : arg,
12707                 url : url,
12708                 callback : callback,
12709                 scope : scope,
12710                 reader : reader
12711             };
12712             var conn = this;
12713
12714             window[trans.cb] = function(o){
12715                 conn.handleResponse(o, trans);
12716             };
12717
12718             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12719
12720             if(this.autoAbort !== false){
12721                 this.abort();
12722             }
12723
12724             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12725
12726             var script = document.createElement("script");
12727             script.setAttribute("src", url);
12728             script.setAttribute("type", "text/javascript");
12729             script.setAttribute("id", trans.scriptId);
12730             this.head.appendChild(script);
12731
12732             this.trans = trans;
12733         }else{
12734             callback.call(scope||this, null, arg, false);
12735         }
12736     },
12737
12738     // private
12739     isLoading : function(){
12740         return this.trans ? true : false;
12741     },
12742
12743     /**
12744      * Abort the current server request.
12745      */
12746     abort : function(){
12747         if(this.isLoading()){
12748             this.destroyTrans(this.trans);
12749         }
12750     },
12751
12752     // private
12753     destroyTrans : function(trans, isLoaded){
12754         this.head.removeChild(document.getElementById(trans.scriptId));
12755         clearTimeout(trans.timeoutId);
12756         if(isLoaded){
12757             window[trans.cb] = undefined;
12758             try{
12759                 delete window[trans.cb];
12760             }catch(e){}
12761         }else{
12762             // if hasn't been loaded, wait for load to remove it to prevent script error
12763             window[trans.cb] = function(){
12764                 window[trans.cb] = undefined;
12765                 try{
12766                     delete window[trans.cb];
12767                 }catch(e){}
12768             };
12769         }
12770     },
12771
12772     // private
12773     handleResponse : function(o, trans){
12774         this.trans = false;
12775         this.destroyTrans(trans, true);
12776         var result;
12777         try {
12778             result = trans.reader.readRecords(o);
12779         }catch(e){
12780             this.fireEvent("loadexception", this, o, trans.arg, e);
12781             trans.callback.call(trans.scope||window, null, trans.arg, false);
12782             return;
12783         }
12784         this.fireEvent("load", this, o, trans.arg);
12785         trans.callback.call(trans.scope||window, result, trans.arg, true);
12786     },
12787
12788     // private
12789     handleFailure : function(trans){
12790         this.trans = false;
12791         this.destroyTrans(trans, false);
12792         this.fireEvent("loadexception", this, null, trans.arg);
12793         trans.callback.call(trans.scope||window, null, trans.arg, false);
12794     }
12795 });/*
12796  * Based on:
12797  * Ext JS Library 1.1.1
12798  * Copyright(c) 2006-2007, Ext JS, LLC.
12799  *
12800  * Originally Released Under LGPL - original licence link has changed is not relivant.
12801  *
12802  * Fork - LGPL
12803  * <script type="text/javascript">
12804  */
12805
12806 /**
12807  * @class Roo.data.JsonReader
12808  * @extends Roo.data.DataReader
12809  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12810  * based on mappings in a provided Roo.data.Record constructor.
12811  * 
12812  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12813  * in the reply previously. 
12814  * 
12815  * <p>
12816  * Example code:
12817  * <pre><code>
12818 var RecordDef = Roo.data.Record.create([
12819     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12820     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12821 ]);
12822 var myReader = new Roo.data.JsonReader({
12823     totalProperty: "results",    // The property which contains the total dataset size (optional)
12824     root: "rows",                // The property which contains an Array of row objects
12825     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12826 }, RecordDef);
12827 </code></pre>
12828  * <p>
12829  * This would consume a JSON file like this:
12830  * <pre><code>
12831 { 'results': 2, 'rows': [
12832     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12833     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12834 }
12835 </code></pre>
12836  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12837  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12838  * paged from the remote server.
12839  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12840  * @cfg {String} root name of the property which contains the Array of row objects.
12841  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12842  * @cfg {Array} fields Array of field definition objects
12843  * @constructor
12844  * Create a new JsonReader
12845  * @param {Object} meta Metadata configuration options
12846  * @param {Object} recordType Either an Array of field definition objects,
12847  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12848  */
12849 Roo.data.JsonReader = function(meta, recordType){
12850     
12851     meta = meta || {};
12852     // set some defaults:
12853     Roo.applyIf(meta, {
12854         totalProperty: 'total',
12855         successProperty : 'success',
12856         root : 'data',
12857         id : 'id'
12858     });
12859     
12860     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12861 };
12862 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12863     
12864     /**
12865      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12866      * Used by Store query builder to append _requestMeta to params.
12867      * 
12868      */
12869     metaFromRemote : false,
12870     /**
12871      * This method is only used by a DataProxy which has retrieved data from a remote server.
12872      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12873      * @return {Object} data A data block which is used by an Roo.data.Store object as
12874      * a cache of Roo.data.Records.
12875      */
12876     read : function(response){
12877         var json = response.responseText;
12878        
12879         var o = /* eval:var:o */ eval("("+json+")");
12880         if(!o) {
12881             throw {message: "JsonReader.read: Json object not found"};
12882         }
12883         
12884         if(o.metaData){
12885             
12886             delete this.ef;
12887             this.metaFromRemote = true;
12888             this.meta = o.metaData;
12889             this.recordType = Roo.data.Record.create(o.metaData.fields);
12890             this.onMetaChange(this.meta, this.recordType, o);
12891         }
12892         return this.readRecords(o);
12893     },
12894
12895     // private function a store will implement
12896     onMetaChange : function(meta, recordType, o){
12897
12898     },
12899
12900     /**
12901          * @ignore
12902          */
12903     simpleAccess: function(obj, subsc) {
12904         return obj[subsc];
12905     },
12906
12907         /**
12908          * @ignore
12909          */
12910     getJsonAccessor: function(){
12911         var re = /[\[\.]/;
12912         return function(expr) {
12913             try {
12914                 return(re.test(expr))
12915                     ? new Function("obj", "return obj." + expr)
12916                     : function(obj){
12917                         return obj[expr];
12918                     };
12919             } catch(e){}
12920             return Roo.emptyFn;
12921         };
12922     }(),
12923
12924     /**
12925      * Create a data block containing Roo.data.Records from an XML document.
12926      * @param {Object} o An object which contains an Array of row objects in the property specified
12927      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12928      * which contains the total size of the dataset.
12929      * @return {Object} data A data block which is used by an Roo.data.Store object as
12930      * a cache of Roo.data.Records.
12931      */
12932     readRecords : function(o){
12933         /**
12934          * After any data loads, the raw JSON data is available for further custom processing.
12935          * @type Object
12936          */
12937         this.o = o;
12938         var s = this.meta, Record = this.recordType,
12939             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12940
12941 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12942         if (!this.ef) {
12943             if(s.totalProperty) {
12944                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12945                 }
12946                 if(s.successProperty) {
12947                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12948                 }
12949                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12950                 if (s.id) {
12951                         var g = this.getJsonAccessor(s.id);
12952                         this.getId = function(rec) {
12953                                 var r = g(rec);  
12954                                 return (r === undefined || r === "") ? null : r;
12955                         };
12956                 } else {
12957                         this.getId = function(){return null;};
12958                 }
12959             this.ef = [];
12960             for(var jj = 0; jj < fl; jj++){
12961                 f = fi[jj];
12962                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12963                 this.ef[jj] = this.getJsonAccessor(map);
12964             }
12965         }
12966
12967         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12968         if(s.totalProperty){
12969             var vt = parseInt(this.getTotal(o), 10);
12970             if(!isNaN(vt)){
12971                 totalRecords = vt;
12972             }
12973         }
12974         if(s.successProperty){
12975             var vs = this.getSuccess(o);
12976             if(vs === false || vs === 'false'){
12977                 success = false;
12978             }
12979         }
12980         var records = [];
12981         for(var i = 0; i < c; i++){
12982                 var n = root[i];
12983             var values = {};
12984             var id = this.getId(n);
12985             for(var j = 0; j < fl; j++){
12986                 f = fi[j];
12987             var v = this.ef[j](n);
12988             if (!f.convert) {
12989                 Roo.log('missing convert for ' + f.name);
12990                 Roo.log(f);
12991                 continue;
12992             }
12993             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12994             }
12995             var record = new Record(values, id);
12996             record.json = n;
12997             records[i] = record;
12998         }
12999         return {
13000             raw : o,
13001             success : success,
13002             records : records,
13003             totalRecords : totalRecords
13004         };
13005     }
13006 });/*
13007  * Based on:
13008  * Ext JS Library 1.1.1
13009  * Copyright(c) 2006-2007, Ext JS, LLC.
13010  *
13011  * Originally Released Under LGPL - original licence link has changed is not relivant.
13012  *
13013  * Fork - LGPL
13014  * <script type="text/javascript">
13015  */
13016
13017 /**
13018  * @class Roo.data.ArrayReader
13019  * @extends Roo.data.DataReader
13020  * Data reader class to create an Array of Roo.data.Record objects from an Array.
13021  * Each element of that Array represents a row of data fields. The
13022  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
13023  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
13024  * <p>
13025  * Example code:.
13026  * <pre><code>
13027 var RecordDef = Roo.data.Record.create([
13028     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
13029     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
13030 ]);
13031 var myReader = new Roo.data.ArrayReader({
13032     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
13033 }, RecordDef);
13034 </code></pre>
13035  * <p>
13036  * This would consume an Array like this:
13037  * <pre><code>
13038 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
13039   </code></pre>
13040  
13041  * @constructor
13042  * Create a new JsonReader
13043  * @param {Object} meta Metadata configuration options.
13044  * @param {Object|Array} recordType Either an Array of field definition objects
13045  * 
13046  * @cfg {Array} fields Array of field definition objects
13047  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
13048  * as specified to {@link Roo.data.Record#create},
13049  * or an {@link Roo.data.Record} object
13050  *
13051  * 
13052  * created using {@link Roo.data.Record#create}.
13053  */
13054 Roo.data.ArrayReader = function(meta, recordType){
13055     
13056      
13057     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
13058 };
13059
13060 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
13061     /**
13062      * Create a data block containing Roo.data.Records from an XML document.
13063      * @param {Object} o An Array of row objects which represents the dataset.
13064      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
13065      * a cache of Roo.data.Records.
13066      */
13067     readRecords : function(o){
13068         var sid = this.meta ? this.meta.id : null;
13069         var recordType = this.recordType, fields = recordType.prototype.fields;
13070         var records = [];
13071         var root = o;
13072             for(var i = 0; i < root.length; i++){
13073                     var n = root[i];
13074                 var values = {};
13075                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
13076                 for(var j = 0, jlen = fields.length; j < jlen; j++){
13077                 var f = fields.items[j];
13078                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
13079                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
13080                 v = f.convert(v);
13081                 values[f.name] = v;
13082             }
13083                 var record = new recordType(values, id);
13084                 record.json = n;
13085                 records[records.length] = record;
13086             }
13087             return {
13088                 records : records,
13089                 totalRecords : records.length
13090             };
13091     }
13092 });/*
13093  * - LGPL
13094  * * 
13095  */
13096
13097 /**
13098  * @class Roo.bootstrap.ComboBox
13099  * @extends Roo.bootstrap.TriggerField
13100  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
13101  * @cfg {Boolean} append (true|false) default false
13102  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
13103  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
13104  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
13105  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
13106  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
13107  * @cfg {Boolean} animate default true
13108  * @cfg {Boolean} emptyResultText only for touch device
13109  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
13110  * @cfg {String} emptyTitle default ''
13111  * @constructor
13112  * Create a new ComboBox.
13113  * @param {Object} config Configuration options
13114  */
13115 Roo.bootstrap.ComboBox = function(config){
13116     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
13117     this.addEvents({
13118         /**
13119          * @event expand
13120          * Fires when the dropdown list is expanded
13121         * @param {Roo.bootstrap.ComboBox} combo This combo box
13122         */
13123         'expand' : true,
13124         /**
13125          * @event collapse
13126          * Fires when the dropdown list is collapsed
13127         * @param {Roo.bootstrap.ComboBox} combo This combo box
13128         */
13129         'collapse' : true,
13130         /**
13131          * @event beforeselect
13132          * Fires before a list item is selected. Return false to cancel the selection.
13133         * @param {Roo.bootstrap.ComboBox} combo This combo box
13134         * @param {Roo.data.Record} record The data record returned from the underlying store
13135         * @param {Number} index The index of the selected item in the dropdown list
13136         */
13137         'beforeselect' : true,
13138         /**
13139          * @event select
13140          * Fires when a list item is selected
13141         * @param {Roo.bootstrap.ComboBox} combo This combo box
13142         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
13143         * @param {Number} index The index of the selected item in the dropdown list
13144         */
13145         'select' : true,
13146         /**
13147          * @event beforequery
13148          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
13149          * The event object passed has these properties:
13150         * @param {Roo.bootstrap.ComboBox} combo This combo box
13151         * @param {String} query The query
13152         * @param {Boolean} forceAll true to force "all" query
13153         * @param {Boolean} cancel true to cancel the query
13154         * @param {Object} e The query event object
13155         */
13156         'beforequery': true,
13157          /**
13158          * @event add
13159          * Fires when the 'add' icon is pressed (add a listener to enable add button)
13160         * @param {Roo.bootstrap.ComboBox} combo This combo box
13161         */
13162         'add' : true,
13163         /**
13164          * @event edit
13165          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
13166         * @param {Roo.bootstrap.ComboBox} combo This combo box
13167         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13168         */
13169         'edit' : true,
13170         /**
13171          * @event remove
13172          * Fires when the remove value from the combobox array
13173         * @param {Roo.bootstrap.ComboBox} combo This combo box
13174         */
13175         'remove' : true,
13176         /**
13177          * @event afterremove
13178          * Fires when the remove value from the combobox array
13179         * @param {Roo.bootstrap.ComboBox} combo This combo box
13180         */
13181         'afterremove' : true,
13182         /**
13183          * @event specialfilter
13184          * Fires when specialfilter
13185             * @param {Roo.bootstrap.ComboBox} combo This combo box
13186             */
13187         'specialfilter' : true,
13188         /**
13189          * @event tick
13190          * Fires when tick the element
13191             * @param {Roo.bootstrap.ComboBox} combo This combo box
13192             */
13193         'tick' : true,
13194         /**
13195          * @event touchviewdisplay
13196          * Fires when touch view require special display (default is using displayField)
13197             * @param {Roo.bootstrap.ComboBox} combo This combo box
13198             * @param {Object} cfg set html .
13199             */
13200         'touchviewdisplay' : true
13201         
13202     });
13203     
13204     this.item = [];
13205     this.tickItems = [];
13206     
13207     this.selectedIndex = -1;
13208     if(this.mode == 'local'){
13209         if(config.queryDelay === undefined){
13210             this.queryDelay = 10;
13211         }
13212         if(config.minChars === undefined){
13213             this.minChars = 0;
13214         }
13215     }
13216 };
13217
13218 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13219      
13220     /**
13221      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13222      * rendering into an Roo.Editor, defaults to false)
13223      */
13224     /**
13225      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13226      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13227      */
13228     /**
13229      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13230      */
13231     /**
13232      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13233      * the dropdown list (defaults to undefined, with no header element)
13234      */
13235
13236      /**
13237      * @cfg {String/Roo.Template} tpl The template to use to render the output
13238      */
13239      
13240      /**
13241      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13242      */
13243     listWidth: undefined,
13244     /**
13245      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13246      * mode = 'remote' or 'text' if mode = 'local')
13247      */
13248     displayField: undefined,
13249     
13250     /**
13251      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13252      * mode = 'remote' or 'value' if mode = 'local'). 
13253      * Note: use of a valueField requires the user make a selection
13254      * in order for a value to be mapped.
13255      */
13256     valueField: undefined,
13257     /**
13258      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13259      */
13260     modalTitle : '',
13261     
13262     /**
13263      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13264      * field's data value (defaults to the underlying DOM element's name)
13265      */
13266     hiddenName: undefined,
13267     /**
13268      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13269      */
13270     listClass: '',
13271     /**
13272      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13273      */
13274     selectedClass: 'active',
13275     
13276     /**
13277      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13278      */
13279     shadow:'sides',
13280     /**
13281      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13282      * anchor positions (defaults to 'tl-bl')
13283      */
13284     listAlign: 'tl-bl?',
13285     /**
13286      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13287      */
13288     maxHeight: 300,
13289     /**
13290      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13291      * query specified by the allQuery config option (defaults to 'query')
13292      */
13293     triggerAction: 'query',
13294     /**
13295      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13296      * (defaults to 4, does not apply if editable = false)
13297      */
13298     minChars : 4,
13299     /**
13300      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13301      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13302      */
13303     typeAhead: false,
13304     /**
13305      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13306      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13307      */
13308     queryDelay: 500,
13309     /**
13310      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13311      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13312      */
13313     pageSize: 0,
13314     /**
13315      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13316      * when editable = true (defaults to false)
13317      */
13318     selectOnFocus:false,
13319     /**
13320      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13321      */
13322     queryParam: 'query',
13323     /**
13324      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13325      * when mode = 'remote' (defaults to 'Loading...')
13326      */
13327     loadingText: 'Loading...',
13328     /**
13329      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13330      */
13331     resizable: false,
13332     /**
13333      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13334      */
13335     handleHeight : 8,
13336     /**
13337      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13338      * traditional select (defaults to true)
13339      */
13340     editable: true,
13341     /**
13342      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13343      */
13344     allQuery: '',
13345     /**
13346      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13347      */
13348     mode: 'remote',
13349     /**
13350      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13351      * listWidth has a higher value)
13352      */
13353     minListWidth : 70,
13354     /**
13355      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13356      * allow the user to set arbitrary text into the field (defaults to false)
13357      */
13358     forceSelection:false,
13359     /**
13360      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13361      * if typeAhead = true (defaults to 250)
13362      */
13363     typeAheadDelay : 250,
13364     /**
13365      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13366      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13367      */
13368     valueNotFoundText : undefined,
13369     /**
13370      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13371      */
13372     blockFocus : false,
13373     
13374     /**
13375      * @cfg {Boolean} disableClear Disable showing of clear button.
13376      */
13377     disableClear : false,
13378     /**
13379      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13380      */
13381     alwaysQuery : false,
13382     
13383     /**
13384      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13385      */
13386     multiple : false,
13387     
13388     /**
13389      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
13390      */
13391     invalidClass : "has-warning",
13392     
13393     /**
13394      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
13395      */
13396     validClass : "has-success",
13397     
13398     /**
13399      * @cfg {Boolean} specialFilter (true|false) special filter default false
13400      */
13401     specialFilter : false,
13402     
13403     /**
13404      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13405      */
13406     mobileTouchView : true,
13407     
13408     /**
13409      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13410      */
13411     useNativeIOS : false,
13412     
13413     /**
13414      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13415      */
13416     mobile_restrict_height : false,
13417     
13418     ios_options : false,
13419     
13420     //private
13421     addicon : false,
13422     editicon: false,
13423     
13424     page: 0,
13425     hasQuery: false,
13426     append: false,
13427     loadNext: false,
13428     autoFocus : true,
13429     tickable : false,
13430     btnPosition : 'right',
13431     triggerList : true,
13432     showToggleBtn : true,
13433     animate : true,
13434     emptyResultText: 'Empty',
13435     triggerText : 'Select',
13436     emptyTitle : '',
13437     
13438     // element that contains real text value.. (when hidden is used..)
13439     
13440     getAutoCreate : function()
13441     {   
13442         var cfg = false;
13443         //render
13444         /*
13445          * Render classic select for iso
13446          */
13447         
13448         if(Roo.isIOS && this.useNativeIOS){
13449             cfg = this.getAutoCreateNativeIOS();
13450             return cfg;
13451         }
13452         
13453         /*
13454          * Touch Devices
13455          */
13456         
13457         if(Roo.isTouch && this.mobileTouchView){
13458             cfg = this.getAutoCreateTouchView();
13459             return cfg;;
13460         }
13461         
13462         /*
13463          *  Normal ComboBox
13464          */
13465         if(!this.tickable){
13466             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13467             return cfg;
13468         }
13469         
13470         /*
13471          *  ComboBox with tickable selections
13472          */
13473              
13474         var align = this.labelAlign || this.parentLabelAlign();
13475         
13476         cfg = {
13477             cls : 'form-group roo-combobox-tickable' //input-group
13478         };
13479         
13480         var btn_text_select = '';
13481         var btn_text_done = '';
13482         var btn_text_cancel = '';
13483         
13484         if (this.btn_text_show) {
13485             btn_text_select = 'Select';
13486             btn_text_done = 'Done';
13487             btn_text_cancel = 'Cancel'; 
13488         }
13489         
13490         var buttons = {
13491             tag : 'div',
13492             cls : 'tickable-buttons',
13493             cn : [
13494                 {
13495                     tag : 'button',
13496                     type : 'button',
13497                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13498                     //html : this.triggerText
13499                     html: btn_text_select
13500                 },
13501                 {
13502                     tag : 'button',
13503                     type : 'button',
13504                     name : 'ok',
13505                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13506                     //html : 'Done'
13507                     html: btn_text_done
13508                 },
13509                 {
13510                     tag : 'button',
13511                     type : 'button',
13512                     name : 'cancel',
13513                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13514                     //html : 'Cancel'
13515                     html: btn_text_cancel
13516                 }
13517             ]
13518         };
13519         
13520         if(this.editable){
13521             buttons.cn.unshift({
13522                 tag: 'input',
13523                 cls: 'roo-select2-search-field-input'
13524             });
13525         }
13526         
13527         var _this = this;
13528         
13529         Roo.each(buttons.cn, function(c){
13530             if (_this.size) {
13531                 c.cls += ' btn-' + _this.size;
13532             }
13533
13534             if (_this.disabled) {
13535                 c.disabled = true;
13536             }
13537         });
13538         
13539         var box = {
13540             tag: 'div',
13541             style : 'display: contents',
13542             cn: [
13543                 {
13544                     tag: 'input',
13545                     type : 'hidden',
13546                     cls: 'form-hidden-field'
13547                 },
13548                 {
13549                     tag: 'ul',
13550                     cls: 'roo-select2-choices',
13551                     cn:[
13552                         {
13553                             tag: 'li',
13554                             cls: 'roo-select2-search-field',
13555                             cn: [
13556                                 buttons
13557                             ]
13558                         }
13559                     ]
13560                 }
13561             ]
13562         };
13563         
13564         var combobox = {
13565             cls: 'roo-select2-container input-group roo-select2-container-multi',
13566             cn: [
13567                 
13568                 box
13569 //                {
13570 //                    tag: 'ul',
13571 //                    cls: 'typeahead typeahead-long dropdown-menu',
13572 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13573 //                }
13574             ]
13575         };
13576         
13577         if(this.hasFeedback && !this.allowBlank){
13578             
13579             var feedback = {
13580                 tag: 'span',
13581                 cls: 'glyphicon form-control-feedback'
13582             };
13583
13584             combobox.cn.push(feedback);
13585         }
13586         
13587         var indicator = {
13588             tag : 'i',
13589             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13590             tooltip : 'This field is required'
13591         };
13592         if (Roo.bootstrap.version == 4) {
13593             indicator = {
13594                 tag : 'i',
13595                 style : 'display:none'
13596             };
13597         }
13598         if (align ==='left' && this.fieldLabel.length) {
13599             
13600             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13601             
13602             cfg.cn = [
13603                 indicator,
13604                 {
13605                     tag: 'label',
13606                     'for' :  id,
13607                     cls : 'control-label col-form-label',
13608                     html : this.fieldLabel
13609
13610                 },
13611                 {
13612                     cls : "", 
13613                     cn: [
13614                         combobox
13615                     ]
13616                 }
13617
13618             ];
13619             
13620             var labelCfg = cfg.cn[1];
13621             var contentCfg = cfg.cn[2];
13622             
13623
13624             if(this.indicatorpos == 'right'){
13625                 
13626                 cfg.cn = [
13627                     {
13628                         tag: 'label',
13629                         'for' :  id,
13630                         cls : 'control-label col-form-label',
13631                         cn : [
13632                             {
13633                                 tag : 'span',
13634                                 html : this.fieldLabel
13635                             },
13636                             indicator
13637                         ]
13638                     },
13639                     {
13640                         cls : "",
13641                         cn: [
13642                             combobox
13643                         ]
13644                     }
13645
13646                 ];
13647                 
13648                 
13649                 
13650                 labelCfg = cfg.cn[0];
13651                 contentCfg = cfg.cn[1];
13652             
13653             }
13654             
13655             if(this.labelWidth > 12){
13656                 labelCfg.style = "width: " + this.labelWidth + 'px';
13657             }
13658             
13659             if(this.labelWidth < 13 && this.labelmd == 0){
13660                 this.labelmd = this.labelWidth;
13661             }
13662             
13663             if(this.labellg > 0){
13664                 labelCfg.cls += ' col-lg-' + this.labellg;
13665                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13666             }
13667             
13668             if(this.labelmd > 0){
13669                 labelCfg.cls += ' col-md-' + this.labelmd;
13670                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13671             }
13672             
13673             if(this.labelsm > 0){
13674                 labelCfg.cls += ' col-sm-' + this.labelsm;
13675                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13676             }
13677             
13678             if(this.labelxs > 0){
13679                 labelCfg.cls += ' col-xs-' + this.labelxs;
13680                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13681             }
13682                 
13683                 
13684         } else if ( this.fieldLabel.length) {
13685 //                Roo.log(" label");
13686                  cfg.cn = [
13687                    indicator,
13688                     {
13689                         tag: 'label',
13690                         //cls : 'input-group-addon',
13691                         html : this.fieldLabel
13692                     },
13693                     combobox
13694                 ];
13695                 
13696                 if(this.indicatorpos == 'right'){
13697                     cfg.cn = [
13698                         {
13699                             tag: 'label',
13700                             //cls : 'input-group-addon',
13701                             html : this.fieldLabel
13702                         },
13703                         indicator,
13704                         combobox
13705                     ];
13706                     
13707                 }
13708
13709         } else {
13710             
13711 //                Roo.log(" no label && no align");
13712                 cfg = combobox
13713                      
13714                 
13715         }
13716          
13717         var settings=this;
13718         ['xs','sm','md','lg'].map(function(size){
13719             if (settings[size]) {
13720                 cfg.cls += ' col-' + size + '-' + settings[size];
13721             }
13722         });
13723         
13724         return cfg;
13725         
13726     },
13727     
13728     _initEventsCalled : false,
13729     
13730     // private
13731     initEvents: function()
13732     {   
13733         if (this._initEventsCalled) { // as we call render... prevent looping...
13734             return;
13735         }
13736         this._initEventsCalled = true;
13737         
13738         if (!this.store) {
13739             throw "can not find store for combo";
13740         }
13741         
13742         this.indicator = this.indicatorEl();
13743         
13744         this.store = Roo.factory(this.store, Roo.data);
13745         this.store.parent = this;
13746         
13747         // if we are building from html. then this element is so complex, that we can not really
13748         // use the rendered HTML.
13749         // so we have to trash and replace the previous code.
13750         if (Roo.XComponent.build_from_html) {
13751             // remove this element....
13752             var e = this.el.dom, k=0;
13753             while (e ) { e = e.previousSibling;  ++k;}
13754
13755             this.el.remove();
13756             
13757             this.el=false;
13758             this.rendered = false;
13759             
13760             this.render(this.parent().getChildContainer(true), k);
13761         }
13762         
13763         if(Roo.isIOS && this.useNativeIOS){
13764             this.initIOSView();
13765             return;
13766         }
13767         
13768         /*
13769          * Touch Devices
13770          */
13771         
13772         if(Roo.isTouch && this.mobileTouchView){
13773             this.initTouchView();
13774             return;
13775         }
13776         
13777         if(this.tickable){
13778             this.initTickableEvents();
13779             return;
13780         }
13781         
13782         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13783         
13784         if(this.hiddenName){
13785             
13786             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13787             
13788             this.hiddenField.dom.value =
13789                 this.hiddenValue !== undefined ? this.hiddenValue :
13790                 this.value !== undefined ? this.value : '';
13791
13792             // prevent input submission
13793             this.el.dom.removeAttribute('name');
13794             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13795              
13796              
13797         }
13798         //if(Roo.isGecko){
13799         //    this.el.dom.setAttribute('autocomplete', 'off');
13800         //}
13801         
13802         var cls = 'x-combo-list';
13803         
13804         //this.list = new Roo.Layer({
13805         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13806         //});
13807         
13808         var _this = this;
13809         
13810         (function(){
13811             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13812             _this.list.setWidth(lw);
13813         }).defer(100);
13814         
13815         this.list.on('mouseover', this.onViewOver, this);
13816         this.list.on('mousemove', this.onViewMove, this);
13817         this.list.on('scroll', this.onViewScroll, this);
13818         
13819         /*
13820         this.list.swallowEvent('mousewheel');
13821         this.assetHeight = 0;
13822
13823         if(this.title){
13824             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13825             this.assetHeight += this.header.getHeight();
13826         }
13827
13828         this.innerList = this.list.createChild({cls:cls+'-inner'});
13829         this.innerList.on('mouseover', this.onViewOver, this);
13830         this.innerList.on('mousemove', this.onViewMove, this);
13831         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13832         
13833         if(this.allowBlank && !this.pageSize && !this.disableClear){
13834             this.footer = this.list.createChild({cls:cls+'-ft'});
13835             this.pageTb = new Roo.Toolbar(this.footer);
13836            
13837         }
13838         if(this.pageSize){
13839             this.footer = this.list.createChild({cls:cls+'-ft'});
13840             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13841                     {pageSize: this.pageSize});
13842             
13843         }
13844         
13845         if (this.pageTb && this.allowBlank && !this.disableClear) {
13846             var _this = this;
13847             this.pageTb.add(new Roo.Toolbar.Fill(), {
13848                 cls: 'x-btn-icon x-btn-clear',
13849                 text: '&#160;',
13850                 handler: function()
13851                 {
13852                     _this.collapse();
13853                     _this.clearValue();
13854                     _this.onSelect(false, -1);
13855                 }
13856             });
13857         }
13858         if (this.footer) {
13859             this.assetHeight += this.footer.getHeight();
13860         }
13861         */
13862             
13863         if(!this.tpl){
13864             this.tpl = Roo.bootstrap.version == 4 ?
13865                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
13866                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13867         }
13868
13869         this.view = new Roo.View(this.list, this.tpl, {
13870             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13871         });
13872         //this.view.wrapEl.setDisplayed(false);
13873         this.view.on('click', this.onViewClick, this);
13874         
13875         
13876         this.store.on('beforeload', this.onBeforeLoad, this);
13877         this.store.on('load', this.onLoad, this);
13878         this.store.on('loadexception', this.onLoadException, this);
13879         /*
13880         if(this.resizable){
13881             this.resizer = new Roo.Resizable(this.list,  {
13882                pinned:true, handles:'se'
13883             });
13884             this.resizer.on('resize', function(r, w, h){
13885                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13886                 this.listWidth = w;
13887                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13888                 this.restrictHeight();
13889             }, this);
13890             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13891         }
13892         */
13893         if(!this.editable){
13894             this.editable = true;
13895             this.setEditable(false);
13896         }
13897         
13898         /*
13899         
13900         if (typeof(this.events.add.listeners) != 'undefined') {
13901             
13902             this.addicon = this.wrap.createChild(
13903                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13904        
13905             this.addicon.on('click', function(e) {
13906                 this.fireEvent('add', this);
13907             }, this);
13908         }
13909         if (typeof(this.events.edit.listeners) != 'undefined') {
13910             
13911             this.editicon = this.wrap.createChild(
13912                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13913             if (this.addicon) {
13914                 this.editicon.setStyle('margin-left', '40px');
13915             }
13916             this.editicon.on('click', function(e) {
13917                 
13918                 // we fire even  if inothing is selected..
13919                 this.fireEvent('edit', this, this.lastData );
13920                 
13921             }, this);
13922         }
13923         */
13924         
13925         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13926             "up" : function(e){
13927                 this.inKeyMode = true;
13928                 this.selectPrev();
13929             },
13930
13931             "down" : function(e){
13932                 if(!this.isExpanded()){
13933                     this.onTriggerClick();
13934                 }else{
13935                     this.inKeyMode = true;
13936                     this.selectNext();
13937                 }
13938             },
13939
13940             "enter" : function(e){
13941 //                this.onViewClick();
13942                 //return true;
13943                 this.collapse();
13944                 
13945                 if(this.fireEvent("specialkey", this, e)){
13946                     this.onViewClick(false);
13947                 }
13948                 
13949                 return true;
13950             },
13951
13952             "esc" : function(e){
13953                 this.collapse();
13954             },
13955
13956             "tab" : function(e){
13957                 this.collapse();
13958                 
13959                 if(this.fireEvent("specialkey", this, e)){
13960                     this.onViewClick(false);
13961                 }
13962                 
13963                 return true;
13964             },
13965
13966             scope : this,
13967
13968             doRelay : function(foo, bar, hname){
13969                 if(hname == 'down' || this.scope.isExpanded()){
13970                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13971                 }
13972                 return true;
13973             },
13974
13975             forceKeyDown: true
13976         });
13977         
13978         
13979         this.queryDelay = Math.max(this.queryDelay || 10,
13980                 this.mode == 'local' ? 10 : 250);
13981         
13982         
13983         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13984         
13985         if(this.typeAhead){
13986             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13987         }
13988         if(this.editable !== false){
13989             this.inputEl().on("keyup", this.onKeyUp, this);
13990         }
13991         if(this.forceSelection){
13992             this.inputEl().on('blur', this.doForce, this);
13993         }
13994         
13995         if(this.multiple){
13996             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13997             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13998         }
13999     },
14000     
14001     initTickableEvents: function()
14002     {   
14003         this.createList();
14004         
14005         if(this.hiddenName){
14006             
14007             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14008             
14009             this.hiddenField.dom.value =
14010                 this.hiddenValue !== undefined ? this.hiddenValue :
14011                 this.value !== undefined ? this.value : '';
14012
14013             // prevent input submission
14014             this.el.dom.removeAttribute('name');
14015             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14016              
14017              
14018         }
14019         
14020 //        this.list = this.el.select('ul.dropdown-menu',true).first();
14021         
14022         this.choices = this.el.select('ul.roo-select2-choices', true).first();
14023         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14024         if(this.triggerList){
14025             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
14026         }
14027          
14028         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
14029         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
14030         
14031         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
14032         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
14033         
14034         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
14035         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
14036         
14037         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
14038         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
14039         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
14040         
14041         this.okBtn.hide();
14042         this.cancelBtn.hide();
14043         
14044         var _this = this;
14045         
14046         (function(){
14047             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
14048             _this.list.setWidth(lw);
14049         }).defer(100);
14050         
14051         this.list.on('mouseover', this.onViewOver, this);
14052         this.list.on('mousemove', this.onViewMove, this);
14053         
14054         this.list.on('scroll', this.onViewScroll, this);
14055         
14056         if(!this.tpl){
14057             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
14058                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
14059         }
14060
14061         this.view = new Roo.View(this.list, this.tpl, {
14062             singleSelect:true,
14063             tickable:true,
14064             parent:this,
14065             store: this.store,
14066             selectedClass: this.selectedClass
14067         });
14068         
14069         //this.view.wrapEl.setDisplayed(false);
14070         this.view.on('click', this.onViewClick, this);
14071         
14072         
14073         
14074         this.store.on('beforeload', this.onBeforeLoad, this);
14075         this.store.on('load', this.onLoad, this);
14076         this.store.on('loadexception', this.onLoadException, this);
14077         
14078         if(this.editable){
14079             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
14080                 "up" : function(e){
14081                     this.inKeyMode = true;
14082                     this.selectPrev();
14083                 },
14084
14085                 "down" : function(e){
14086                     this.inKeyMode = true;
14087                     this.selectNext();
14088                 },
14089
14090                 "enter" : function(e){
14091                     if(this.fireEvent("specialkey", this, e)){
14092                         this.onViewClick(false);
14093                     }
14094                     
14095                     return true;
14096                 },
14097
14098                 "esc" : function(e){
14099                     this.onTickableFooterButtonClick(e, false, false);
14100                 },
14101
14102                 "tab" : function(e){
14103                     this.fireEvent("specialkey", this, e);
14104                     
14105                     this.onTickableFooterButtonClick(e, false, false);
14106                     
14107                     return true;
14108                 },
14109
14110                 scope : this,
14111
14112                 doRelay : function(e, fn, key){
14113                     if(this.scope.isExpanded()){
14114                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14115                     }
14116                     return true;
14117                 },
14118
14119                 forceKeyDown: true
14120             });
14121         }
14122         
14123         this.queryDelay = Math.max(this.queryDelay || 10,
14124                 this.mode == 'local' ? 10 : 250);
14125         
14126         
14127         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14128         
14129         if(this.typeAhead){
14130             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14131         }
14132         
14133         if(this.editable !== false){
14134             this.tickableInputEl().on("keyup", this.onKeyUp, this);
14135         }
14136         
14137         this.indicator = this.indicatorEl();
14138         
14139         if(this.indicator){
14140             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
14141             this.indicator.hide();
14142         }
14143         
14144     },
14145
14146     onDestroy : function(){
14147         if(this.view){
14148             this.view.setStore(null);
14149             this.view.el.removeAllListeners();
14150             this.view.el.remove();
14151             this.view.purgeListeners();
14152         }
14153         if(this.list){
14154             this.list.dom.innerHTML  = '';
14155         }
14156         
14157         if(this.store){
14158             this.store.un('beforeload', this.onBeforeLoad, this);
14159             this.store.un('load', this.onLoad, this);
14160             this.store.un('loadexception', this.onLoadException, this);
14161         }
14162         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
14163     },
14164
14165     // private
14166     fireKey : function(e){
14167         if(e.isNavKeyPress() && !this.list.isVisible()){
14168             this.fireEvent("specialkey", this, e);
14169         }
14170     },
14171
14172     // private
14173     onResize: function(w, h){
14174 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14175 //        
14176 //        if(typeof w != 'number'){
14177 //            // we do not handle it!?!?
14178 //            return;
14179 //        }
14180 //        var tw = this.trigger.getWidth();
14181 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
14182 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
14183 //        var x = w - tw;
14184 //        this.inputEl().setWidth( this.adjustWidth('input', x));
14185 //            
14186 //        //this.trigger.setStyle('left', x+'px');
14187 //        
14188 //        if(this.list && this.listWidth === undefined){
14189 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14190 //            this.list.setWidth(lw);
14191 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14192 //        }
14193         
14194     
14195         
14196     },
14197
14198     /**
14199      * Allow or prevent the user from directly editing the field text.  If false is passed,
14200      * the user will only be able to select from the items defined in the dropdown list.  This method
14201      * is the runtime equivalent of setting the 'editable' config option at config time.
14202      * @param {Boolean} value True to allow the user to directly edit the field text
14203      */
14204     setEditable : function(value){
14205         if(value == this.editable){
14206             return;
14207         }
14208         this.editable = value;
14209         if(!value){
14210             this.inputEl().dom.setAttribute('readOnly', true);
14211             this.inputEl().on('mousedown', this.onTriggerClick,  this);
14212             this.inputEl().addClass('x-combo-noedit');
14213         }else{
14214             this.inputEl().dom.setAttribute('readOnly', false);
14215             this.inputEl().un('mousedown', this.onTriggerClick,  this);
14216             this.inputEl().removeClass('x-combo-noedit');
14217         }
14218     },
14219
14220     // private
14221     
14222     onBeforeLoad : function(combo,opts){
14223         if(!this.hasFocus){
14224             return;
14225         }
14226          if (!opts.add) {
14227             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14228          }
14229         this.restrictHeight();
14230         this.selectedIndex = -1;
14231     },
14232
14233     // private
14234     onLoad : function(){
14235         
14236         this.hasQuery = false;
14237         
14238         if(!this.hasFocus){
14239             return;
14240         }
14241         
14242         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14243             this.loading.hide();
14244         }
14245         
14246         if(this.store.getCount() > 0){
14247             
14248             this.expand();
14249             this.restrictHeight();
14250             if(this.lastQuery == this.allQuery){
14251                 if(this.editable && !this.tickable){
14252                     this.inputEl().dom.select();
14253                 }
14254                 
14255                 if(
14256                     !this.selectByValue(this.value, true) &&
14257                     this.autoFocus && 
14258                     (
14259                         !this.store.lastOptions ||
14260                         typeof(this.store.lastOptions.add) == 'undefined' || 
14261                         this.store.lastOptions.add != true
14262                     )
14263                 ){
14264                     this.select(0, true);
14265                 }
14266             }else{
14267                 if(this.autoFocus){
14268                     this.selectNext();
14269                 }
14270                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14271                     this.taTask.delay(this.typeAheadDelay);
14272                 }
14273             }
14274         }else{
14275             this.onEmptyResults();
14276         }
14277         
14278         //this.el.focus();
14279     },
14280     // private
14281     onLoadException : function()
14282     {
14283         this.hasQuery = false;
14284         
14285         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14286             this.loading.hide();
14287         }
14288         
14289         if(this.tickable && this.editable){
14290             return;
14291         }
14292         
14293         this.collapse();
14294         // only causes errors at present
14295         //Roo.log(this.store.reader.jsonData);
14296         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14297             // fixme
14298             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14299         //}
14300         
14301         
14302     },
14303     // private
14304     onTypeAhead : function(){
14305         if(this.store.getCount() > 0){
14306             var r = this.store.getAt(0);
14307             var newValue = r.data[this.displayField];
14308             var len = newValue.length;
14309             var selStart = this.getRawValue().length;
14310             
14311             if(selStart != len){
14312                 this.setRawValue(newValue);
14313                 this.selectText(selStart, newValue.length);
14314             }
14315         }
14316     },
14317
14318     // private
14319     onSelect : function(record, index){
14320         
14321         if(this.fireEvent('beforeselect', this, record, index) !== false){
14322         
14323             this.setFromData(index > -1 ? record.data : false);
14324             
14325             this.collapse();
14326             this.fireEvent('select', this, record, index);
14327         }
14328     },
14329
14330     /**
14331      * Returns the currently selected field value or empty string if no value is set.
14332      * @return {String} value The selected value
14333      */
14334     getValue : function()
14335     {
14336         if(Roo.isIOS && this.useNativeIOS){
14337             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14338         }
14339         
14340         if(this.multiple){
14341             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14342         }
14343         
14344         if(this.valueField){
14345             return typeof this.value != 'undefined' ? this.value : '';
14346         }else{
14347             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14348         }
14349     },
14350     
14351     getRawValue : function()
14352     {
14353         if(Roo.isIOS && this.useNativeIOS){
14354             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14355         }
14356         
14357         var v = this.inputEl().getValue();
14358         
14359         return v;
14360     },
14361
14362     /**
14363      * Clears any text/value currently set in the field
14364      */
14365     clearValue : function(){
14366         
14367         if(this.hiddenField){
14368             this.hiddenField.dom.value = '';
14369         }
14370         this.value = '';
14371         this.setRawValue('');
14372         this.lastSelectionText = '';
14373         this.lastData = false;
14374         
14375         var close = this.closeTriggerEl();
14376         
14377         if(close){
14378             close.hide();
14379         }
14380         
14381         this.validate();
14382         
14383     },
14384
14385     /**
14386      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14387      * will be displayed in the field.  If the value does not match the data value of an existing item,
14388      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14389      * Otherwise the field will be blank (although the value will still be set).
14390      * @param {String} value The value to match
14391      */
14392     setValue : function(v)
14393     {
14394         if(Roo.isIOS && this.useNativeIOS){
14395             this.setIOSValue(v);
14396             return;
14397         }
14398         
14399         if(this.multiple){
14400             this.syncValue();
14401             return;
14402         }
14403         
14404         var text = v;
14405         if(this.valueField){
14406             var r = this.findRecord(this.valueField, v);
14407             if(r){
14408                 text = r.data[this.displayField];
14409             }else if(this.valueNotFoundText !== undefined){
14410                 text = this.valueNotFoundText;
14411             }
14412         }
14413         this.lastSelectionText = text;
14414         if(this.hiddenField){
14415             this.hiddenField.dom.value = v;
14416         }
14417         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14418         this.value = v;
14419         
14420         var close = this.closeTriggerEl();
14421         
14422         if(close){
14423             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14424         }
14425         
14426         this.validate();
14427     },
14428     /**
14429      * @property {Object} the last set data for the element
14430      */
14431     
14432     lastData : false,
14433     /**
14434      * Sets the value of the field based on a object which is related to the record format for the store.
14435      * @param {Object} value the value to set as. or false on reset?
14436      */
14437     setFromData : function(o){
14438         
14439         if(this.multiple){
14440             this.addItem(o);
14441             return;
14442         }
14443             
14444         var dv = ''; // display value
14445         var vv = ''; // value value..
14446         this.lastData = o;
14447         if (this.displayField) {
14448             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14449         } else {
14450             // this is an error condition!!!
14451             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14452         }
14453         
14454         if(this.valueField){
14455             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14456         }
14457         
14458         var close = this.closeTriggerEl();
14459         
14460         if(close){
14461             if(dv.length || vv * 1 > 0){
14462                 close.show() ;
14463                 this.blockFocus=true;
14464             } else {
14465                 close.hide();
14466             }             
14467         }
14468         
14469         if(this.hiddenField){
14470             this.hiddenField.dom.value = vv;
14471             
14472             this.lastSelectionText = dv;
14473             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14474             this.value = vv;
14475             return;
14476         }
14477         // no hidden field.. - we store the value in 'value', but still display
14478         // display field!!!!
14479         this.lastSelectionText = dv;
14480         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14481         this.value = vv;
14482         
14483         
14484         
14485     },
14486     // private
14487     reset : function(){
14488         // overridden so that last data is reset..
14489         
14490         if(this.multiple){
14491             this.clearItem();
14492             return;
14493         }
14494         
14495         this.setValue(this.originalValue);
14496         //this.clearInvalid();
14497         this.lastData = false;
14498         if (this.view) {
14499             this.view.clearSelections();
14500         }
14501         
14502         this.validate();
14503     },
14504     // private
14505     findRecord : function(prop, value){
14506         var record;
14507         if(this.store.getCount() > 0){
14508             this.store.each(function(r){
14509                 if(r.data[prop] == value){
14510                     record = r;
14511                     return false;
14512                 }
14513                 return true;
14514             });
14515         }
14516         return record;
14517     },
14518     
14519     getName: function()
14520     {
14521         // returns hidden if it's set..
14522         if (!this.rendered) {return ''};
14523         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14524         
14525     },
14526     // private
14527     onViewMove : function(e, t){
14528         this.inKeyMode = false;
14529     },
14530
14531     // private
14532     onViewOver : function(e, t){
14533         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14534             return;
14535         }
14536         var item = this.view.findItemFromChild(t);
14537         
14538         if(item){
14539             var index = this.view.indexOf(item);
14540             this.select(index, false);
14541         }
14542     },
14543
14544     // private
14545     onViewClick : function(view, doFocus, el, e)
14546     {
14547         var index = this.view.getSelectedIndexes()[0];
14548         
14549         var r = this.store.getAt(index);
14550         
14551         if(this.tickable){
14552             
14553             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14554                 return;
14555             }
14556             
14557             var rm = false;
14558             var _this = this;
14559             
14560             Roo.each(this.tickItems, function(v,k){
14561                 
14562                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14563                     Roo.log(v);
14564                     _this.tickItems.splice(k, 1);
14565                     
14566                     if(typeof(e) == 'undefined' && view == false){
14567                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14568                     }
14569                     
14570                     rm = true;
14571                     return;
14572                 }
14573             });
14574             
14575             if(rm){
14576                 return;
14577             }
14578             
14579             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14580                 this.tickItems.push(r.data);
14581             }
14582             
14583             if(typeof(e) == 'undefined' && view == false){
14584                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14585             }
14586                     
14587             return;
14588         }
14589         
14590         if(r){
14591             this.onSelect(r, index);
14592         }
14593         if(doFocus !== false && !this.blockFocus){
14594             this.inputEl().focus();
14595         }
14596     },
14597
14598     // private
14599     restrictHeight : function(){
14600         //this.innerList.dom.style.height = '';
14601         //var inner = this.innerList.dom;
14602         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14603         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14604         //this.list.beginUpdate();
14605         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14606         this.list.alignTo(this.inputEl(), this.listAlign);
14607         this.list.alignTo(this.inputEl(), this.listAlign);
14608         //this.list.endUpdate();
14609     },
14610
14611     // private
14612     onEmptyResults : function(){
14613         
14614         if(this.tickable && this.editable){
14615             this.hasFocus = false;
14616             this.restrictHeight();
14617             return;
14618         }
14619         
14620         this.collapse();
14621     },
14622
14623     /**
14624      * Returns true if the dropdown list is expanded, else false.
14625      */
14626     isExpanded : function(){
14627         return this.list.isVisible();
14628     },
14629
14630     /**
14631      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14632      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14633      * @param {String} value The data value of the item to select
14634      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14635      * selected item if it is not currently in view (defaults to true)
14636      * @return {Boolean} True if the value matched an item in the list, else false
14637      */
14638     selectByValue : function(v, scrollIntoView){
14639         if(v !== undefined && v !== null){
14640             var r = this.findRecord(this.valueField || this.displayField, v);
14641             if(r){
14642                 this.select(this.store.indexOf(r), scrollIntoView);
14643                 return true;
14644             }
14645         }
14646         return false;
14647     },
14648
14649     /**
14650      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14651      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14652      * @param {Number} index The zero-based index of the list item to select
14653      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14654      * selected item if it is not currently in view (defaults to true)
14655      */
14656     select : function(index, scrollIntoView){
14657         this.selectedIndex = index;
14658         this.view.select(index);
14659         if(scrollIntoView !== false){
14660             var el = this.view.getNode(index);
14661             /*
14662              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14663              */
14664             if(el){
14665                 this.list.scrollChildIntoView(el, false);
14666             }
14667         }
14668     },
14669
14670     // private
14671     selectNext : function(){
14672         var ct = this.store.getCount();
14673         if(ct > 0){
14674             if(this.selectedIndex == -1){
14675                 this.select(0);
14676             }else if(this.selectedIndex < ct-1){
14677                 this.select(this.selectedIndex+1);
14678             }
14679         }
14680     },
14681
14682     // private
14683     selectPrev : function(){
14684         var ct = this.store.getCount();
14685         if(ct > 0){
14686             if(this.selectedIndex == -1){
14687                 this.select(0);
14688             }else if(this.selectedIndex != 0){
14689                 this.select(this.selectedIndex-1);
14690             }
14691         }
14692     },
14693
14694     // private
14695     onKeyUp : function(e){
14696         if(this.editable !== false && !e.isSpecialKey()){
14697             this.lastKey = e.getKey();
14698             this.dqTask.delay(this.queryDelay);
14699         }
14700     },
14701
14702     // private
14703     validateBlur : function(){
14704         return !this.list || !this.list.isVisible();   
14705     },
14706
14707     // private
14708     initQuery : function(){
14709         
14710         var v = this.getRawValue();
14711         
14712         if(this.tickable && this.editable){
14713             v = this.tickableInputEl().getValue();
14714         }
14715         
14716         this.doQuery(v);
14717     },
14718
14719     // private
14720     doForce : function(){
14721         if(this.inputEl().dom.value.length > 0){
14722             this.inputEl().dom.value =
14723                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14724              
14725         }
14726     },
14727
14728     /**
14729      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14730      * query allowing the query action to be canceled if needed.
14731      * @param {String} query The SQL query to execute
14732      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14733      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14734      * saved in the current store (defaults to false)
14735      */
14736     doQuery : function(q, forceAll){
14737         
14738         if(q === undefined || q === null){
14739             q = '';
14740         }
14741         var qe = {
14742             query: q,
14743             forceAll: forceAll,
14744             combo: this,
14745             cancel:false
14746         };
14747         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14748             return false;
14749         }
14750         q = qe.query;
14751         
14752         forceAll = qe.forceAll;
14753         if(forceAll === true || (q.length >= this.minChars)){
14754             
14755             this.hasQuery = true;
14756             
14757             if(this.lastQuery != q || this.alwaysQuery){
14758                 this.lastQuery = q;
14759                 if(this.mode == 'local'){
14760                     this.selectedIndex = -1;
14761                     if(forceAll){
14762                         this.store.clearFilter();
14763                     }else{
14764                         
14765                         if(this.specialFilter){
14766                             this.fireEvent('specialfilter', this);
14767                             this.onLoad();
14768                             return;
14769                         }
14770                         
14771                         this.store.filter(this.displayField, q);
14772                     }
14773                     
14774                     this.store.fireEvent("datachanged", this.store);
14775                     
14776                     this.onLoad();
14777                     
14778                     
14779                 }else{
14780                     
14781                     this.store.baseParams[this.queryParam] = q;
14782                     
14783                     var options = {params : this.getParams(q)};
14784                     
14785                     if(this.loadNext){
14786                         options.add = true;
14787                         options.params.start = this.page * this.pageSize;
14788                     }
14789                     
14790                     this.store.load(options);
14791                     
14792                     /*
14793                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14794                      *  we should expand the list on onLoad
14795                      *  so command out it
14796                      */
14797 //                    this.expand();
14798                 }
14799             }else{
14800                 this.selectedIndex = -1;
14801                 this.onLoad();   
14802             }
14803         }
14804         
14805         this.loadNext = false;
14806     },
14807     
14808     // private
14809     getParams : function(q){
14810         var p = {};
14811         //p[this.queryParam] = q;
14812         
14813         if(this.pageSize){
14814             p.start = 0;
14815             p.limit = this.pageSize;
14816         }
14817         return p;
14818     },
14819
14820     /**
14821      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14822      */
14823     collapse : function(){
14824         if(!this.isExpanded()){
14825             return;
14826         }
14827         
14828         this.list.hide();
14829         
14830         this.hasFocus = false;
14831         
14832         if(this.tickable){
14833             this.okBtn.hide();
14834             this.cancelBtn.hide();
14835             this.trigger.show();
14836             
14837             if(this.editable){
14838                 this.tickableInputEl().dom.value = '';
14839                 this.tickableInputEl().blur();
14840             }
14841             
14842         }
14843         
14844         Roo.get(document).un('mousedown', this.collapseIf, this);
14845         Roo.get(document).un('mousewheel', this.collapseIf, this);
14846         if (!this.editable) {
14847             Roo.get(document).un('keydown', this.listKeyPress, this);
14848         }
14849         this.fireEvent('collapse', this);
14850         
14851         this.validate();
14852     },
14853
14854     // private
14855     collapseIf : function(e){
14856         var in_combo  = e.within(this.el);
14857         var in_list =  e.within(this.list);
14858         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14859         
14860         if (in_combo || in_list || is_list) {
14861             //e.stopPropagation();
14862             return;
14863         }
14864         
14865         if(this.tickable){
14866             this.onTickableFooterButtonClick(e, false, false);
14867         }
14868
14869         this.collapse();
14870         
14871     },
14872
14873     /**
14874      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14875      */
14876     expand : function(){
14877        
14878         if(this.isExpanded() || !this.hasFocus){
14879             return;
14880         }
14881         
14882         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14883         this.list.setWidth(lw);
14884         
14885         Roo.log('expand');
14886         
14887         this.list.show();
14888         
14889         this.restrictHeight();
14890         
14891         if(this.tickable){
14892             
14893             this.tickItems = Roo.apply([], this.item);
14894             
14895             this.okBtn.show();
14896             this.cancelBtn.show();
14897             this.trigger.hide();
14898             
14899             if(this.editable){
14900                 this.tickableInputEl().focus();
14901             }
14902             
14903         }
14904         
14905         Roo.get(document).on('mousedown', this.collapseIf, this);
14906         Roo.get(document).on('mousewheel', this.collapseIf, this);
14907         if (!this.editable) {
14908             Roo.get(document).on('keydown', this.listKeyPress, this);
14909         }
14910         
14911         this.fireEvent('expand', this);
14912     },
14913
14914     // private
14915     // Implements the default empty TriggerField.onTriggerClick function
14916     onTriggerClick : function(e)
14917     {
14918         Roo.log('trigger click');
14919         
14920         if(this.disabled || !this.triggerList){
14921             return;
14922         }
14923         
14924         this.page = 0;
14925         this.loadNext = false;
14926         
14927         if(this.isExpanded()){
14928             this.collapse();
14929             if (!this.blockFocus) {
14930                 this.inputEl().focus();
14931             }
14932             
14933         }else {
14934             this.hasFocus = true;
14935             if(this.triggerAction == 'all') {
14936                 this.doQuery(this.allQuery, true);
14937             } else {
14938                 this.doQuery(this.getRawValue());
14939             }
14940             if (!this.blockFocus) {
14941                 this.inputEl().focus();
14942             }
14943         }
14944     },
14945     
14946     onTickableTriggerClick : function(e)
14947     {
14948         if(this.disabled){
14949             return;
14950         }
14951         
14952         this.page = 0;
14953         this.loadNext = false;
14954         this.hasFocus = true;
14955         
14956         if(this.triggerAction == 'all') {
14957             this.doQuery(this.allQuery, true);
14958         } else {
14959             this.doQuery(this.getRawValue());
14960         }
14961     },
14962     
14963     onSearchFieldClick : function(e)
14964     {
14965         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14966             this.onTickableFooterButtonClick(e, false, false);
14967             return;
14968         }
14969         
14970         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14971             return;
14972         }
14973         
14974         this.page = 0;
14975         this.loadNext = false;
14976         this.hasFocus = true;
14977         
14978         if(this.triggerAction == 'all') {
14979             this.doQuery(this.allQuery, true);
14980         } else {
14981             this.doQuery(this.getRawValue());
14982         }
14983     },
14984     
14985     listKeyPress : function(e)
14986     {
14987         //Roo.log('listkeypress');
14988         // scroll to first matching element based on key pres..
14989         if (e.isSpecialKey()) {
14990             return false;
14991         }
14992         var k = String.fromCharCode(e.getKey()).toUpperCase();
14993         //Roo.log(k);
14994         var match  = false;
14995         var csel = this.view.getSelectedNodes();
14996         var cselitem = false;
14997         if (csel.length) {
14998             var ix = this.view.indexOf(csel[0]);
14999             cselitem  = this.store.getAt(ix);
15000             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
15001                 cselitem = false;
15002             }
15003             
15004         }
15005         
15006         this.store.each(function(v) { 
15007             if (cselitem) {
15008                 // start at existing selection.
15009                 if (cselitem.id == v.id) {
15010                     cselitem = false;
15011                 }
15012                 return true;
15013             }
15014                 
15015             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
15016                 match = this.store.indexOf(v);
15017                 return false;
15018             }
15019             return true;
15020         }, this);
15021         
15022         if (match === false) {
15023             return true; // no more action?
15024         }
15025         // scroll to?
15026         this.view.select(match);
15027         var sn = Roo.get(this.view.getSelectedNodes()[0]);
15028         sn.scrollIntoView(sn.dom.parentNode, false);
15029     },
15030     
15031     onViewScroll : function(e, t){
15032         
15033         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){
15034             return;
15035         }
15036         
15037         this.hasQuery = true;
15038         
15039         this.loading = this.list.select('.loading', true).first();
15040         
15041         if(this.loading === null){
15042             this.list.createChild({
15043                 tag: 'div',
15044                 cls: 'loading roo-select2-more-results roo-select2-active',
15045                 html: 'Loading more results...'
15046             });
15047             
15048             this.loading = this.list.select('.loading', true).first();
15049             
15050             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
15051             
15052             this.loading.hide();
15053         }
15054         
15055         this.loading.show();
15056         
15057         var _combo = this;
15058         
15059         this.page++;
15060         this.loadNext = true;
15061         
15062         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
15063         
15064         return;
15065     },
15066     
15067     addItem : function(o)
15068     {   
15069         var dv = ''; // display value
15070         
15071         if (this.displayField) {
15072             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
15073         } else {
15074             // this is an error condition!!!
15075             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
15076         }
15077         
15078         if(!dv.length){
15079             return;
15080         }
15081         
15082         var choice = this.choices.createChild({
15083             tag: 'li',
15084             cls: 'roo-select2-search-choice',
15085             cn: [
15086                 {
15087                     tag: 'div',
15088                     html: dv
15089                 },
15090                 {
15091                     tag: 'a',
15092                     href: '#',
15093                     cls: 'roo-select2-search-choice-close fa fa-times',
15094                     tabindex: '-1'
15095                 }
15096             ]
15097             
15098         }, this.searchField);
15099         
15100         var close = choice.select('a.roo-select2-search-choice-close', true).first();
15101         
15102         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
15103         
15104         this.item.push(o);
15105         
15106         this.lastData = o;
15107         
15108         this.syncValue();
15109         
15110         this.inputEl().dom.value = '';
15111         
15112         this.validate();
15113     },
15114     
15115     onRemoveItem : function(e, _self, o)
15116     {
15117         e.preventDefault();
15118         
15119         this.lastItem = Roo.apply([], this.item);
15120         
15121         var index = this.item.indexOf(o.data) * 1;
15122         
15123         if( index < 0){
15124             Roo.log('not this item?!');
15125             return;
15126         }
15127         
15128         this.item.splice(index, 1);
15129         o.item.remove();
15130         
15131         this.syncValue();
15132         
15133         this.fireEvent('remove', this, e);
15134         
15135         this.validate();
15136         
15137     },
15138     
15139     syncValue : function()
15140     {
15141         if(!this.item.length){
15142             this.clearValue();
15143             return;
15144         }
15145             
15146         var value = [];
15147         var _this = this;
15148         Roo.each(this.item, function(i){
15149             if(_this.valueField){
15150                 value.push(i[_this.valueField]);
15151                 return;
15152             }
15153
15154             value.push(i);
15155         });
15156
15157         this.value = value.join(',');
15158
15159         if(this.hiddenField){
15160             this.hiddenField.dom.value = this.value;
15161         }
15162         
15163         this.store.fireEvent("datachanged", this.store);
15164         
15165         this.validate();
15166     },
15167     
15168     clearItem : function()
15169     {
15170         if(!this.multiple){
15171             return;
15172         }
15173         
15174         this.item = [];
15175         
15176         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15177            c.remove();
15178         });
15179         
15180         this.syncValue();
15181         
15182         this.validate();
15183         
15184         if(this.tickable && !Roo.isTouch){
15185             this.view.refresh();
15186         }
15187     },
15188     
15189     inputEl: function ()
15190     {
15191         if(Roo.isIOS && this.useNativeIOS){
15192             return this.el.select('select.roo-ios-select', true).first();
15193         }
15194         
15195         if(Roo.isTouch && this.mobileTouchView){
15196             return this.el.select('input.form-control',true).first();
15197         }
15198         
15199         if(this.tickable){
15200             return this.searchField;
15201         }
15202         
15203         return this.el.select('input.form-control',true).first();
15204     },
15205     
15206     onTickableFooterButtonClick : function(e, btn, el)
15207     {
15208         e.preventDefault();
15209         
15210         this.lastItem = Roo.apply([], this.item);
15211         
15212         if(btn && btn.name == 'cancel'){
15213             this.tickItems = Roo.apply([], this.item);
15214             this.collapse();
15215             return;
15216         }
15217         
15218         this.clearItem();
15219         
15220         var _this = this;
15221         
15222         Roo.each(this.tickItems, function(o){
15223             _this.addItem(o);
15224         });
15225         
15226         this.collapse();
15227         
15228     },
15229     
15230     validate : function()
15231     {
15232         if(this.getVisibilityEl().hasClass('hidden')){
15233             return true;
15234         }
15235         
15236         var v = this.getRawValue();
15237         
15238         if(this.multiple){
15239             v = this.getValue();
15240         }
15241         
15242         if(this.disabled || this.allowBlank || v.length){
15243             this.markValid();
15244             return true;
15245         }
15246         
15247         this.markInvalid();
15248         return false;
15249     },
15250     
15251     tickableInputEl : function()
15252     {
15253         if(!this.tickable || !this.editable){
15254             return this.inputEl();
15255         }
15256         
15257         return this.inputEl().select('.roo-select2-search-field-input', true).first();
15258     },
15259     
15260     
15261     getAutoCreateTouchView : function()
15262     {
15263         var id = Roo.id();
15264         
15265         var cfg = {
15266             cls: 'form-group' //input-group
15267         };
15268         
15269         var input =  {
15270             tag: 'input',
15271             id : id,
15272             type : this.inputType,
15273             cls : 'form-control x-combo-noedit',
15274             autocomplete: 'new-password',
15275             placeholder : this.placeholder || '',
15276             readonly : true
15277         };
15278         
15279         if (this.name) {
15280             input.name = this.name;
15281         }
15282         
15283         if (this.size) {
15284             input.cls += ' input-' + this.size;
15285         }
15286         
15287         if (this.disabled) {
15288             input.disabled = true;
15289         }
15290         
15291         var inputblock = {
15292             cls : '',
15293             cn : [
15294                 input
15295             ]
15296         };
15297         
15298         if(this.before){
15299             inputblock.cls += ' input-group';
15300             
15301             inputblock.cn.unshift({
15302                 tag :'span',
15303                 cls : 'input-group-addon input-group-prepend input-group-text',
15304                 html : this.before
15305             });
15306         }
15307         
15308         if(this.removable && !this.multiple){
15309             inputblock.cls += ' roo-removable';
15310             
15311             inputblock.cn.push({
15312                 tag: 'button',
15313                 html : 'x',
15314                 cls : 'roo-combo-removable-btn close'
15315             });
15316         }
15317
15318         if(this.hasFeedback && !this.allowBlank){
15319             
15320             inputblock.cls += ' has-feedback';
15321             
15322             inputblock.cn.push({
15323                 tag: 'span',
15324                 cls: 'glyphicon form-control-feedback'
15325             });
15326             
15327         }
15328         
15329         if (this.after) {
15330             
15331             inputblock.cls += (this.before) ? '' : ' input-group';
15332             
15333             inputblock.cn.push({
15334                 tag :'span',
15335                 cls : 'input-group-addon input-group-append input-group-text',
15336                 html : this.after
15337             });
15338         }
15339
15340         
15341         var ibwrap = inputblock;
15342         
15343         if(this.multiple){
15344             ibwrap = {
15345                 tag: 'ul',
15346                 cls: 'roo-select2-choices',
15347                 cn:[
15348                     {
15349                         tag: 'li',
15350                         cls: 'roo-select2-search-field',
15351                         cn: [
15352
15353                             inputblock
15354                         ]
15355                     }
15356                 ]
15357             };
15358         
15359             
15360         }
15361         
15362         var combobox = {
15363             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15364             cn: [
15365                 {
15366                     tag: 'input',
15367                     type : 'hidden',
15368                     cls: 'form-hidden-field'
15369                 },
15370                 ibwrap
15371             ]
15372         };
15373         
15374         if(!this.multiple && this.showToggleBtn){
15375             
15376             var caret = {
15377                         tag: 'span',
15378                         cls: 'caret'
15379             };
15380             
15381             if (this.caret != false) {
15382                 caret = {
15383                      tag: 'i',
15384                      cls: 'fa fa-' + this.caret
15385                 };
15386                 
15387             }
15388             
15389             combobox.cn.push({
15390                 tag :'span',
15391                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15392                 cn : [
15393                     caret,
15394                     {
15395                         tag: 'span',
15396                         cls: 'combobox-clear',
15397                         cn  : [
15398                             {
15399                                 tag : 'i',
15400                                 cls: 'icon-remove'
15401                             }
15402                         ]
15403                     }
15404                 ]
15405
15406             })
15407         }
15408         
15409         if(this.multiple){
15410             combobox.cls += ' roo-select2-container-multi';
15411         }
15412         
15413         var align = this.labelAlign || this.parentLabelAlign();
15414         
15415         if (align ==='left' && this.fieldLabel.length) {
15416
15417             cfg.cn = [
15418                 {
15419                    tag : 'i',
15420                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15421                    tooltip : 'This field is required'
15422                 },
15423                 {
15424                     tag: 'label',
15425                     cls : 'control-label col-form-label',
15426                     html : this.fieldLabel
15427
15428                 },
15429                 {
15430                     cls : '', 
15431                     cn: [
15432                         combobox
15433                     ]
15434                 }
15435             ];
15436             
15437             var labelCfg = cfg.cn[1];
15438             var contentCfg = cfg.cn[2];
15439             
15440
15441             if(this.indicatorpos == 'right'){
15442                 cfg.cn = [
15443                     {
15444                         tag: 'label',
15445                         'for' :  id,
15446                         cls : 'control-label col-form-label',
15447                         cn : [
15448                             {
15449                                 tag : 'span',
15450                                 html : this.fieldLabel
15451                             },
15452                             {
15453                                 tag : 'i',
15454                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15455                                 tooltip : 'This field is required'
15456                             }
15457                         ]
15458                     },
15459                     {
15460                         cls : "",
15461                         cn: [
15462                             combobox
15463                         ]
15464                     }
15465
15466                 ];
15467                 
15468                 labelCfg = cfg.cn[0];
15469                 contentCfg = cfg.cn[1];
15470             }
15471             
15472            
15473             
15474             if(this.labelWidth > 12){
15475                 labelCfg.style = "width: " + this.labelWidth + 'px';
15476             }
15477             
15478             if(this.labelWidth < 13 && this.labelmd == 0){
15479                 this.labelmd = this.labelWidth;
15480             }
15481             
15482             if(this.labellg > 0){
15483                 labelCfg.cls += ' col-lg-' + this.labellg;
15484                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15485             }
15486             
15487             if(this.labelmd > 0){
15488                 labelCfg.cls += ' col-md-' + this.labelmd;
15489                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15490             }
15491             
15492             if(this.labelsm > 0){
15493                 labelCfg.cls += ' col-sm-' + this.labelsm;
15494                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15495             }
15496             
15497             if(this.labelxs > 0){
15498                 labelCfg.cls += ' col-xs-' + this.labelxs;
15499                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15500             }
15501                 
15502                 
15503         } else if ( this.fieldLabel.length) {
15504             cfg.cn = [
15505                 {
15506                    tag : 'i',
15507                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15508                    tooltip : 'This field is required'
15509                 },
15510                 {
15511                     tag: 'label',
15512                     cls : 'control-label',
15513                     html : this.fieldLabel
15514
15515                 },
15516                 {
15517                     cls : '', 
15518                     cn: [
15519                         combobox
15520                     ]
15521                 }
15522             ];
15523             
15524             if(this.indicatorpos == 'right'){
15525                 cfg.cn = [
15526                     {
15527                         tag: 'label',
15528                         cls : 'control-label',
15529                         html : this.fieldLabel,
15530                         cn : [
15531                             {
15532                                tag : 'i',
15533                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15534                                tooltip : 'This field is required'
15535                             }
15536                         ]
15537                     },
15538                     {
15539                         cls : '', 
15540                         cn: [
15541                             combobox
15542                         ]
15543                     }
15544                 ];
15545             }
15546         } else {
15547             cfg.cn = combobox;    
15548         }
15549         
15550         
15551         var settings = this;
15552         
15553         ['xs','sm','md','lg'].map(function(size){
15554             if (settings[size]) {
15555                 cfg.cls += ' col-' + size + '-' + settings[size];
15556             }
15557         });
15558         
15559         return cfg;
15560     },
15561     
15562     initTouchView : function()
15563     {
15564         this.renderTouchView();
15565         
15566         this.touchViewEl.on('scroll', function(){
15567             this.el.dom.scrollTop = 0;
15568         }, this);
15569         
15570         this.originalValue = this.getValue();
15571         
15572         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15573         
15574         this.inputEl().on("click", this.showTouchView, this);
15575         if (this.triggerEl) {
15576             this.triggerEl.on("click", this.showTouchView, this);
15577         }
15578         
15579         
15580         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15581         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15582         
15583         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15584         
15585         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15586         this.store.on('load', this.onTouchViewLoad, this);
15587         this.store.on('loadexception', this.onTouchViewLoadException, this);
15588         
15589         if(this.hiddenName){
15590             
15591             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15592             
15593             this.hiddenField.dom.value =
15594                 this.hiddenValue !== undefined ? this.hiddenValue :
15595                 this.value !== undefined ? this.value : '';
15596         
15597             this.el.dom.removeAttribute('name');
15598             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15599         }
15600         
15601         if(this.multiple){
15602             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15603             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15604         }
15605         
15606         if(this.removable && !this.multiple){
15607             var close = this.closeTriggerEl();
15608             if(close){
15609                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15610                 close.on('click', this.removeBtnClick, this, close);
15611             }
15612         }
15613         /*
15614          * fix the bug in Safari iOS8
15615          */
15616         this.inputEl().on("focus", function(e){
15617             document.activeElement.blur();
15618         }, this);
15619         
15620         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15621         
15622         return;
15623         
15624         
15625     },
15626     
15627     renderTouchView : function()
15628     {
15629         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15630         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15631         
15632         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15633         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15634         
15635         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15636         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15637         this.touchViewBodyEl.setStyle('overflow', 'auto');
15638         
15639         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15640         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15641         
15642         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15643         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15644         
15645     },
15646     
15647     showTouchView : function()
15648     {
15649         if(this.disabled){
15650             return;
15651         }
15652         
15653         this.touchViewHeaderEl.hide();
15654
15655         if(this.modalTitle.length){
15656             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15657             this.touchViewHeaderEl.show();
15658         }
15659
15660         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15661         this.touchViewEl.show();
15662
15663         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15664         
15665         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15666         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15667
15668         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15669
15670         if(this.modalTitle.length){
15671             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15672         }
15673         
15674         this.touchViewBodyEl.setHeight(bodyHeight);
15675
15676         if(this.animate){
15677             var _this = this;
15678             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15679         }else{
15680             this.touchViewEl.addClass('in');
15681         }
15682         
15683         if(this._touchViewMask){
15684             Roo.get(document.body).addClass("x-body-masked");
15685             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15686             this._touchViewMask.setStyle('z-index', 10000);
15687             this._touchViewMask.addClass('show');
15688         }
15689         
15690         this.doTouchViewQuery();
15691         
15692     },
15693     
15694     hideTouchView : function()
15695     {
15696         this.touchViewEl.removeClass('in');
15697
15698         if(this.animate){
15699             var _this = this;
15700             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15701         }else{
15702             this.touchViewEl.setStyle('display', 'none');
15703         }
15704         
15705         if(this._touchViewMask){
15706             this._touchViewMask.removeClass('show');
15707             Roo.get(document.body).removeClass("x-body-masked");
15708         }
15709     },
15710     
15711     setTouchViewValue : function()
15712     {
15713         if(this.multiple){
15714             this.clearItem();
15715         
15716             var _this = this;
15717
15718             Roo.each(this.tickItems, function(o){
15719                 this.addItem(o);
15720             }, this);
15721         }
15722         
15723         this.hideTouchView();
15724     },
15725     
15726     doTouchViewQuery : function()
15727     {
15728         var qe = {
15729             query: '',
15730             forceAll: true,
15731             combo: this,
15732             cancel:false
15733         };
15734         
15735         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15736             return false;
15737         }
15738         
15739         if(!this.alwaysQuery || this.mode == 'local'){
15740             this.onTouchViewLoad();
15741             return;
15742         }
15743         
15744         this.store.load();
15745     },
15746     
15747     onTouchViewBeforeLoad : function(combo,opts)
15748     {
15749         return;
15750     },
15751
15752     // private
15753     onTouchViewLoad : function()
15754     {
15755         if(this.store.getCount() < 1){
15756             this.onTouchViewEmptyResults();
15757             return;
15758         }
15759         
15760         this.clearTouchView();
15761         
15762         var rawValue = this.getRawValue();
15763         
15764         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15765         
15766         this.tickItems = [];
15767         
15768         this.store.data.each(function(d, rowIndex){
15769             var row = this.touchViewListGroup.createChild(template);
15770             
15771             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15772                 row.addClass(d.data.cls);
15773             }
15774             
15775             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15776                 var cfg = {
15777                     data : d.data,
15778                     html : d.data[this.displayField]
15779                 };
15780                 
15781                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15782                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15783                 }
15784             }
15785             row.removeClass('selected');
15786             if(!this.multiple && this.valueField &&
15787                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15788             {
15789                 // radio buttons..
15790                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15791                 row.addClass('selected');
15792             }
15793             
15794             if(this.multiple && this.valueField &&
15795                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15796             {
15797                 
15798                 // checkboxes...
15799                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15800                 this.tickItems.push(d.data);
15801             }
15802             
15803             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15804             
15805         }, this);
15806         
15807         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15808         
15809         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15810
15811         if(this.modalTitle.length){
15812             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15813         }
15814
15815         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15816         
15817         if(this.mobile_restrict_height && listHeight < bodyHeight){
15818             this.touchViewBodyEl.setHeight(listHeight);
15819         }
15820         
15821         var _this = this;
15822         
15823         if(firstChecked && listHeight > bodyHeight){
15824             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15825         }
15826         
15827     },
15828     
15829     onTouchViewLoadException : function()
15830     {
15831         this.hideTouchView();
15832     },
15833     
15834     onTouchViewEmptyResults : function()
15835     {
15836         this.clearTouchView();
15837         
15838         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15839         
15840         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15841         
15842     },
15843     
15844     clearTouchView : function()
15845     {
15846         this.touchViewListGroup.dom.innerHTML = '';
15847     },
15848     
15849     onTouchViewClick : function(e, el, o)
15850     {
15851         e.preventDefault();
15852         
15853         var row = o.row;
15854         var rowIndex = o.rowIndex;
15855         
15856         var r = this.store.getAt(rowIndex);
15857         
15858         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15859             
15860             if(!this.multiple){
15861                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15862                     c.dom.removeAttribute('checked');
15863                 }, this);
15864
15865                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15866
15867                 this.setFromData(r.data);
15868
15869                 var close = this.closeTriggerEl();
15870
15871                 if(close){
15872                     close.show();
15873                 }
15874
15875                 this.hideTouchView();
15876
15877                 this.fireEvent('select', this, r, rowIndex);
15878
15879                 return;
15880             }
15881
15882             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15883                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15884                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15885                 return;
15886             }
15887
15888             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15889             this.addItem(r.data);
15890             this.tickItems.push(r.data);
15891         }
15892     },
15893     
15894     getAutoCreateNativeIOS : function()
15895     {
15896         var cfg = {
15897             cls: 'form-group' //input-group,
15898         };
15899         
15900         var combobox =  {
15901             tag: 'select',
15902             cls : 'roo-ios-select'
15903         };
15904         
15905         if (this.name) {
15906             combobox.name = this.name;
15907         }
15908         
15909         if (this.disabled) {
15910             combobox.disabled = true;
15911         }
15912         
15913         var settings = this;
15914         
15915         ['xs','sm','md','lg'].map(function(size){
15916             if (settings[size]) {
15917                 cfg.cls += ' col-' + size + '-' + settings[size];
15918             }
15919         });
15920         
15921         cfg.cn = combobox;
15922         
15923         return cfg;
15924         
15925     },
15926     
15927     initIOSView : function()
15928     {
15929         this.store.on('load', this.onIOSViewLoad, this);
15930         
15931         return;
15932     },
15933     
15934     onIOSViewLoad : function()
15935     {
15936         if(this.store.getCount() < 1){
15937             return;
15938         }
15939         
15940         this.clearIOSView();
15941         
15942         if(this.allowBlank) {
15943             
15944             var default_text = '-- SELECT --';
15945             
15946             if(this.placeholder.length){
15947                 default_text = this.placeholder;
15948             }
15949             
15950             if(this.emptyTitle.length){
15951                 default_text += ' - ' + this.emptyTitle + ' -';
15952             }
15953             
15954             var opt = this.inputEl().createChild({
15955                 tag: 'option',
15956                 value : 0,
15957                 html : default_text
15958             });
15959             
15960             var o = {};
15961             o[this.valueField] = 0;
15962             o[this.displayField] = default_text;
15963             
15964             this.ios_options.push({
15965                 data : o,
15966                 el : opt
15967             });
15968             
15969         }
15970         
15971         this.store.data.each(function(d, rowIndex){
15972             
15973             var html = '';
15974             
15975             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15976                 html = d.data[this.displayField];
15977             }
15978             
15979             var value = '';
15980             
15981             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15982                 value = d.data[this.valueField];
15983             }
15984             
15985             var option = {
15986                 tag: 'option',
15987                 value : value,
15988                 html : html
15989             };
15990             
15991             if(this.value == d.data[this.valueField]){
15992                 option['selected'] = true;
15993             }
15994             
15995             var opt = this.inputEl().createChild(option);
15996             
15997             this.ios_options.push({
15998                 data : d.data,
15999                 el : opt
16000             });
16001             
16002         }, this);
16003         
16004         this.inputEl().on('change', function(){
16005            this.fireEvent('select', this);
16006         }, this);
16007         
16008     },
16009     
16010     clearIOSView: function()
16011     {
16012         this.inputEl().dom.innerHTML = '';
16013         
16014         this.ios_options = [];
16015     },
16016     
16017     setIOSValue: function(v)
16018     {
16019         this.value = v;
16020         
16021         if(!this.ios_options){
16022             return;
16023         }
16024         
16025         Roo.each(this.ios_options, function(opts){
16026            
16027            opts.el.dom.removeAttribute('selected');
16028            
16029            if(opts.data[this.valueField] != v){
16030                return;
16031            }
16032            
16033            opts.el.dom.setAttribute('selected', true);
16034            
16035         }, this);
16036     }
16037
16038     /** 
16039     * @cfg {Boolean} grow 
16040     * @hide 
16041     */
16042     /** 
16043     * @cfg {Number} growMin 
16044     * @hide 
16045     */
16046     /** 
16047     * @cfg {Number} growMax 
16048     * @hide 
16049     */
16050     /**
16051      * @hide
16052      * @method autoSize
16053      */
16054 });
16055
16056 Roo.apply(Roo.bootstrap.ComboBox,  {
16057     
16058     header : {
16059         tag: 'div',
16060         cls: 'modal-header',
16061         cn: [
16062             {
16063                 tag: 'h4',
16064                 cls: 'modal-title'
16065             }
16066         ]
16067     },
16068     
16069     body : {
16070         tag: 'div',
16071         cls: 'modal-body',
16072         cn: [
16073             {
16074                 tag: 'ul',
16075                 cls: 'list-group'
16076             }
16077         ]
16078     },
16079     
16080     listItemRadio : {
16081         tag: 'li',
16082         cls: 'list-group-item',
16083         cn: [
16084             {
16085                 tag: 'span',
16086                 cls: 'roo-combobox-list-group-item-value'
16087             },
16088             {
16089                 tag: 'div',
16090                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
16091                 cn: [
16092                     {
16093                         tag: 'input',
16094                         type: 'radio'
16095                     },
16096                     {
16097                         tag: 'label'
16098                     }
16099                 ]
16100             }
16101         ]
16102     },
16103     
16104     listItemCheckbox : {
16105         tag: 'li',
16106         cls: 'list-group-item',
16107         cn: [
16108             {
16109                 tag: 'span',
16110                 cls: 'roo-combobox-list-group-item-value'
16111             },
16112             {
16113                 tag: 'div',
16114                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
16115                 cn: [
16116                     {
16117                         tag: 'input',
16118                         type: 'checkbox'
16119                     },
16120                     {
16121                         tag: 'label'
16122                     }
16123                 ]
16124             }
16125         ]
16126     },
16127     
16128     emptyResult : {
16129         tag: 'div',
16130         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
16131     },
16132     
16133     footer : {
16134         tag: 'div',
16135         cls: 'modal-footer',
16136         cn: [
16137             {
16138                 tag: 'div',
16139                 cls: 'row',
16140                 cn: [
16141                     {
16142                         tag: 'div',
16143                         cls: 'col-xs-6 text-left',
16144                         cn: {
16145                             tag: 'button',
16146                             cls: 'btn btn-danger roo-touch-view-cancel',
16147                             html: 'Cancel'
16148                         }
16149                     },
16150                     {
16151                         tag: 'div',
16152                         cls: 'col-xs-6 text-right',
16153                         cn: {
16154                             tag: 'button',
16155                             cls: 'btn btn-success roo-touch-view-ok',
16156                             html: 'OK'
16157                         }
16158                     }
16159                 ]
16160             }
16161         ]
16162         
16163     }
16164 });
16165
16166 Roo.apply(Roo.bootstrap.ComboBox,  {
16167     
16168     touchViewTemplate : {
16169         tag: 'div',
16170         cls: 'modal fade roo-combobox-touch-view',
16171         cn: [
16172             {
16173                 tag: 'div',
16174                 cls: 'modal-dialog',
16175                 style : 'position:fixed', // we have to fix position....
16176                 cn: [
16177                     {
16178                         tag: 'div',
16179                         cls: 'modal-content',
16180                         cn: [
16181                             Roo.bootstrap.ComboBox.header,
16182                             Roo.bootstrap.ComboBox.body,
16183                             Roo.bootstrap.ComboBox.footer
16184                         ]
16185                     }
16186                 ]
16187             }
16188         ]
16189     }
16190 });/*
16191  * Based on:
16192  * Ext JS Library 1.1.1
16193  * Copyright(c) 2006-2007, Ext JS, LLC.
16194  *
16195  * Originally Released Under LGPL - original licence link has changed is not relivant.
16196  *
16197  * Fork - LGPL
16198  * <script type="text/javascript">
16199  */
16200
16201 /**
16202  * @class Roo.View
16203  * @extends Roo.util.Observable
16204  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
16205  * This class also supports single and multi selection modes. <br>
16206  * Create a data model bound view:
16207  <pre><code>
16208  var store = new Roo.data.Store(...);
16209
16210  var view = new Roo.View({
16211     el : "my-element",
16212     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
16213  
16214     singleSelect: true,
16215     selectedClass: "ydataview-selected",
16216     store: store
16217  });
16218
16219  // listen for node click?
16220  view.on("click", function(vw, index, node, e){
16221  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16222  });
16223
16224  // load XML data
16225  dataModel.load("foobar.xml");
16226  </code></pre>
16227  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16228  * <br><br>
16229  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16230  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16231  * 
16232  * Note: old style constructor is still suported (container, template, config)
16233  * 
16234  * @constructor
16235  * Create a new View
16236  * @param {Object} config The config object
16237  * 
16238  */
16239 Roo.View = function(config, depreciated_tpl, depreciated_config){
16240     
16241     this.parent = false;
16242     
16243     if (typeof(depreciated_tpl) == 'undefined') {
16244         // new way.. - universal constructor.
16245         Roo.apply(this, config);
16246         this.el  = Roo.get(this.el);
16247     } else {
16248         // old format..
16249         this.el  = Roo.get(config);
16250         this.tpl = depreciated_tpl;
16251         Roo.apply(this, depreciated_config);
16252     }
16253     this.wrapEl  = this.el.wrap().wrap();
16254     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16255     
16256     
16257     if(typeof(this.tpl) == "string"){
16258         this.tpl = new Roo.Template(this.tpl);
16259     } else {
16260         // support xtype ctors..
16261         this.tpl = new Roo.factory(this.tpl, Roo);
16262     }
16263     
16264     
16265     this.tpl.compile();
16266     
16267     /** @private */
16268     this.addEvents({
16269         /**
16270          * @event beforeclick
16271          * Fires before a click is processed. Returns false to cancel the default action.
16272          * @param {Roo.View} this
16273          * @param {Number} index The index of the target node
16274          * @param {HTMLElement} node The target node
16275          * @param {Roo.EventObject} e The raw event object
16276          */
16277             "beforeclick" : true,
16278         /**
16279          * @event click
16280          * Fires when a template node is clicked.
16281          * @param {Roo.View} this
16282          * @param {Number} index The index of the target node
16283          * @param {HTMLElement} node The target node
16284          * @param {Roo.EventObject} e The raw event object
16285          */
16286             "click" : true,
16287         /**
16288          * @event dblclick
16289          * Fires when a template node is double clicked.
16290          * @param {Roo.View} this
16291          * @param {Number} index The index of the target node
16292          * @param {HTMLElement} node The target node
16293          * @param {Roo.EventObject} e The raw event object
16294          */
16295             "dblclick" : true,
16296         /**
16297          * @event contextmenu
16298          * Fires when a template node is right clicked.
16299          * @param {Roo.View} this
16300          * @param {Number} index The index of the target node
16301          * @param {HTMLElement} node The target node
16302          * @param {Roo.EventObject} e The raw event object
16303          */
16304             "contextmenu" : true,
16305         /**
16306          * @event selectionchange
16307          * Fires when the selected nodes change.
16308          * @param {Roo.View} this
16309          * @param {Array} selections Array of the selected nodes
16310          */
16311             "selectionchange" : true,
16312     
16313         /**
16314          * @event beforeselect
16315          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16316          * @param {Roo.View} this
16317          * @param {HTMLElement} node The node to be selected
16318          * @param {Array} selections Array of currently selected nodes
16319          */
16320             "beforeselect" : true,
16321         /**
16322          * @event preparedata
16323          * Fires on every row to render, to allow you to change the data.
16324          * @param {Roo.View} this
16325          * @param {Object} data to be rendered (change this)
16326          */
16327           "preparedata" : true
16328           
16329           
16330         });
16331
16332
16333
16334     this.el.on({
16335         "click": this.onClick,
16336         "dblclick": this.onDblClick,
16337         "contextmenu": this.onContextMenu,
16338         scope:this
16339     });
16340
16341     this.selections = [];
16342     this.nodes = [];
16343     this.cmp = new Roo.CompositeElementLite([]);
16344     if(this.store){
16345         this.store = Roo.factory(this.store, Roo.data);
16346         this.setStore(this.store, true);
16347     }
16348     
16349     if ( this.footer && this.footer.xtype) {
16350            
16351          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16352         
16353         this.footer.dataSource = this.store;
16354         this.footer.container = fctr;
16355         this.footer = Roo.factory(this.footer, Roo);
16356         fctr.insertFirst(this.el);
16357         
16358         // this is a bit insane - as the paging toolbar seems to detach the el..
16359 //        dom.parentNode.parentNode.parentNode
16360          // they get detached?
16361     }
16362     
16363     
16364     Roo.View.superclass.constructor.call(this);
16365     
16366     
16367 };
16368
16369 Roo.extend(Roo.View, Roo.util.Observable, {
16370     
16371      /**
16372      * @cfg {Roo.data.Store} store Data store to load data from.
16373      */
16374     store : false,
16375     
16376     /**
16377      * @cfg {String|Roo.Element} el The container element.
16378      */
16379     el : '',
16380     
16381     /**
16382      * @cfg {String|Roo.Template} tpl The template used by this View 
16383      */
16384     tpl : false,
16385     /**
16386      * @cfg {String} dataName the named area of the template to use as the data area
16387      *                          Works with domtemplates roo-name="name"
16388      */
16389     dataName: false,
16390     /**
16391      * @cfg {String} selectedClass The css class to add to selected nodes
16392      */
16393     selectedClass : "x-view-selected",
16394      /**
16395      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16396      */
16397     emptyText : "",
16398     
16399     /**
16400      * @cfg {String} text to display on mask (default Loading)
16401      */
16402     mask : false,
16403     /**
16404      * @cfg {Boolean} multiSelect Allow multiple selection
16405      */
16406     multiSelect : false,
16407     /**
16408      * @cfg {Boolean} singleSelect Allow single selection
16409      */
16410     singleSelect:  false,
16411     
16412     /**
16413      * @cfg {Boolean} toggleSelect - selecting 
16414      */
16415     toggleSelect : false,
16416     
16417     /**
16418      * @cfg {Boolean} tickable - selecting 
16419      */
16420     tickable : false,
16421     
16422     /**
16423      * Returns the element this view is bound to.
16424      * @return {Roo.Element}
16425      */
16426     getEl : function(){
16427         return this.wrapEl;
16428     },
16429     
16430     
16431
16432     /**
16433      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16434      */
16435     refresh : function(){
16436         //Roo.log('refresh');
16437         var t = this.tpl;
16438         
16439         // if we are using something like 'domtemplate', then
16440         // the what gets used is:
16441         // t.applySubtemplate(NAME, data, wrapping data..)
16442         // the outer template then get' applied with
16443         //     the store 'extra data'
16444         // and the body get's added to the
16445         //      roo-name="data" node?
16446         //      <span class='roo-tpl-{name}'></span> ?????
16447         
16448         
16449         
16450         this.clearSelections();
16451         this.el.update("");
16452         var html = [];
16453         var records = this.store.getRange();
16454         if(records.length < 1) {
16455             
16456             // is this valid??  = should it render a template??
16457             
16458             this.el.update(this.emptyText);
16459             return;
16460         }
16461         var el = this.el;
16462         if (this.dataName) {
16463             this.el.update(t.apply(this.store.meta)); //????
16464             el = this.el.child('.roo-tpl-' + this.dataName);
16465         }
16466         
16467         for(var i = 0, len = records.length; i < len; i++){
16468             var data = this.prepareData(records[i].data, i, records[i]);
16469             this.fireEvent("preparedata", this, data, i, records[i]);
16470             
16471             var d = Roo.apply({}, data);
16472             
16473             if(this.tickable){
16474                 Roo.apply(d, {'roo-id' : Roo.id()});
16475                 
16476                 var _this = this;
16477             
16478                 Roo.each(this.parent.item, function(item){
16479                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16480                         return;
16481                     }
16482                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16483                 });
16484             }
16485             
16486             html[html.length] = Roo.util.Format.trim(
16487                 this.dataName ?
16488                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16489                     t.apply(d)
16490             );
16491         }
16492         
16493         
16494         
16495         el.update(html.join(""));
16496         this.nodes = el.dom.childNodes;
16497         this.updateIndexes(0);
16498     },
16499     
16500
16501     /**
16502      * Function to override to reformat the data that is sent to
16503      * the template for each node.
16504      * DEPRICATED - use the preparedata event handler.
16505      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16506      * a JSON object for an UpdateManager bound view).
16507      */
16508     prepareData : function(data, index, record)
16509     {
16510         this.fireEvent("preparedata", this, data, index, record);
16511         return data;
16512     },
16513
16514     onUpdate : function(ds, record){
16515         // Roo.log('on update');   
16516         this.clearSelections();
16517         var index = this.store.indexOf(record);
16518         var n = this.nodes[index];
16519         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16520         n.parentNode.removeChild(n);
16521         this.updateIndexes(index, index);
16522     },
16523
16524     
16525     
16526 // --------- FIXME     
16527     onAdd : function(ds, records, index)
16528     {
16529         //Roo.log(['on Add', ds, records, index] );        
16530         this.clearSelections();
16531         if(this.nodes.length == 0){
16532             this.refresh();
16533             return;
16534         }
16535         var n = this.nodes[index];
16536         for(var i = 0, len = records.length; i < len; i++){
16537             var d = this.prepareData(records[i].data, i, records[i]);
16538             if(n){
16539                 this.tpl.insertBefore(n, d);
16540             }else{
16541                 
16542                 this.tpl.append(this.el, d);
16543             }
16544         }
16545         this.updateIndexes(index);
16546     },
16547
16548     onRemove : function(ds, record, index){
16549        // Roo.log('onRemove');
16550         this.clearSelections();
16551         var el = this.dataName  ?
16552             this.el.child('.roo-tpl-' + this.dataName) :
16553             this.el; 
16554         
16555         el.dom.removeChild(this.nodes[index]);
16556         this.updateIndexes(index);
16557     },
16558
16559     /**
16560      * Refresh an individual node.
16561      * @param {Number} index
16562      */
16563     refreshNode : function(index){
16564         this.onUpdate(this.store, this.store.getAt(index));
16565     },
16566
16567     updateIndexes : function(startIndex, endIndex){
16568         var ns = this.nodes;
16569         startIndex = startIndex || 0;
16570         endIndex = endIndex || ns.length - 1;
16571         for(var i = startIndex; i <= endIndex; i++){
16572             ns[i].nodeIndex = i;
16573         }
16574     },
16575
16576     /**
16577      * Changes the data store this view uses and refresh the view.
16578      * @param {Store} store
16579      */
16580     setStore : function(store, initial){
16581         if(!initial && this.store){
16582             this.store.un("datachanged", this.refresh);
16583             this.store.un("add", this.onAdd);
16584             this.store.un("remove", this.onRemove);
16585             this.store.un("update", this.onUpdate);
16586             this.store.un("clear", this.refresh);
16587             this.store.un("beforeload", this.onBeforeLoad);
16588             this.store.un("load", this.onLoad);
16589             this.store.un("loadexception", this.onLoad);
16590         }
16591         if(store){
16592           
16593             store.on("datachanged", this.refresh, this);
16594             store.on("add", this.onAdd, this);
16595             store.on("remove", this.onRemove, this);
16596             store.on("update", this.onUpdate, this);
16597             store.on("clear", this.refresh, this);
16598             store.on("beforeload", this.onBeforeLoad, this);
16599             store.on("load", this.onLoad, this);
16600             store.on("loadexception", this.onLoad, this);
16601         }
16602         
16603         if(store){
16604             this.refresh();
16605         }
16606     },
16607     /**
16608      * onbeforeLoad - masks the loading area.
16609      *
16610      */
16611     onBeforeLoad : function(store,opts)
16612     {
16613          //Roo.log('onBeforeLoad');   
16614         if (!opts.add) {
16615             this.el.update("");
16616         }
16617         this.el.mask(this.mask ? this.mask : "Loading" ); 
16618     },
16619     onLoad : function ()
16620     {
16621         this.el.unmask();
16622     },
16623     
16624
16625     /**
16626      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16627      * @param {HTMLElement} node
16628      * @return {HTMLElement} The template node
16629      */
16630     findItemFromChild : function(node){
16631         var el = this.dataName  ?
16632             this.el.child('.roo-tpl-' + this.dataName,true) :
16633             this.el.dom; 
16634         
16635         if(!node || node.parentNode == el){
16636                     return node;
16637             }
16638             var p = node.parentNode;
16639             while(p && p != el){
16640             if(p.parentNode == el){
16641                 return p;
16642             }
16643             p = p.parentNode;
16644         }
16645             return null;
16646     },
16647
16648     /** @ignore */
16649     onClick : function(e){
16650         var item = this.findItemFromChild(e.getTarget());
16651         if(item){
16652             var index = this.indexOf(item);
16653             if(this.onItemClick(item, index, e) !== false){
16654                 this.fireEvent("click", this, index, item, e);
16655             }
16656         }else{
16657             this.clearSelections();
16658         }
16659     },
16660
16661     /** @ignore */
16662     onContextMenu : function(e){
16663         var item = this.findItemFromChild(e.getTarget());
16664         if(item){
16665             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16666         }
16667     },
16668
16669     /** @ignore */
16670     onDblClick : function(e){
16671         var item = this.findItemFromChild(e.getTarget());
16672         if(item){
16673             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16674         }
16675     },
16676
16677     onItemClick : function(item, index, e)
16678     {
16679         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16680             return false;
16681         }
16682         if (this.toggleSelect) {
16683             var m = this.isSelected(item) ? 'unselect' : 'select';
16684             //Roo.log(m);
16685             var _t = this;
16686             _t[m](item, true, false);
16687             return true;
16688         }
16689         if(this.multiSelect || this.singleSelect){
16690             if(this.multiSelect && e.shiftKey && this.lastSelection){
16691                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16692             }else{
16693                 this.select(item, this.multiSelect && e.ctrlKey);
16694                 this.lastSelection = item;
16695             }
16696             
16697             if(!this.tickable){
16698                 e.preventDefault();
16699             }
16700             
16701         }
16702         return true;
16703     },
16704
16705     /**
16706      * Get the number of selected nodes.
16707      * @return {Number}
16708      */
16709     getSelectionCount : function(){
16710         return this.selections.length;
16711     },
16712
16713     /**
16714      * Get the currently selected nodes.
16715      * @return {Array} An array of HTMLElements
16716      */
16717     getSelectedNodes : function(){
16718         return this.selections;
16719     },
16720
16721     /**
16722      * Get the indexes of the selected nodes.
16723      * @return {Array}
16724      */
16725     getSelectedIndexes : function(){
16726         var indexes = [], s = this.selections;
16727         for(var i = 0, len = s.length; i < len; i++){
16728             indexes.push(s[i].nodeIndex);
16729         }
16730         return indexes;
16731     },
16732
16733     /**
16734      * Clear all selections
16735      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16736      */
16737     clearSelections : function(suppressEvent){
16738         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16739             this.cmp.elements = this.selections;
16740             this.cmp.removeClass(this.selectedClass);
16741             this.selections = [];
16742             if(!suppressEvent){
16743                 this.fireEvent("selectionchange", this, this.selections);
16744             }
16745         }
16746     },
16747
16748     /**
16749      * Returns true if the passed node is selected
16750      * @param {HTMLElement/Number} node The node or node index
16751      * @return {Boolean}
16752      */
16753     isSelected : function(node){
16754         var s = this.selections;
16755         if(s.length < 1){
16756             return false;
16757         }
16758         node = this.getNode(node);
16759         return s.indexOf(node) !== -1;
16760     },
16761
16762     /**
16763      * Selects nodes.
16764      * @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
16765      * @param {Boolean} keepExisting (optional) true to keep existing selections
16766      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16767      */
16768     select : function(nodeInfo, keepExisting, suppressEvent){
16769         if(nodeInfo instanceof Array){
16770             if(!keepExisting){
16771                 this.clearSelections(true);
16772             }
16773             for(var i = 0, len = nodeInfo.length; i < len; i++){
16774                 this.select(nodeInfo[i], true, true);
16775             }
16776             return;
16777         } 
16778         var node = this.getNode(nodeInfo);
16779         if(!node || this.isSelected(node)){
16780             return; // already selected.
16781         }
16782         if(!keepExisting){
16783             this.clearSelections(true);
16784         }
16785         
16786         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16787             Roo.fly(node).addClass(this.selectedClass);
16788             this.selections.push(node);
16789             if(!suppressEvent){
16790                 this.fireEvent("selectionchange", this, this.selections);
16791             }
16792         }
16793         
16794         
16795     },
16796       /**
16797      * Unselects nodes.
16798      * @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
16799      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16800      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16801      */
16802     unselect : function(nodeInfo, keepExisting, suppressEvent)
16803     {
16804         if(nodeInfo instanceof Array){
16805             Roo.each(this.selections, function(s) {
16806                 this.unselect(s, nodeInfo);
16807             }, this);
16808             return;
16809         }
16810         var node = this.getNode(nodeInfo);
16811         if(!node || !this.isSelected(node)){
16812             //Roo.log("not selected");
16813             return; // not selected.
16814         }
16815         // fireevent???
16816         var ns = [];
16817         Roo.each(this.selections, function(s) {
16818             if (s == node ) {
16819                 Roo.fly(node).removeClass(this.selectedClass);
16820
16821                 return;
16822             }
16823             ns.push(s);
16824         },this);
16825         
16826         this.selections= ns;
16827         this.fireEvent("selectionchange", this, this.selections);
16828     },
16829
16830     /**
16831      * Gets a template node.
16832      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16833      * @return {HTMLElement} The node or null if it wasn't found
16834      */
16835     getNode : function(nodeInfo){
16836         if(typeof nodeInfo == "string"){
16837             return document.getElementById(nodeInfo);
16838         }else if(typeof nodeInfo == "number"){
16839             return this.nodes[nodeInfo];
16840         }
16841         return nodeInfo;
16842     },
16843
16844     /**
16845      * Gets a range template nodes.
16846      * @param {Number} startIndex
16847      * @param {Number} endIndex
16848      * @return {Array} An array of nodes
16849      */
16850     getNodes : function(start, end){
16851         var ns = this.nodes;
16852         start = start || 0;
16853         end = typeof end == "undefined" ? ns.length - 1 : end;
16854         var nodes = [];
16855         if(start <= end){
16856             for(var i = start; i <= end; i++){
16857                 nodes.push(ns[i]);
16858             }
16859         } else{
16860             for(var i = start; i >= end; i--){
16861                 nodes.push(ns[i]);
16862             }
16863         }
16864         return nodes;
16865     },
16866
16867     /**
16868      * Finds the index of the passed node
16869      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16870      * @return {Number} The index of the node or -1
16871      */
16872     indexOf : function(node){
16873         node = this.getNode(node);
16874         if(typeof node.nodeIndex == "number"){
16875             return node.nodeIndex;
16876         }
16877         var ns = this.nodes;
16878         for(var i = 0, len = ns.length; i < len; i++){
16879             if(ns[i] == node){
16880                 return i;
16881             }
16882         }
16883         return -1;
16884     }
16885 });
16886 /*
16887  * - LGPL
16888  *
16889  * based on jquery fullcalendar
16890  * 
16891  */
16892
16893 Roo.bootstrap = Roo.bootstrap || {};
16894 /**
16895  * @class Roo.bootstrap.Calendar
16896  * @extends Roo.bootstrap.Component
16897  * Bootstrap Calendar class
16898  * @cfg {Boolean} loadMask (true|false) default false
16899  * @cfg {Object} header generate the user specific header of the calendar, default false
16900
16901  * @constructor
16902  * Create a new Container
16903  * @param {Object} config The config object
16904  */
16905
16906
16907
16908 Roo.bootstrap.Calendar = function(config){
16909     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16910      this.addEvents({
16911         /**
16912              * @event select
16913              * Fires when a date is selected
16914              * @param {DatePicker} this
16915              * @param {Date} date The selected date
16916              */
16917         'select': true,
16918         /**
16919              * @event monthchange
16920              * Fires when the displayed month changes 
16921              * @param {DatePicker} this
16922              * @param {Date} date The selected month
16923              */
16924         'monthchange': true,
16925         /**
16926              * @event evententer
16927              * Fires when mouse over an event
16928              * @param {Calendar} this
16929              * @param {event} Event
16930              */
16931         'evententer': true,
16932         /**
16933              * @event eventleave
16934              * Fires when the mouse leaves an
16935              * @param {Calendar} this
16936              * @param {event}
16937              */
16938         'eventleave': true,
16939         /**
16940              * @event eventclick
16941              * Fires when the mouse click an
16942              * @param {Calendar} this
16943              * @param {event}
16944              */
16945         'eventclick': true
16946         
16947     });
16948
16949 };
16950
16951 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16952     
16953      /**
16954      * @cfg {Number} startDay
16955      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16956      */
16957     startDay : 0,
16958     
16959     loadMask : false,
16960     
16961     header : false,
16962       
16963     getAutoCreate : function(){
16964         
16965         
16966         var fc_button = function(name, corner, style, content ) {
16967             return Roo.apply({},{
16968                 tag : 'span',
16969                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16970                          (corner.length ?
16971                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16972                             ''
16973                         ),
16974                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16975                 unselectable: 'on'
16976             });
16977         };
16978         
16979         var header = {};
16980         
16981         if(!this.header){
16982             header = {
16983                 tag : 'table',
16984                 cls : 'fc-header',
16985                 style : 'width:100%',
16986                 cn : [
16987                     {
16988                         tag: 'tr',
16989                         cn : [
16990                             {
16991                                 tag : 'td',
16992                                 cls : 'fc-header-left',
16993                                 cn : [
16994                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16995                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16996                                     { tag: 'span', cls: 'fc-header-space' },
16997                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16998
16999
17000                                 ]
17001                             },
17002
17003                             {
17004                                 tag : 'td',
17005                                 cls : 'fc-header-center',
17006                                 cn : [
17007                                     {
17008                                         tag: 'span',
17009                                         cls: 'fc-header-title',
17010                                         cn : {
17011                                             tag: 'H2',
17012                                             html : 'month / year'
17013                                         }
17014                                     }
17015
17016                                 ]
17017                             },
17018                             {
17019                                 tag : 'td',
17020                                 cls : 'fc-header-right',
17021                                 cn : [
17022                               /*      fc_button('month', 'left', '', 'month' ),
17023                                     fc_button('week', '', '', 'week' ),
17024                                     fc_button('day', 'right', '', 'day' )
17025                                 */    
17026
17027                                 ]
17028                             }
17029
17030                         ]
17031                     }
17032                 ]
17033             };
17034         }
17035         
17036         header = this.header;
17037         
17038        
17039         var cal_heads = function() {
17040             var ret = [];
17041             // fixme - handle this.
17042             
17043             for (var i =0; i < Date.dayNames.length; i++) {
17044                 var d = Date.dayNames[i];
17045                 ret.push({
17046                     tag: 'th',
17047                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
17048                     html : d.substring(0,3)
17049                 });
17050                 
17051             }
17052             ret[0].cls += ' fc-first';
17053             ret[6].cls += ' fc-last';
17054             return ret;
17055         };
17056         var cal_cell = function(n) {
17057             return  {
17058                 tag: 'td',
17059                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
17060                 cn : [
17061                     {
17062                         cn : [
17063                             {
17064                                 cls: 'fc-day-number',
17065                                 html: 'D'
17066                             },
17067                             {
17068                                 cls: 'fc-day-content',
17069                              
17070                                 cn : [
17071                                      {
17072                                         style: 'position: relative;' // height: 17px;
17073                                     }
17074                                 ]
17075                             }
17076                             
17077                             
17078                         ]
17079                     }
17080                 ]
17081                 
17082             }
17083         };
17084         var cal_rows = function() {
17085             
17086             var ret = [];
17087             for (var r = 0; r < 6; r++) {
17088                 var row= {
17089                     tag : 'tr',
17090                     cls : 'fc-week',
17091                     cn : []
17092                 };
17093                 
17094                 for (var i =0; i < Date.dayNames.length; i++) {
17095                     var d = Date.dayNames[i];
17096                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
17097
17098                 }
17099                 row.cn[0].cls+=' fc-first';
17100                 row.cn[0].cn[0].style = 'min-height:90px';
17101                 row.cn[6].cls+=' fc-last';
17102                 ret.push(row);
17103                 
17104             }
17105             ret[0].cls += ' fc-first';
17106             ret[4].cls += ' fc-prev-last';
17107             ret[5].cls += ' fc-last';
17108             return ret;
17109             
17110         };
17111         
17112         var cal_table = {
17113             tag: 'table',
17114             cls: 'fc-border-separate',
17115             style : 'width:100%',
17116             cellspacing  : 0,
17117             cn : [
17118                 { 
17119                     tag: 'thead',
17120                     cn : [
17121                         { 
17122                             tag: 'tr',
17123                             cls : 'fc-first fc-last',
17124                             cn : cal_heads()
17125                         }
17126                     ]
17127                 },
17128                 { 
17129                     tag: 'tbody',
17130                     cn : cal_rows()
17131                 }
17132                   
17133             ]
17134         };
17135          
17136          var cfg = {
17137             cls : 'fc fc-ltr',
17138             cn : [
17139                 header,
17140                 {
17141                     cls : 'fc-content',
17142                     style : "position: relative;",
17143                     cn : [
17144                         {
17145                             cls : 'fc-view fc-view-month fc-grid',
17146                             style : 'position: relative',
17147                             unselectable : 'on',
17148                             cn : [
17149                                 {
17150                                     cls : 'fc-event-container',
17151                                     style : 'position:absolute;z-index:8;top:0;left:0;'
17152                                 },
17153                                 cal_table
17154                             ]
17155                         }
17156                     ]
17157     
17158                 }
17159            ] 
17160             
17161         };
17162         
17163          
17164         
17165         return cfg;
17166     },
17167     
17168     
17169     initEvents : function()
17170     {
17171         if(!this.store){
17172             throw "can not find store for calendar";
17173         }
17174         
17175         var mark = {
17176             tag: "div",
17177             cls:"x-dlg-mask",
17178             style: "text-align:center",
17179             cn: [
17180                 {
17181                     tag: "div",
17182                     style: "background-color:white;width:50%;margin:250 auto",
17183                     cn: [
17184                         {
17185                             tag: "img",
17186                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
17187                         },
17188                         {
17189                             tag: "span",
17190                             html: "Loading"
17191                         }
17192                         
17193                     ]
17194                 }
17195             ]
17196         };
17197         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17198         
17199         var size = this.el.select('.fc-content', true).first().getSize();
17200         this.maskEl.setSize(size.width, size.height);
17201         this.maskEl.enableDisplayMode("block");
17202         if(!this.loadMask){
17203             this.maskEl.hide();
17204         }
17205         
17206         this.store = Roo.factory(this.store, Roo.data);
17207         this.store.on('load', this.onLoad, this);
17208         this.store.on('beforeload', this.onBeforeLoad, this);
17209         
17210         this.resize();
17211         
17212         this.cells = this.el.select('.fc-day',true);
17213         //Roo.log(this.cells);
17214         this.textNodes = this.el.query('.fc-day-number');
17215         this.cells.addClassOnOver('fc-state-hover');
17216         
17217         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17218         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17219         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17220         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17221         
17222         this.on('monthchange', this.onMonthChange, this);
17223         
17224         this.update(new Date().clearTime());
17225     },
17226     
17227     resize : function() {
17228         var sz  = this.el.getSize();
17229         
17230         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17231         this.el.select('.fc-day-content div',true).setHeight(34);
17232     },
17233     
17234     
17235     // private
17236     showPrevMonth : function(e){
17237         this.update(this.activeDate.add("mo", -1));
17238     },
17239     showToday : function(e){
17240         this.update(new Date().clearTime());
17241     },
17242     // private
17243     showNextMonth : function(e){
17244         this.update(this.activeDate.add("mo", 1));
17245     },
17246
17247     // private
17248     showPrevYear : function(){
17249         this.update(this.activeDate.add("y", -1));
17250     },
17251
17252     // private
17253     showNextYear : function(){
17254         this.update(this.activeDate.add("y", 1));
17255     },
17256
17257     
17258    // private
17259     update : function(date)
17260     {
17261         var vd = this.activeDate;
17262         this.activeDate = date;
17263 //        if(vd && this.el){
17264 //            var t = date.getTime();
17265 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17266 //                Roo.log('using add remove');
17267 //                
17268 //                this.fireEvent('monthchange', this, date);
17269 //                
17270 //                this.cells.removeClass("fc-state-highlight");
17271 //                this.cells.each(function(c){
17272 //                   if(c.dateValue == t){
17273 //                       c.addClass("fc-state-highlight");
17274 //                       setTimeout(function(){
17275 //                            try{c.dom.firstChild.focus();}catch(e){}
17276 //                       }, 50);
17277 //                       return false;
17278 //                   }
17279 //                   return true;
17280 //                });
17281 //                return;
17282 //            }
17283 //        }
17284         
17285         var days = date.getDaysInMonth();
17286         
17287         var firstOfMonth = date.getFirstDateOfMonth();
17288         var startingPos = firstOfMonth.getDay()-this.startDay;
17289         
17290         if(startingPos < this.startDay){
17291             startingPos += 7;
17292         }
17293         
17294         var pm = date.add(Date.MONTH, -1);
17295         var prevStart = pm.getDaysInMonth()-startingPos;
17296 //        
17297         this.cells = this.el.select('.fc-day',true);
17298         this.textNodes = this.el.query('.fc-day-number');
17299         this.cells.addClassOnOver('fc-state-hover');
17300         
17301         var cells = this.cells.elements;
17302         var textEls = this.textNodes;
17303         
17304         Roo.each(cells, function(cell){
17305             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17306         });
17307         
17308         days += startingPos;
17309
17310         // convert everything to numbers so it's fast
17311         var day = 86400000;
17312         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17313         //Roo.log(d);
17314         //Roo.log(pm);
17315         //Roo.log(prevStart);
17316         
17317         var today = new Date().clearTime().getTime();
17318         var sel = date.clearTime().getTime();
17319         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17320         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17321         var ddMatch = this.disabledDatesRE;
17322         var ddText = this.disabledDatesText;
17323         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17324         var ddaysText = this.disabledDaysText;
17325         var format = this.format;
17326         
17327         var setCellClass = function(cal, cell){
17328             cell.row = 0;
17329             cell.events = [];
17330             cell.more = [];
17331             //Roo.log('set Cell Class');
17332             cell.title = "";
17333             var t = d.getTime();
17334             
17335             //Roo.log(d);
17336             
17337             cell.dateValue = t;
17338             if(t == today){
17339                 cell.className += " fc-today";
17340                 cell.className += " fc-state-highlight";
17341                 cell.title = cal.todayText;
17342             }
17343             if(t == sel){
17344                 // disable highlight in other month..
17345                 //cell.className += " fc-state-highlight";
17346                 
17347             }
17348             // disabling
17349             if(t < min) {
17350                 cell.className = " fc-state-disabled";
17351                 cell.title = cal.minText;
17352                 return;
17353             }
17354             if(t > max) {
17355                 cell.className = " fc-state-disabled";
17356                 cell.title = cal.maxText;
17357                 return;
17358             }
17359             if(ddays){
17360                 if(ddays.indexOf(d.getDay()) != -1){
17361                     cell.title = ddaysText;
17362                     cell.className = " fc-state-disabled";
17363                 }
17364             }
17365             if(ddMatch && format){
17366                 var fvalue = d.dateFormat(format);
17367                 if(ddMatch.test(fvalue)){
17368                     cell.title = ddText.replace("%0", fvalue);
17369                     cell.className = " fc-state-disabled";
17370                 }
17371             }
17372             
17373             if (!cell.initialClassName) {
17374                 cell.initialClassName = cell.dom.className;
17375             }
17376             
17377             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17378         };
17379
17380         var i = 0;
17381         
17382         for(; i < startingPos; i++) {
17383             textEls[i].innerHTML = (++prevStart);
17384             d.setDate(d.getDate()+1);
17385             
17386             cells[i].className = "fc-past fc-other-month";
17387             setCellClass(this, cells[i]);
17388         }
17389         
17390         var intDay = 0;
17391         
17392         for(; i < days; i++){
17393             intDay = i - startingPos + 1;
17394             textEls[i].innerHTML = (intDay);
17395             d.setDate(d.getDate()+1);
17396             
17397             cells[i].className = ''; // "x-date-active";
17398             setCellClass(this, cells[i]);
17399         }
17400         var extraDays = 0;
17401         
17402         for(; i < 42; i++) {
17403             textEls[i].innerHTML = (++extraDays);
17404             d.setDate(d.getDate()+1);
17405             
17406             cells[i].className = "fc-future fc-other-month";
17407             setCellClass(this, cells[i]);
17408         }
17409         
17410         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17411         
17412         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17413         
17414         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17415         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17416         
17417         if(totalRows != 6){
17418             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17419             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17420         }
17421         
17422         this.fireEvent('monthchange', this, date);
17423         
17424         
17425         /*
17426         if(!this.internalRender){
17427             var main = this.el.dom.firstChild;
17428             var w = main.offsetWidth;
17429             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17430             Roo.fly(main).setWidth(w);
17431             this.internalRender = true;
17432             // opera does not respect the auto grow header center column
17433             // then, after it gets a width opera refuses to recalculate
17434             // without a second pass
17435             if(Roo.isOpera && !this.secondPass){
17436                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17437                 this.secondPass = true;
17438                 this.update.defer(10, this, [date]);
17439             }
17440         }
17441         */
17442         
17443     },
17444     
17445     findCell : function(dt) {
17446         dt = dt.clearTime().getTime();
17447         var ret = false;
17448         this.cells.each(function(c){
17449             //Roo.log("check " +c.dateValue + '?=' + dt);
17450             if(c.dateValue == dt){
17451                 ret = c;
17452                 return false;
17453             }
17454             return true;
17455         });
17456         
17457         return ret;
17458     },
17459     
17460     findCells : function(ev) {
17461         var s = ev.start.clone().clearTime().getTime();
17462        // Roo.log(s);
17463         var e= ev.end.clone().clearTime().getTime();
17464        // Roo.log(e);
17465         var ret = [];
17466         this.cells.each(function(c){
17467              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17468             
17469             if(c.dateValue > e){
17470                 return ;
17471             }
17472             if(c.dateValue < s){
17473                 return ;
17474             }
17475             ret.push(c);
17476         });
17477         
17478         return ret;    
17479     },
17480     
17481 //    findBestRow: function(cells)
17482 //    {
17483 //        var ret = 0;
17484 //        
17485 //        for (var i =0 ; i < cells.length;i++) {
17486 //            ret  = Math.max(cells[i].rows || 0,ret);
17487 //        }
17488 //        return ret;
17489 //        
17490 //    },
17491     
17492     
17493     addItem : function(ev)
17494     {
17495         // look for vertical location slot in
17496         var cells = this.findCells(ev);
17497         
17498 //        ev.row = this.findBestRow(cells);
17499         
17500         // work out the location.
17501         
17502         var crow = false;
17503         var rows = [];
17504         for(var i =0; i < cells.length; i++) {
17505             
17506             cells[i].row = cells[0].row;
17507             
17508             if(i == 0){
17509                 cells[i].row = cells[i].row + 1;
17510             }
17511             
17512             if (!crow) {
17513                 crow = {
17514                     start : cells[i],
17515                     end :  cells[i]
17516                 };
17517                 continue;
17518             }
17519             if (crow.start.getY() == cells[i].getY()) {
17520                 // on same row.
17521                 crow.end = cells[i];
17522                 continue;
17523             }
17524             // different row.
17525             rows.push(crow);
17526             crow = {
17527                 start: cells[i],
17528                 end : cells[i]
17529             };
17530             
17531         }
17532         
17533         rows.push(crow);
17534         ev.els = [];
17535         ev.rows = rows;
17536         ev.cells = cells;
17537         
17538         cells[0].events.push(ev);
17539         
17540         this.calevents.push(ev);
17541     },
17542     
17543     clearEvents: function() {
17544         
17545         if(!this.calevents){
17546             return;
17547         }
17548         
17549         Roo.each(this.cells.elements, function(c){
17550             c.row = 0;
17551             c.events = [];
17552             c.more = [];
17553         });
17554         
17555         Roo.each(this.calevents, function(e) {
17556             Roo.each(e.els, function(el) {
17557                 el.un('mouseenter' ,this.onEventEnter, this);
17558                 el.un('mouseleave' ,this.onEventLeave, this);
17559                 el.remove();
17560             },this);
17561         },this);
17562         
17563         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17564             e.remove();
17565         });
17566         
17567     },
17568     
17569     renderEvents: function()
17570     {   
17571         var _this = this;
17572         
17573         this.cells.each(function(c) {
17574             
17575             if(c.row < 5){
17576                 return;
17577             }
17578             
17579             var ev = c.events;
17580             
17581             var r = 4;
17582             if(c.row != c.events.length){
17583                 r = 4 - (4 - (c.row - c.events.length));
17584             }
17585             
17586             c.events = ev.slice(0, r);
17587             c.more = ev.slice(r);
17588             
17589             if(c.more.length && c.more.length == 1){
17590                 c.events.push(c.more.pop());
17591             }
17592             
17593             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17594             
17595         });
17596             
17597         this.cells.each(function(c) {
17598             
17599             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17600             
17601             
17602             for (var e = 0; e < c.events.length; e++){
17603                 var ev = c.events[e];
17604                 var rows = ev.rows;
17605                 
17606                 for(var i = 0; i < rows.length; i++) {
17607                 
17608                     // how many rows should it span..
17609
17610                     var  cfg = {
17611                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17612                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17613
17614                         unselectable : "on",
17615                         cn : [
17616                             {
17617                                 cls: 'fc-event-inner',
17618                                 cn : [
17619     //                                {
17620     //                                  tag:'span',
17621     //                                  cls: 'fc-event-time',
17622     //                                  html : cells.length > 1 ? '' : ev.time
17623     //                                },
17624                                     {
17625                                       tag:'span',
17626                                       cls: 'fc-event-title',
17627                                       html : String.format('{0}', ev.title)
17628                                     }
17629
17630
17631                                 ]
17632                             },
17633                             {
17634                                 cls: 'ui-resizable-handle ui-resizable-e',
17635                                 html : '&nbsp;&nbsp;&nbsp'
17636                             }
17637
17638                         ]
17639                     };
17640
17641                     if (i == 0) {
17642                         cfg.cls += ' fc-event-start';
17643                     }
17644                     if ((i+1) == rows.length) {
17645                         cfg.cls += ' fc-event-end';
17646                     }
17647
17648                     var ctr = _this.el.select('.fc-event-container',true).first();
17649                     var cg = ctr.createChild(cfg);
17650
17651                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17652                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17653
17654                     var r = (c.more.length) ? 1 : 0;
17655                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17656                     cg.setWidth(ebox.right - sbox.x -2);
17657
17658                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17659                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17660                     cg.on('click', _this.onEventClick, _this, ev);
17661
17662                     ev.els.push(cg);
17663                     
17664                 }
17665                 
17666             }
17667             
17668             
17669             if(c.more.length){
17670                 var  cfg = {
17671                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17672                     style : 'position: absolute',
17673                     unselectable : "on",
17674                     cn : [
17675                         {
17676                             cls: 'fc-event-inner',
17677                             cn : [
17678                                 {
17679                                   tag:'span',
17680                                   cls: 'fc-event-title',
17681                                   html : 'More'
17682                                 }
17683
17684
17685                             ]
17686                         },
17687                         {
17688                             cls: 'ui-resizable-handle ui-resizable-e',
17689                             html : '&nbsp;&nbsp;&nbsp'
17690                         }
17691
17692                     ]
17693                 };
17694
17695                 var ctr = _this.el.select('.fc-event-container',true).first();
17696                 var cg = ctr.createChild(cfg);
17697
17698                 var sbox = c.select('.fc-day-content',true).first().getBox();
17699                 var ebox = c.select('.fc-day-content',true).first().getBox();
17700                 //Roo.log(cg);
17701                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17702                 cg.setWidth(ebox.right - sbox.x -2);
17703
17704                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17705                 
17706             }
17707             
17708         });
17709         
17710         
17711         
17712     },
17713     
17714     onEventEnter: function (e, el,event,d) {
17715         this.fireEvent('evententer', this, el, event);
17716     },
17717     
17718     onEventLeave: function (e, el,event,d) {
17719         this.fireEvent('eventleave', this, el, event);
17720     },
17721     
17722     onEventClick: function (e, el,event,d) {
17723         this.fireEvent('eventclick', this, el, event);
17724     },
17725     
17726     onMonthChange: function () {
17727         this.store.load();
17728     },
17729     
17730     onMoreEventClick: function(e, el, more)
17731     {
17732         var _this = this;
17733         
17734         this.calpopover.placement = 'right';
17735         this.calpopover.setTitle('More');
17736         
17737         this.calpopover.setContent('');
17738         
17739         var ctr = this.calpopover.el.select('.popover-content', true).first();
17740         
17741         Roo.each(more, function(m){
17742             var cfg = {
17743                 cls : 'fc-event-hori fc-event-draggable',
17744                 html : m.title
17745             };
17746             var cg = ctr.createChild(cfg);
17747             
17748             cg.on('click', _this.onEventClick, _this, m);
17749         });
17750         
17751         this.calpopover.show(el);
17752         
17753         
17754     },
17755     
17756     onLoad: function () 
17757     {   
17758         this.calevents = [];
17759         var cal = this;
17760         
17761         if(this.store.getCount() > 0){
17762             this.store.data.each(function(d){
17763                cal.addItem({
17764                     id : d.data.id,
17765                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17766                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17767                     time : d.data.start_time,
17768                     title : d.data.title,
17769                     description : d.data.description,
17770                     venue : d.data.venue
17771                 });
17772             });
17773         }
17774         
17775         this.renderEvents();
17776         
17777         if(this.calevents.length && this.loadMask){
17778             this.maskEl.hide();
17779         }
17780     },
17781     
17782     onBeforeLoad: function()
17783     {
17784         this.clearEvents();
17785         if(this.loadMask){
17786             this.maskEl.show();
17787         }
17788     }
17789 });
17790
17791  
17792  /*
17793  * - LGPL
17794  *
17795  * element
17796  * 
17797  */
17798
17799 /**
17800  * @class Roo.bootstrap.Popover
17801  * @extends Roo.bootstrap.Component
17802  * Bootstrap Popover class
17803  * @cfg {String} html contents of the popover   (or false to use children..)
17804  * @cfg {String} title of popover (or false to hide)
17805  * @cfg {String} placement how it is placed
17806  * @cfg {String} trigger click || hover (or false to trigger manually)
17807  * @cfg {String} over what (parent or false to trigger manually.)
17808  * @cfg {Number} delay - delay before showing
17809  
17810  * @constructor
17811  * Create a new Popover
17812  * @param {Object} config The config object
17813  */
17814
17815 Roo.bootstrap.Popover = function(config){
17816     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17817     
17818     this.addEvents({
17819         // raw events
17820          /**
17821          * @event show
17822          * After the popover show
17823          * 
17824          * @param {Roo.bootstrap.Popover} this
17825          */
17826         "show" : true,
17827         /**
17828          * @event hide
17829          * After the popover hide
17830          * 
17831          * @param {Roo.bootstrap.Popover} this
17832          */
17833         "hide" : true
17834     });
17835 };
17836
17837 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17838     
17839     title: 'Fill in a title',
17840     html: false,
17841     
17842     placement : 'right',
17843     trigger : 'hover', // hover
17844     
17845     delay : 0,
17846     
17847     over: 'parent',
17848     
17849     can_build_overlaid : false,
17850     
17851     getChildContainer : function()
17852     {
17853         return this.el.select('.popover-content',true).first();
17854     },
17855     
17856     getAutoCreate : function(){
17857          
17858         var cfg = {
17859            cls : 'popover roo-dynamic',
17860            style: 'display:block',
17861            cn : [
17862                 {
17863                     cls : 'arrow'
17864                 },
17865                 {
17866                     cls : 'popover-inner',
17867                     cn : [
17868                         {
17869                             tag: 'h3',
17870                             cls: 'popover-title popover-header',
17871                             html : this.title
17872                         },
17873                         {
17874                             cls : 'popover-content popover-body',
17875                             html : this.html
17876                         }
17877                     ]
17878                     
17879                 }
17880            ]
17881         };
17882         
17883         return cfg;
17884     },
17885     setTitle: function(str)
17886     {
17887         this.title = str;
17888         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17889     },
17890     setContent: function(str)
17891     {
17892         this.html = str;
17893         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17894     },
17895     // as it get's added to the bottom of the page.
17896     onRender : function(ct, position)
17897     {
17898         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17899         if(!this.el){
17900             var cfg = Roo.apply({},  this.getAutoCreate());
17901             cfg.id = Roo.id();
17902             
17903             if (this.cls) {
17904                 cfg.cls += ' ' + this.cls;
17905             }
17906             if (this.style) {
17907                 cfg.style = this.style;
17908             }
17909             //Roo.log("adding to ");
17910             this.el = Roo.get(document.body).createChild(cfg, position);
17911 //            Roo.log(this.el);
17912         }
17913         this.initEvents();
17914     },
17915     
17916     initEvents : function()
17917     {
17918         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17919         this.el.enableDisplayMode('block');
17920         this.el.hide();
17921         if (this.over === false) {
17922             return; 
17923         }
17924         if (this.triggers === false) {
17925             return;
17926         }
17927         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17928         var triggers = this.trigger ? this.trigger.split(' ') : [];
17929         Roo.each(triggers, function(trigger) {
17930         
17931             if (trigger == 'click') {
17932                 on_el.on('click', this.toggle, this);
17933             } else if (trigger != 'manual') {
17934                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17935                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17936       
17937                 on_el.on(eventIn  ,this.enter, this);
17938                 on_el.on(eventOut, this.leave, this);
17939             }
17940         }, this);
17941         
17942     },
17943     
17944     
17945     // private
17946     timeout : null,
17947     hoverState : null,
17948     
17949     toggle : function () {
17950         this.hoverState == 'in' ? this.leave() : this.enter();
17951     },
17952     
17953     enter : function () {
17954         
17955         clearTimeout(this.timeout);
17956     
17957         this.hoverState = 'in';
17958     
17959         if (!this.delay || !this.delay.show) {
17960             this.show();
17961             return;
17962         }
17963         var _t = this;
17964         this.timeout = setTimeout(function () {
17965             if (_t.hoverState == 'in') {
17966                 _t.show();
17967             }
17968         }, this.delay.show)
17969     },
17970     
17971     leave : function() {
17972         clearTimeout(this.timeout);
17973     
17974         this.hoverState = 'out';
17975     
17976         if (!this.delay || !this.delay.hide) {
17977             this.hide();
17978             return;
17979         }
17980         var _t = this;
17981         this.timeout = setTimeout(function () {
17982             if (_t.hoverState == 'out') {
17983                 _t.hide();
17984             }
17985         }, this.delay.hide)
17986     },
17987     
17988     show : function (on_el)
17989     {
17990         if (!on_el) {
17991             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17992         }
17993         
17994         // set content.
17995         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17996         if (this.html !== false) {
17997             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17998         }
17999         this.el.removeClass([
18000             'fade','top','bottom', 'left', 'right','in',
18001             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
18002         ]);
18003         if (!this.title.length) {
18004             this.el.select('.popover-title',true).hide();
18005         }
18006         
18007         var placement = typeof this.placement == 'function' ?
18008             this.placement.call(this, this.el, on_el) :
18009             this.placement;
18010             
18011         var autoToken = /\s?auto?\s?/i;
18012         var autoPlace = autoToken.test(placement);
18013         if (autoPlace) {
18014             placement = placement.replace(autoToken, '') || 'top';
18015         }
18016         
18017         //this.el.detach()
18018         //this.el.setXY([0,0]);
18019         this.el.show();
18020         this.el.dom.style.display='block';
18021         this.el.addClass(placement);
18022         
18023         //this.el.appendTo(on_el);
18024         
18025         var p = this.getPosition();
18026         var box = this.el.getBox();
18027         
18028         if (autoPlace) {
18029             // fixme..
18030         }
18031         var align = Roo.bootstrap.Popover.alignment[placement];
18032         
18033 //        Roo.log(align);
18034         this.el.alignTo(on_el, align[0],align[1]);
18035         //var arrow = this.el.select('.arrow',true).first();
18036         //arrow.set(align[2], 
18037         
18038         this.el.addClass('in');
18039         
18040         
18041         if (this.el.hasClass('fade')) {
18042             // fade it?
18043         }
18044         
18045         this.hoverState = 'in';
18046         
18047         this.fireEvent('show', this);
18048         
18049     },
18050     hide : function()
18051     {
18052         this.el.setXY([0,0]);
18053         this.el.removeClass('in');
18054         this.el.hide();
18055         this.hoverState = null;
18056         
18057         this.fireEvent('hide', this);
18058     }
18059     
18060 });
18061
18062 Roo.bootstrap.Popover.alignment = {
18063     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
18064     'right' : ['l-r', [10,0], 'left bs-popover-left'],
18065     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
18066     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
18067 };
18068
18069  /*
18070  * - LGPL
18071  *
18072  * Progress
18073  * 
18074  */
18075
18076 /**
18077  * @class Roo.bootstrap.Progress
18078  * @extends Roo.bootstrap.Component
18079  * Bootstrap Progress class
18080  * @cfg {Boolean} striped striped of the progress bar
18081  * @cfg {Boolean} active animated of the progress bar
18082  * 
18083  * 
18084  * @constructor
18085  * Create a new Progress
18086  * @param {Object} config The config object
18087  */
18088
18089 Roo.bootstrap.Progress = function(config){
18090     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
18091 };
18092
18093 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
18094     
18095     striped : false,
18096     active: false,
18097     
18098     getAutoCreate : function(){
18099         var cfg = {
18100             tag: 'div',
18101             cls: 'progress'
18102         };
18103         
18104         
18105         if(this.striped){
18106             cfg.cls += ' progress-striped';
18107         }
18108       
18109         if(this.active){
18110             cfg.cls += ' active';
18111         }
18112         
18113         
18114         return cfg;
18115     }
18116    
18117 });
18118
18119  
18120
18121  /*
18122  * - LGPL
18123  *
18124  * ProgressBar
18125  * 
18126  */
18127
18128 /**
18129  * @class Roo.bootstrap.ProgressBar
18130  * @extends Roo.bootstrap.Component
18131  * Bootstrap ProgressBar class
18132  * @cfg {Number} aria_valuenow aria-value now
18133  * @cfg {Number} aria_valuemin aria-value min
18134  * @cfg {Number} aria_valuemax aria-value max
18135  * @cfg {String} label label for the progress bar
18136  * @cfg {String} panel (success | info | warning | danger )
18137  * @cfg {String} role role of the progress bar
18138  * @cfg {String} sr_only text
18139  * 
18140  * 
18141  * @constructor
18142  * Create a new ProgressBar
18143  * @param {Object} config The config object
18144  */
18145
18146 Roo.bootstrap.ProgressBar = function(config){
18147     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
18148 };
18149
18150 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
18151     
18152     aria_valuenow : 0,
18153     aria_valuemin : 0,
18154     aria_valuemax : 100,
18155     label : false,
18156     panel : false,
18157     role : false,
18158     sr_only: false,
18159     
18160     getAutoCreate : function()
18161     {
18162         
18163         var cfg = {
18164             tag: 'div',
18165             cls: 'progress-bar',
18166             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18167         };
18168         
18169         if(this.sr_only){
18170             cfg.cn = {
18171                 tag: 'span',
18172                 cls: 'sr-only',
18173                 html: this.sr_only
18174             }
18175         }
18176         
18177         if(this.role){
18178             cfg.role = this.role;
18179         }
18180         
18181         if(this.aria_valuenow){
18182             cfg['aria-valuenow'] = this.aria_valuenow;
18183         }
18184         
18185         if(this.aria_valuemin){
18186             cfg['aria-valuemin'] = this.aria_valuemin;
18187         }
18188         
18189         if(this.aria_valuemax){
18190             cfg['aria-valuemax'] = this.aria_valuemax;
18191         }
18192         
18193         if(this.label && !this.sr_only){
18194             cfg.html = this.label;
18195         }
18196         
18197         if(this.panel){
18198             cfg.cls += ' progress-bar-' + this.panel;
18199         }
18200         
18201         return cfg;
18202     },
18203     
18204     update : function(aria_valuenow)
18205     {
18206         this.aria_valuenow = aria_valuenow;
18207         
18208         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18209     }
18210    
18211 });
18212
18213  
18214
18215  /*
18216  * - LGPL
18217  *
18218  * column
18219  * 
18220  */
18221
18222 /**
18223  * @class Roo.bootstrap.TabGroup
18224  * @extends Roo.bootstrap.Column
18225  * Bootstrap Column class
18226  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18227  * @cfg {Boolean} carousel true to make the group behave like a carousel
18228  * @cfg {Boolean} bullets show bullets for the panels
18229  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18230  * @cfg {Number} timer auto slide timer .. default 0 millisecond
18231  * @cfg {Boolean} showarrow (true|false) show arrow default true
18232  * 
18233  * @constructor
18234  * Create a new TabGroup
18235  * @param {Object} config The config object
18236  */
18237
18238 Roo.bootstrap.TabGroup = function(config){
18239     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18240     if (!this.navId) {
18241         this.navId = Roo.id();
18242     }
18243     this.tabs = [];
18244     Roo.bootstrap.TabGroup.register(this);
18245     
18246 };
18247
18248 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
18249     
18250     carousel : false,
18251     transition : false,
18252     bullets : 0,
18253     timer : 0,
18254     autoslide : false,
18255     slideFn : false,
18256     slideOnTouch : false,
18257     showarrow : true,
18258     
18259     getAutoCreate : function()
18260     {
18261         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18262         
18263         cfg.cls += ' tab-content';
18264         
18265         if (this.carousel) {
18266             cfg.cls += ' carousel slide';
18267             
18268             cfg.cn = [{
18269                cls : 'carousel-inner',
18270                cn : []
18271             }];
18272         
18273             if(this.bullets  && !Roo.isTouch){
18274                 
18275                 var bullets = {
18276                     cls : 'carousel-bullets',
18277                     cn : []
18278                 };
18279                
18280                 if(this.bullets_cls){
18281                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18282                 }
18283                 
18284                 bullets.cn.push({
18285                     cls : 'clear'
18286                 });
18287                 
18288                 cfg.cn[0].cn.push(bullets);
18289             }
18290             
18291             if(this.showarrow){
18292                 cfg.cn[0].cn.push({
18293                     tag : 'div',
18294                     class : 'carousel-arrow',
18295                     cn : [
18296                         {
18297                             tag : 'div',
18298                             class : 'carousel-prev',
18299                             cn : [
18300                                 {
18301                                     tag : 'i',
18302                                     class : 'fa fa-chevron-left'
18303                                 }
18304                             ]
18305                         },
18306                         {
18307                             tag : 'div',
18308                             class : 'carousel-next',
18309                             cn : [
18310                                 {
18311                                     tag : 'i',
18312                                     class : 'fa fa-chevron-right'
18313                                 }
18314                             ]
18315                         }
18316                     ]
18317                 });
18318             }
18319             
18320         }
18321         
18322         return cfg;
18323     },
18324     
18325     initEvents:  function()
18326     {
18327 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18328 //            this.el.on("touchstart", this.onTouchStart, this);
18329 //        }
18330         
18331         if(this.autoslide){
18332             var _this = this;
18333             
18334             this.slideFn = window.setInterval(function() {
18335                 _this.showPanelNext();
18336             }, this.timer);
18337         }
18338         
18339         if(this.showarrow){
18340             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18341             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18342         }
18343         
18344         
18345     },
18346     
18347 //    onTouchStart : function(e, el, o)
18348 //    {
18349 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18350 //            return;
18351 //        }
18352 //        
18353 //        this.showPanelNext();
18354 //    },
18355     
18356     
18357     getChildContainer : function()
18358     {
18359         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18360     },
18361     
18362     /**
18363     * register a Navigation item
18364     * @param {Roo.bootstrap.NavItem} the navitem to add
18365     */
18366     register : function(item)
18367     {
18368         this.tabs.push( item);
18369         item.navId = this.navId; // not really needed..
18370         this.addBullet();
18371     
18372     },
18373     
18374     getActivePanel : function()
18375     {
18376         var r = false;
18377         Roo.each(this.tabs, function(t) {
18378             if (t.active) {
18379                 r = t;
18380                 return false;
18381             }
18382             return null;
18383         });
18384         return r;
18385         
18386     },
18387     getPanelByName : function(n)
18388     {
18389         var r = false;
18390         Roo.each(this.tabs, function(t) {
18391             if (t.tabId == n) {
18392                 r = t;
18393                 return false;
18394             }
18395             return null;
18396         });
18397         return r;
18398     },
18399     indexOfPanel : function(p)
18400     {
18401         var r = false;
18402         Roo.each(this.tabs, function(t,i) {
18403             if (t.tabId == p.tabId) {
18404                 r = i;
18405                 return false;
18406             }
18407             return null;
18408         });
18409         return r;
18410     },
18411     /**
18412      * show a specific panel
18413      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18414      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18415      */
18416     showPanel : function (pan)
18417     {
18418         if(this.transition || typeof(pan) == 'undefined'){
18419             Roo.log("waiting for the transitionend");
18420             return false;
18421         }
18422         
18423         if (typeof(pan) == 'number') {
18424             pan = this.tabs[pan];
18425         }
18426         
18427         if (typeof(pan) == 'string') {
18428             pan = this.getPanelByName(pan);
18429         }
18430         
18431         var cur = this.getActivePanel();
18432         
18433         if(!pan || !cur){
18434             Roo.log('pan or acitve pan is undefined');
18435             return false;
18436         }
18437         
18438         if (pan.tabId == this.getActivePanel().tabId) {
18439             return true;
18440         }
18441         
18442         if (false === cur.fireEvent('beforedeactivate')) {
18443             return false;
18444         }
18445         
18446         if(this.bullets > 0 && !Roo.isTouch){
18447             this.setActiveBullet(this.indexOfPanel(pan));
18448         }
18449         
18450         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18451             
18452             //class="carousel-item carousel-item-next carousel-item-left"
18453             
18454             this.transition = true;
18455             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18456             var lr = dir == 'next' ? 'left' : 'right';
18457             pan.el.addClass(dir); // or prev
18458             pan.el.addClass('carousel-item-' + dir); // or prev
18459             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18460             cur.el.addClass(lr); // or right
18461             pan.el.addClass(lr);
18462             cur.el.addClass('carousel-item-' +lr); // or right
18463             pan.el.addClass('carousel-item-' +lr);
18464             
18465             
18466             var _this = this;
18467             cur.el.on('transitionend', function() {
18468                 Roo.log("trans end?");
18469                 
18470                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
18471                 pan.setActive(true);
18472                 
18473                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
18474                 cur.setActive(false);
18475                 
18476                 _this.transition = false;
18477                 
18478             }, this, { single:  true } );
18479             
18480             return true;
18481         }
18482         
18483         cur.setActive(false);
18484         pan.setActive(true);
18485         
18486         return true;
18487         
18488     },
18489     showPanelNext : function()
18490     {
18491         var i = this.indexOfPanel(this.getActivePanel());
18492         
18493         if (i >= this.tabs.length - 1 && !this.autoslide) {
18494             return;
18495         }
18496         
18497         if (i >= this.tabs.length - 1 && this.autoslide) {
18498             i = -1;
18499         }
18500         
18501         this.showPanel(this.tabs[i+1]);
18502     },
18503     
18504     showPanelPrev : function()
18505     {
18506         var i = this.indexOfPanel(this.getActivePanel());
18507         
18508         if (i  < 1 && !this.autoslide) {
18509             return;
18510         }
18511         
18512         if (i < 1 && this.autoslide) {
18513             i = this.tabs.length;
18514         }
18515         
18516         this.showPanel(this.tabs[i-1]);
18517     },
18518     
18519     
18520     addBullet: function()
18521     {
18522         if(!this.bullets || Roo.isTouch){
18523             return;
18524         }
18525         var ctr = this.el.select('.carousel-bullets',true).first();
18526         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18527         var bullet = ctr.createChild({
18528             cls : 'bullet bullet-' + i
18529         },ctr.dom.lastChild);
18530         
18531         
18532         var _this = this;
18533         
18534         bullet.on('click', (function(e, el, o, ii, t){
18535
18536             e.preventDefault();
18537
18538             this.showPanel(ii);
18539
18540             if(this.autoslide && this.slideFn){
18541                 clearInterval(this.slideFn);
18542                 this.slideFn = window.setInterval(function() {
18543                     _this.showPanelNext();
18544                 }, this.timer);
18545             }
18546
18547         }).createDelegate(this, [i, bullet], true));
18548                 
18549         
18550     },
18551      
18552     setActiveBullet : function(i)
18553     {
18554         if(Roo.isTouch){
18555             return;
18556         }
18557         
18558         Roo.each(this.el.select('.bullet', true).elements, function(el){
18559             el.removeClass('selected');
18560         });
18561
18562         var bullet = this.el.select('.bullet-' + i, true).first();
18563         
18564         if(!bullet){
18565             return;
18566         }
18567         
18568         bullet.addClass('selected');
18569     }
18570     
18571     
18572   
18573 });
18574
18575  
18576
18577  
18578  
18579 Roo.apply(Roo.bootstrap.TabGroup, {
18580     
18581     groups: {},
18582      /**
18583     * register a Navigation Group
18584     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18585     */
18586     register : function(navgrp)
18587     {
18588         this.groups[navgrp.navId] = navgrp;
18589         
18590     },
18591     /**
18592     * fetch a Navigation Group based on the navigation ID
18593     * if one does not exist , it will get created.
18594     * @param {string} the navgroup to add
18595     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18596     */
18597     get: function(navId) {
18598         if (typeof(this.groups[navId]) == 'undefined') {
18599             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18600         }
18601         return this.groups[navId] ;
18602     }
18603     
18604     
18605     
18606 });
18607
18608  /*
18609  * - LGPL
18610  *
18611  * TabPanel
18612  * 
18613  */
18614
18615 /**
18616  * @class Roo.bootstrap.TabPanel
18617  * @extends Roo.bootstrap.Component
18618  * Bootstrap TabPanel class
18619  * @cfg {Boolean} active panel active
18620  * @cfg {String} html panel content
18621  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18622  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18623  * @cfg {String} href click to link..
18624  * 
18625  * 
18626  * @constructor
18627  * Create a new TabPanel
18628  * @param {Object} config The config object
18629  */
18630
18631 Roo.bootstrap.TabPanel = function(config){
18632     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18633     this.addEvents({
18634         /**
18635              * @event changed
18636              * Fires when the active status changes
18637              * @param {Roo.bootstrap.TabPanel} this
18638              * @param {Boolean} state the new state
18639             
18640          */
18641         'changed': true,
18642         /**
18643              * @event beforedeactivate
18644              * Fires before a tab is de-activated - can be used to do validation on a form.
18645              * @param {Roo.bootstrap.TabPanel} this
18646              * @return {Boolean} false if there is an error
18647             
18648          */
18649         'beforedeactivate': true
18650      });
18651     
18652     this.tabId = this.tabId || Roo.id();
18653   
18654 };
18655
18656 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18657     
18658     active: false,
18659     html: false,
18660     tabId: false,
18661     navId : false,
18662     href : '',
18663     
18664     getAutoCreate : function(){
18665         
18666         
18667         var cfg = {
18668             tag: 'div',
18669             // item is needed for carousel - not sure if it has any effect otherwise
18670             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18671             html: this.html || ''
18672         };
18673         
18674         if(this.active){
18675             cfg.cls += ' active';
18676         }
18677         
18678         if(this.tabId){
18679             cfg.tabId = this.tabId;
18680         }
18681         
18682         
18683         
18684         return cfg;
18685     },
18686     
18687     initEvents:  function()
18688     {
18689         var p = this.parent();
18690         
18691         this.navId = this.navId || p.navId;
18692         
18693         if (typeof(this.navId) != 'undefined') {
18694             // not really needed.. but just in case.. parent should be a NavGroup.
18695             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18696             
18697             tg.register(this);
18698             
18699             var i = tg.tabs.length - 1;
18700             
18701             if(this.active && tg.bullets > 0 && i < tg.bullets){
18702                 tg.setActiveBullet(i);
18703             }
18704         }
18705         
18706         this.el.on('click', this.onClick, this);
18707         
18708         if(Roo.isTouch){
18709             this.el.on("touchstart", this.onTouchStart, this);
18710             this.el.on("touchmove", this.onTouchMove, this);
18711             this.el.on("touchend", this.onTouchEnd, this);
18712         }
18713         
18714     },
18715     
18716     onRender : function(ct, position)
18717     {
18718         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18719     },
18720     
18721     setActive : function(state)
18722     {
18723         Roo.log("panel - set active " + this.tabId + "=" + state);
18724         
18725         this.active = state;
18726         if (!state) {
18727             this.el.removeClass('active');
18728             
18729         } else  if (!this.el.hasClass('active')) {
18730             this.el.addClass('active');
18731         }
18732         
18733         this.fireEvent('changed', this, state);
18734     },
18735     
18736     onClick : function(e)
18737     {
18738         e.preventDefault();
18739         
18740         if(!this.href.length){
18741             return;
18742         }
18743         
18744         window.location.href = this.href;
18745     },
18746     
18747     startX : 0,
18748     startY : 0,
18749     endX : 0,
18750     endY : 0,
18751     swiping : false,
18752     
18753     onTouchStart : function(e)
18754     {
18755         this.swiping = false;
18756         
18757         this.startX = e.browserEvent.touches[0].clientX;
18758         this.startY = e.browserEvent.touches[0].clientY;
18759     },
18760     
18761     onTouchMove : function(e)
18762     {
18763         this.swiping = true;
18764         
18765         this.endX = e.browserEvent.touches[0].clientX;
18766         this.endY = e.browserEvent.touches[0].clientY;
18767     },
18768     
18769     onTouchEnd : function(e)
18770     {
18771         if(!this.swiping){
18772             this.onClick(e);
18773             return;
18774         }
18775         
18776         var tabGroup = this.parent();
18777         
18778         if(this.endX > this.startX){ // swiping right
18779             tabGroup.showPanelPrev();
18780             return;
18781         }
18782         
18783         if(this.startX > this.endX){ // swiping left
18784             tabGroup.showPanelNext();
18785             return;
18786         }
18787     }
18788     
18789     
18790 });
18791  
18792
18793  
18794
18795  /*
18796  * - LGPL
18797  *
18798  * DateField
18799  * 
18800  */
18801
18802 /**
18803  * @class Roo.bootstrap.DateField
18804  * @extends Roo.bootstrap.Input
18805  * Bootstrap DateField class
18806  * @cfg {Number} weekStart default 0
18807  * @cfg {String} viewMode default empty, (months|years)
18808  * @cfg {String} minViewMode default empty, (months|years)
18809  * @cfg {Number} startDate default -Infinity
18810  * @cfg {Number} endDate default Infinity
18811  * @cfg {Boolean} todayHighlight default false
18812  * @cfg {Boolean} todayBtn default false
18813  * @cfg {Boolean} calendarWeeks default false
18814  * @cfg {Object} daysOfWeekDisabled default empty
18815  * @cfg {Boolean} singleMode default false (true | false)
18816  * 
18817  * @cfg {Boolean} keyboardNavigation default true
18818  * @cfg {String} language default en
18819  * 
18820  * @constructor
18821  * Create a new DateField
18822  * @param {Object} config The config object
18823  */
18824
18825 Roo.bootstrap.DateField = function(config){
18826     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18827      this.addEvents({
18828             /**
18829              * @event show
18830              * Fires when this field show.
18831              * @param {Roo.bootstrap.DateField} this
18832              * @param {Mixed} date The date value
18833              */
18834             show : true,
18835             /**
18836              * @event show
18837              * Fires when this field hide.
18838              * @param {Roo.bootstrap.DateField} this
18839              * @param {Mixed} date The date value
18840              */
18841             hide : true,
18842             /**
18843              * @event select
18844              * Fires when select a date.
18845              * @param {Roo.bootstrap.DateField} this
18846              * @param {Mixed} date The date value
18847              */
18848             select : true,
18849             /**
18850              * @event beforeselect
18851              * Fires when before select a date.
18852              * @param {Roo.bootstrap.DateField} this
18853              * @param {Mixed} date The date value
18854              */
18855             beforeselect : true
18856         });
18857 };
18858
18859 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18860     
18861     /**
18862      * @cfg {String} format
18863      * The default date format string which can be overriden for localization support.  The format must be
18864      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18865      */
18866     format : "m/d/y",
18867     /**
18868      * @cfg {String} altFormats
18869      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18870      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18871      */
18872     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18873     
18874     weekStart : 0,
18875     
18876     viewMode : '',
18877     
18878     minViewMode : '',
18879     
18880     todayHighlight : false,
18881     
18882     todayBtn: false,
18883     
18884     language: 'en',
18885     
18886     keyboardNavigation: true,
18887     
18888     calendarWeeks: false,
18889     
18890     startDate: -Infinity,
18891     
18892     endDate: Infinity,
18893     
18894     daysOfWeekDisabled: [],
18895     
18896     _events: [],
18897     
18898     singleMode : false,
18899     
18900     UTCDate: function()
18901     {
18902         return new Date(Date.UTC.apply(Date, arguments));
18903     },
18904     
18905     UTCToday: function()
18906     {
18907         var today = new Date();
18908         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18909     },
18910     
18911     getDate: function() {
18912             var d = this.getUTCDate();
18913             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18914     },
18915     
18916     getUTCDate: function() {
18917             return this.date;
18918     },
18919     
18920     setDate: function(d) {
18921             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18922     },
18923     
18924     setUTCDate: function(d) {
18925             this.date = d;
18926             this.setValue(this.formatDate(this.date));
18927     },
18928         
18929     onRender: function(ct, position)
18930     {
18931         
18932         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18933         
18934         this.language = this.language || 'en';
18935         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18936         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18937         
18938         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18939         this.format = this.format || 'm/d/y';
18940         this.isInline = false;
18941         this.isInput = true;
18942         this.component = this.el.select('.add-on', true).first() || false;
18943         this.component = (this.component && this.component.length === 0) ? false : this.component;
18944         this.hasInput = this.component && this.inputEl().length;
18945         
18946         if (typeof(this.minViewMode === 'string')) {
18947             switch (this.minViewMode) {
18948                 case 'months':
18949                     this.minViewMode = 1;
18950                     break;
18951                 case 'years':
18952                     this.minViewMode = 2;
18953                     break;
18954                 default:
18955                     this.minViewMode = 0;
18956                     break;
18957             }
18958         }
18959         
18960         if (typeof(this.viewMode === 'string')) {
18961             switch (this.viewMode) {
18962                 case 'months':
18963                     this.viewMode = 1;
18964                     break;
18965                 case 'years':
18966                     this.viewMode = 2;
18967                     break;
18968                 default:
18969                     this.viewMode = 0;
18970                     break;
18971             }
18972         }
18973                 
18974         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18975         
18976 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18977         
18978         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18979         
18980         this.picker().on('mousedown', this.onMousedown, this);
18981         this.picker().on('click', this.onClick, this);
18982         
18983         this.picker().addClass('datepicker-dropdown');
18984         
18985         this.startViewMode = this.viewMode;
18986         
18987         if(this.singleMode){
18988             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18989                 v.setVisibilityMode(Roo.Element.DISPLAY);
18990                 v.hide();
18991             });
18992             
18993             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18994                 v.setStyle('width', '189px');
18995             });
18996         }
18997         
18998         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18999             if(!this.calendarWeeks){
19000                 v.remove();
19001                 return;
19002             }
19003             
19004             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19005             v.attr('colspan', function(i, val){
19006                 return parseInt(val) + 1;
19007             });
19008         });
19009                         
19010         
19011         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
19012         
19013         this.setStartDate(this.startDate);
19014         this.setEndDate(this.endDate);
19015         
19016         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
19017         
19018         this.fillDow();
19019         this.fillMonths();
19020         this.update();
19021         this.showMode();
19022         
19023         if(this.isInline) {
19024             this.showPopup();
19025         }
19026     },
19027     
19028     picker : function()
19029     {
19030         return this.pickerEl;
19031 //        return this.el.select('.datepicker', true).first();
19032     },
19033     
19034     fillDow: function()
19035     {
19036         var dowCnt = this.weekStart;
19037         
19038         var dow = {
19039             tag: 'tr',
19040             cn: [
19041                 
19042             ]
19043         };
19044         
19045         if(this.calendarWeeks){
19046             dow.cn.push({
19047                 tag: 'th',
19048                 cls: 'cw',
19049                 html: '&nbsp;'
19050             })
19051         }
19052         
19053         while (dowCnt < this.weekStart + 7) {
19054             dow.cn.push({
19055                 tag: 'th',
19056                 cls: 'dow',
19057                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
19058             });
19059         }
19060         
19061         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
19062     },
19063     
19064     fillMonths: function()
19065     {    
19066         var i = 0;
19067         var months = this.picker().select('>.datepicker-months td', true).first();
19068         
19069         months.dom.innerHTML = '';
19070         
19071         while (i < 12) {
19072             var month = {
19073                 tag: 'span',
19074                 cls: 'month',
19075                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
19076             };
19077             
19078             months.createChild(month);
19079         }
19080         
19081     },
19082     
19083     update: function()
19084     {
19085         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;
19086         
19087         if (this.date < this.startDate) {
19088             this.viewDate = new Date(this.startDate);
19089         } else if (this.date > this.endDate) {
19090             this.viewDate = new Date(this.endDate);
19091         } else {
19092             this.viewDate = new Date(this.date);
19093         }
19094         
19095         this.fill();
19096     },
19097     
19098     fill: function() 
19099     {
19100         var d = new Date(this.viewDate),
19101                 year = d.getUTCFullYear(),
19102                 month = d.getUTCMonth(),
19103                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
19104                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
19105                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
19106                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
19107                 currentDate = this.date && this.date.valueOf(),
19108                 today = this.UTCToday();
19109         
19110         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
19111         
19112 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19113         
19114 //        this.picker.select('>tfoot th.today').
19115 //                                              .text(dates[this.language].today)
19116 //                                              .toggle(this.todayBtn !== false);
19117     
19118         this.updateNavArrows();
19119         this.fillMonths();
19120                                                 
19121         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
19122         
19123         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
19124          
19125         prevMonth.setUTCDate(day);
19126         
19127         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
19128         
19129         var nextMonth = new Date(prevMonth);
19130         
19131         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
19132         
19133         nextMonth = nextMonth.valueOf();
19134         
19135         var fillMonths = false;
19136         
19137         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
19138         
19139         while(prevMonth.valueOf() <= nextMonth) {
19140             var clsName = '';
19141             
19142             if (prevMonth.getUTCDay() === this.weekStart) {
19143                 if(fillMonths){
19144                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
19145                 }
19146                     
19147                 fillMonths = {
19148                     tag: 'tr',
19149                     cn: []
19150                 };
19151                 
19152                 if(this.calendarWeeks){
19153                     // ISO 8601: First week contains first thursday.
19154                     // ISO also states week starts on Monday, but we can be more abstract here.
19155                     var
19156                     // Start of current week: based on weekstart/current date
19157                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
19158                     // Thursday of this week
19159                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
19160                     // First Thursday of year, year from thursday
19161                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
19162                     // Calendar week: ms between thursdays, div ms per day, div 7 days
19163                     calWeek =  (th - yth) / 864e5 / 7 + 1;
19164                     
19165                     fillMonths.cn.push({
19166                         tag: 'td',
19167                         cls: 'cw',
19168                         html: calWeek
19169                     });
19170                 }
19171             }
19172             
19173             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
19174                 clsName += ' old';
19175             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19176                 clsName += ' new';
19177             }
19178             if (this.todayHighlight &&
19179                 prevMonth.getUTCFullYear() == today.getFullYear() &&
19180                 prevMonth.getUTCMonth() == today.getMonth() &&
19181                 prevMonth.getUTCDate() == today.getDate()) {
19182                 clsName += ' today';
19183             }
19184             
19185             if (currentDate && prevMonth.valueOf() === currentDate) {
19186                 clsName += ' active';
19187             }
19188             
19189             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19190                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19191                     clsName += ' disabled';
19192             }
19193             
19194             fillMonths.cn.push({
19195                 tag: 'td',
19196                 cls: 'day ' + clsName,
19197                 html: prevMonth.getDate()
19198             });
19199             
19200             prevMonth.setDate(prevMonth.getDate()+1);
19201         }
19202           
19203         var currentYear = this.date && this.date.getUTCFullYear();
19204         var currentMonth = this.date && this.date.getUTCMonth();
19205         
19206         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19207         
19208         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19209             v.removeClass('active');
19210             
19211             if(currentYear === year && k === currentMonth){
19212                 v.addClass('active');
19213             }
19214             
19215             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19216                 v.addClass('disabled');
19217             }
19218             
19219         });
19220         
19221         
19222         year = parseInt(year/10, 10) * 10;
19223         
19224         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19225         
19226         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19227         
19228         year -= 1;
19229         for (var i = -1; i < 11; i++) {
19230             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19231                 tag: 'span',
19232                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19233                 html: year
19234             });
19235             
19236             year += 1;
19237         }
19238     },
19239     
19240     showMode: function(dir) 
19241     {
19242         if (dir) {
19243             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19244         }
19245         
19246         Roo.each(this.picker().select('>div',true).elements, function(v){
19247             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19248             v.hide();
19249         });
19250         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19251     },
19252     
19253     place: function()
19254     {
19255         if(this.isInline) {
19256             return;
19257         }
19258         
19259         this.picker().removeClass(['bottom', 'top']);
19260         
19261         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19262             /*
19263              * place to the top of element!
19264              *
19265              */
19266             
19267             this.picker().addClass('top');
19268             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19269             
19270             return;
19271         }
19272         
19273         this.picker().addClass('bottom');
19274         
19275         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19276     },
19277     
19278     parseDate : function(value)
19279     {
19280         if(!value || value instanceof Date){
19281             return value;
19282         }
19283         var v = Date.parseDate(value, this.format);
19284         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19285             v = Date.parseDate(value, 'Y-m-d');
19286         }
19287         if(!v && this.altFormats){
19288             if(!this.altFormatsArray){
19289                 this.altFormatsArray = this.altFormats.split("|");
19290             }
19291             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19292                 v = Date.parseDate(value, this.altFormatsArray[i]);
19293             }
19294         }
19295         return v;
19296     },
19297     
19298     formatDate : function(date, fmt)
19299     {   
19300         return (!date || !(date instanceof Date)) ?
19301         date : date.dateFormat(fmt || this.format);
19302     },
19303     
19304     onFocus : function()
19305     {
19306         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19307         this.showPopup();
19308     },
19309     
19310     onBlur : function()
19311     {
19312         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19313         
19314         var d = this.inputEl().getValue();
19315         
19316         this.setValue(d);
19317                 
19318         this.hidePopup();
19319     },
19320     
19321     showPopup : function()
19322     {
19323         this.picker().show();
19324         this.update();
19325         this.place();
19326         
19327         this.fireEvent('showpopup', this, this.date);
19328     },
19329     
19330     hidePopup : function()
19331     {
19332         if(this.isInline) {
19333             return;
19334         }
19335         this.picker().hide();
19336         this.viewMode = this.startViewMode;
19337         this.showMode();
19338         
19339         this.fireEvent('hidepopup', this, this.date);
19340         
19341     },
19342     
19343     onMousedown: function(e)
19344     {
19345         e.stopPropagation();
19346         e.preventDefault();
19347     },
19348     
19349     keyup: function(e)
19350     {
19351         Roo.bootstrap.DateField.superclass.keyup.call(this);
19352         this.update();
19353     },
19354
19355     setValue: function(v)
19356     {
19357         if(this.fireEvent('beforeselect', this, v) !== false){
19358             var d = new Date(this.parseDate(v) ).clearTime();
19359         
19360             if(isNaN(d.getTime())){
19361                 this.date = this.viewDate = '';
19362                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19363                 return;
19364             }
19365
19366             v = this.formatDate(d);
19367
19368             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19369
19370             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19371
19372             this.update();
19373
19374             this.fireEvent('select', this, this.date);
19375         }
19376     },
19377     
19378     getValue: function()
19379     {
19380         return this.formatDate(this.date);
19381     },
19382     
19383     fireKey: function(e)
19384     {
19385         if (!this.picker().isVisible()){
19386             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19387                 this.showPopup();
19388             }
19389             return;
19390         }
19391         
19392         var dateChanged = false,
19393         dir, day, month,
19394         newDate, newViewDate;
19395         
19396         switch(e.keyCode){
19397             case 27: // escape
19398                 this.hidePopup();
19399                 e.preventDefault();
19400                 break;
19401             case 37: // left
19402             case 39: // right
19403                 if (!this.keyboardNavigation) {
19404                     break;
19405                 }
19406                 dir = e.keyCode == 37 ? -1 : 1;
19407                 
19408                 if (e.ctrlKey){
19409                     newDate = this.moveYear(this.date, dir);
19410                     newViewDate = this.moveYear(this.viewDate, dir);
19411                 } else if (e.shiftKey){
19412                     newDate = this.moveMonth(this.date, dir);
19413                     newViewDate = this.moveMonth(this.viewDate, dir);
19414                 } else {
19415                     newDate = new Date(this.date);
19416                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19417                     newViewDate = new Date(this.viewDate);
19418                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19419                 }
19420                 if (this.dateWithinRange(newDate)){
19421                     this.date = newDate;
19422                     this.viewDate = newViewDate;
19423                     this.setValue(this.formatDate(this.date));
19424 //                    this.update();
19425                     e.preventDefault();
19426                     dateChanged = true;
19427                 }
19428                 break;
19429             case 38: // up
19430             case 40: // down
19431                 if (!this.keyboardNavigation) {
19432                     break;
19433                 }
19434                 dir = e.keyCode == 38 ? -1 : 1;
19435                 if (e.ctrlKey){
19436                     newDate = this.moveYear(this.date, dir);
19437                     newViewDate = this.moveYear(this.viewDate, dir);
19438                 } else if (e.shiftKey){
19439                     newDate = this.moveMonth(this.date, dir);
19440                     newViewDate = this.moveMonth(this.viewDate, dir);
19441                 } else {
19442                     newDate = new Date(this.date);
19443                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19444                     newViewDate = new Date(this.viewDate);
19445                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19446                 }
19447                 if (this.dateWithinRange(newDate)){
19448                     this.date = newDate;
19449                     this.viewDate = newViewDate;
19450                     this.setValue(this.formatDate(this.date));
19451 //                    this.update();
19452                     e.preventDefault();
19453                     dateChanged = true;
19454                 }
19455                 break;
19456             case 13: // enter
19457                 this.setValue(this.formatDate(this.date));
19458                 this.hidePopup();
19459                 e.preventDefault();
19460                 break;
19461             case 9: // tab
19462                 this.setValue(this.formatDate(this.date));
19463                 this.hidePopup();
19464                 break;
19465             case 16: // shift
19466             case 17: // ctrl
19467             case 18: // alt
19468                 break;
19469             default :
19470                 this.hidePopup();
19471                 
19472         }
19473     },
19474     
19475     
19476     onClick: function(e) 
19477     {
19478         e.stopPropagation();
19479         e.preventDefault();
19480         
19481         var target = e.getTarget();
19482         
19483         if(target.nodeName.toLowerCase() === 'i'){
19484             target = Roo.get(target).dom.parentNode;
19485         }
19486         
19487         var nodeName = target.nodeName;
19488         var className = target.className;
19489         var html = target.innerHTML;
19490         //Roo.log(nodeName);
19491         
19492         switch(nodeName.toLowerCase()) {
19493             case 'th':
19494                 switch(className) {
19495                     case 'switch':
19496                         this.showMode(1);
19497                         break;
19498                     case 'prev':
19499                     case 'next':
19500                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19501                         switch(this.viewMode){
19502                                 case 0:
19503                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19504                                         break;
19505                                 case 1:
19506                                 case 2:
19507                                         this.viewDate = this.moveYear(this.viewDate, dir);
19508                                         break;
19509                         }
19510                         this.fill();
19511                         break;
19512                     case 'today':
19513                         var date = new Date();
19514                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19515 //                        this.fill()
19516                         this.setValue(this.formatDate(this.date));
19517                         
19518                         this.hidePopup();
19519                         break;
19520                 }
19521                 break;
19522             case 'span':
19523                 if (className.indexOf('disabled') < 0) {
19524                     this.viewDate.setUTCDate(1);
19525                     if (className.indexOf('month') > -1) {
19526                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19527                     } else {
19528                         var year = parseInt(html, 10) || 0;
19529                         this.viewDate.setUTCFullYear(year);
19530                         
19531                     }
19532                     
19533                     if(this.singleMode){
19534                         this.setValue(this.formatDate(this.viewDate));
19535                         this.hidePopup();
19536                         return;
19537                     }
19538                     
19539                     this.showMode(-1);
19540                     this.fill();
19541                 }
19542                 break;
19543                 
19544             case 'td':
19545                 //Roo.log(className);
19546                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19547                     var day = parseInt(html, 10) || 1;
19548                     var year = this.viewDate.getUTCFullYear(),
19549                         month = this.viewDate.getUTCMonth();
19550
19551                     if (className.indexOf('old') > -1) {
19552                         if(month === 0 ){
19553                             month = 11;
19554                             year -= 1;
19555                         }else{
19556                             month -= 1;
19557                         }
19558                     } else if (className.indexOf('new') > -1) {
19559                         if (month == 11) {
19560                             month = 0;
19561                             year += 1;
19562                         } else {
19563                             month += 1;
19564                         }
19565                     }
19566                     //Roo.log([year,month,day]);
19567                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19568                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19569 //                    this.fill();
19570                     //Roo.log(this.formatDate(this.date));
19571                     this.setValue(this.formatDate(this.date));
19572                     this.hidePopup();
19573                 }
19574                 break;
19575         }
19576     },
19577     
19578     setStartDate: function(startDate)
19579     {
19580         this.startDate = startDate || -Infinity;
19581         if (this.startDate !== -Infinity) {
19582             this.startDate = this.parseDate(this.startDate);
19583         }
19584         this.update();
19585         this.updateNavArrows();
19586     },
19587
19588     setEndDate: function(endDate)
19589     {
19590         this.endDate = endDate || Infinity;
19591         if (this.endDate !== Infinity) {
19592             this.endDate = this.parseDate(this.endDate);
19593         }
19594         this.update();
19595         this.updateNavArrows();
19596     },
19597     
19598     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19599     {
19600         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19601         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19602             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19603         }
19604         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19605             return parseInt(d, 10);
19606         });
19607         this.update();
19608         this.updateNavArrows();
19609     },
19610     
19611     updateNavArrows: function() 
19612     {
19613         if(this.singleMode){
19614             return;
19615         }
19616         
19617         var d = new Date(this.viewDate),
19618         year = d.getUTCFullYear(),
19619         month = d.getUTCMonth();
19620         
19621         Roo.each(this.picker().select('.prev', true).elements, function(v){
19622             v.show();
19623             switch (this.viewMode) {
19624                 case 0:
19625
19626                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19627                         v.hide();
19628                     }
19629                     break;
19630                 case 1:
19631                 case 2:
19632                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19633                         v.hide();
19634                     }
19635                     break;
19636             }
19637         });
19638         
19639         Roo.each(this.picker().select('.next', true).elements, function(v){
19640             v.show();
19641             switch (this.viewMode) {
19642                 case 0:
19643
19644                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19645                         v.hide();
19646                     }
19647                     break;
19648                 case 1:
19649                 case 2:
19650                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19651                         v.hide();
19652                     }
19653                     break;
19654             }
19655         })
19656     },
19657     
19658     moveMonth: function(date, dir)
19659     {
19660         if (!dir) {
19661             return date;
19662         }
19663         var new_date = new Date(date.valueOf()),
19664         day = new_date.getUTCDate(),
19665         month = new_date.getUTCMonth(),
19666         mag = Math.abs(dir),
19667         new_month, test;
19668         dir = dir > 0 ? 1 : -1;
19669         if (mag == 1){
19670             test = dir == -1
19671             // If going back one month, make sure month is not current month
19672             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19673             ? function(){
19674                 return new_date.getUTCMonth() == month;
19675             }
19676             // If going forward one month, make sure month is as expected
19677             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19678             : function(){
19679                 return new_date.getUTCMonth() != new_month;
19680             };
19681             new_month = month + dir;
19682             new_date.setUTCMonth(new_month);
19683             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19684             if (new_month < 0 || new_month > 11) {
19685                 new_month = (new_month + 12) % 12;
19686             }
19687         } else {
19688             // For magnitudes >1, move one month at a time...
19689             for (var i=0; i<mag; i++) {
19690                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19691                 new_date = this.moveMonth(new_date, dir);
19692             }
19693             // ...then reset the day, keeping it in the new month
19694             new_month = new_date.getUTCMonth();
19695             new_date.setUTCDate(day);
19696             test = function(){
19697                 return new_month != new_date.getUTCMonth();
19698             };
19699         }
19700         // Common date-resetting loop -- if date is beyond end of month, make it
19701         // end of month
19702         while (test()){
19703             new_date.setUTCDate(--day);
19704             new_date.setUTCMonth(new_month);
19705         }
19706         return new_date;
19707     },
19708
19709     moveYear: function(date, dir)
19710     {
19711         return this.moveMonth(date, dir*12);
19712     },
19713
19714     dateWithinRange: function(date)
19715     {
19716         return date >= this.startDate && date <= this.endDate;
19717     },
19718
19719     
19720     remove: function() 
19721     {
19722         this.picker().remove();
19723     },
19724     
19725     validateValue : function(value)
19726     {
19727         if(this.getVisibilityEl().hasClass('hidden')){
19728             return true;
19729         }
19730         
19731         if(value.length < 1)  {
19732             if(this.allowBlank){
19733                 return true;
19734             }
19735             return false;
19736         }
19737         
19738         if(value.length < this.minLength){
19739             return false;
19740         }
19741         if(value.length > this.maxLength){
19742             return false;
19743         }
19744         if(this.vtype){
19745             var vt = Roo.form.VTypes;
19746             if(!vt[this.vtype](value, this)){
19747                 return false;
19748             }
19749         }
19750         if(typeof this.validator == "function"){
19751             var msg = this.validator(value);
19752             if(msg !== true){
19753                 return false;
19754             }
19755         }
19756         
19757         if(this.regex && !this.regex.test(value)){
19758             return false;
19759         }
19760         
19761         if(typeof(this.parseDate(value)) == 'undefined'){
19762             return false;
19763         }
19764         
19765         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19766             return false;
19767         }      
19768         
19769         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19770             return false;
19771         } 
19772         
19773         
19774         return true;
19775     },
19776     
19777     reset : function()
19778     {
19779         this.date = this.viewDate = '';
19780         
19781         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19782     }
19783    
19784 });
19785
19786 Roo.apply(Roo.bootstrap.DateField,  {
19787     
19788     head : {
19789         tag: 'thead',
19790         cn: [
19791         {
19792             tag: 'tr',
19793             cn: [
19794             {
19795                 tag: 'th',
19796                 cls: 'prev',
19797                 html: '<i class="fa fa-arrow-left"/>'
19798             },
19799             {
19800                 tag: 'th',
19801                 cls: 'switch',
19802                 colspan: '5'
19803             },
19804             {
19805                 tag: 'th',
19806                 cls: 'next',
19807                 html: '<i class="fa fa-arrow-right"/>'
19808             }
19809
19810             ]
19811         }
19812         ]
19813     },
19814     
19815     content : {
19816         tag: 'tbody',
19817         cn: [
19818         {
19819             tag: 'tr',
19820             cn: [
19821             {
19822                 tag: 'td',
19823                 colspan: '7'
19824             }
19825             ]
19826         }
19827         ]
19828     },
19829     
19830     footer : {
19831         tag: 'tfoot',
19832         cn: [
19833         {
19834             tag: 'tr',
19835             cn: [
19836             {
19837                 tag: 'th',
19838                 colspan: '7',
19839                 cls: 'today'
19840             }
19841                     
19842             ]
19843         }
19844         ]
19845     },
19846     
19847     dates:{
19848         en: {
19849             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19850             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19851             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19852             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19853             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19854             today: "Today"
19855         }
19856     },
19857     
19858     modes: [
19859     {
19860         clsName: 'days',
19861         navFnc: 'Month',
19862         navStep: 1
19863     },
19864     {
19865         clsName: 'months',
19866         navFnc: 'FullYear',
19867         navStep: 1
19868     },
19869     {
19870         clsName: 'years',
19871         navFnc: 'FullYear',
19872         navStep: 10
19873     }]
19874 });
19875
19876 Roo.apply(Roo.bootstrap.DateField,  {
19877   
19878     template : {
19879         tag: 'div',
19880         cls: 'datepicker dropdown-menu roo-dynamic',
19881         cn: [
19882         {
19883             tag: 'div',
19884             cls: 'datepicker-days',
19885             cn: [
19886             {
19887                 tag: 'table',
19888                 cls: 'table-condensed',
19889                 cn:[
19890                 Roo.bootstrap.DateField.head,
19891                 {
19892                     tag: 'tbody'
19893                 },
19894                 Roo.bootstrap.DateField.footer
19895                 ]
19896             }
19897             ]
19898         },
19899         {
19900             tag: 'div',
19901             cls: 'datepicker-months',
19902             cn: [
19903             {
19904                 tag: 'table',
19905                 cls: 'table-condensed',
19906                 cn:[
19907                 Roo.bootstrap.DateField.head,
19908                 Roo.bootstrap.DateField.content,
19909                 Roo.bootstrap.DateField.footer
19910                 ]
19911             }
19912             ]
19913         },
19914         {
19915             tag: 'div',
19916             cls: 'datepicker-years',
19917             cn: [
19918             {
19919                 tag: 'table',
19920                 cls: 'table-condensed',
19921                 cn:[
19922                 Roo.bootstrap.DateField.head,
19923                 Roo.bootstrap.DateField.content,
19924                 Roo.bootstrap.DateField.footer
19925                 ]
19926             }
19927             ]
19928         }
19929         ]
19930     }
19931 });
19932
19933  
19934
19935  /*
19936  * - LGPL
19937  *
19938  * TimeField
19939  * 
19940  */
19941
19942 /**
19943  * @class Roo.bootstrap.TimeField
19944  * @extends Roo.bootstrap.Input
19945  * Bootstrap DateField class
19946  * 
19947  * 
19948  * @constructor
19949  * Create a new TimeField
19950  * @param {Object} config The config object
19951  */
19952
19953 Roo.bootstrap.TimeField = function(config){
19954     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19955     this.addEvents({
19956             /**
19957              * @event show
19958              * Fires when this field show.
19959              * @param {Roo.bootstrap.DateField} thisthis
19960              * @param {Mixed} date The date value
19961              */
19962             show : true,
19963             /**
19964              * @event show
19965              * Fires when this field hide.
19966              * @param {Roo.bootstrap.DateField} this
19967              * @param {Mixed} date The date value
19968              */
19969             hide : true,
19970             /**
19971              * @event select
19972              * Fires when select a date.
19973              * @param {Roo.bootstrap.DateField} this
19974              * @param {Mixed} date The date value
19975              */
19976             select : true
19977         });
19978 };
19979
19980 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19981     
19982     /**
19983      * @cfg {String} format
19984      * The default time format string which can be overriden for localization support.  The format must be
19985      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19986      */
19987     format : "H:i",
19988        
19989     onRender: function(ct, position)
19990     {
19991         
19992         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19993                 
19994         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19995         
19996         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19997         
19998         this.pop = this.picker().select('>.datepicker-time',true).first();
19999         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20000         
20001         this.picker().on('mousedown', this.onMousedown, this);
20002         this.picker().on('click', this.onClick, this);
20003         
20004         this.picker().addClass('datepicker-dropdown');
20005     
20006         this.fillTime();
20007         this.update();
20008             
20009         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
20010         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
20011         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
20012         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
20013         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
20014         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
20015
20016     },
20017     
20018     fireKey: function(e){
20019         if (!this.picker().isVisible()){
20020             if (e.keyCode == 27) { // allow escape to hide and re-show picker
20021                 this.show();
20022             }
20023             return;
20024         }
20025
20026         e.preventDefault();
20027         
20028         switch(e.keyCode){
20029             case 27: // escape
20030                 this.hide();
20031                 break;
20032             case 37: // left
20033             case 39: // right
20034                 this.onTogglePeriod();
20035                 break;
20036             case 38: // up
20037                 this.onIncrementMinutes();
20038                 break;
20039             case 40: // down
20040                 this.onDecrementMinutes();
20041                 break;
20042             case 13: // enter
20043             case 9: // tab
20044                 this.setTime();
20045                 break;
20046         }
20047     },
20048     
20049     onClick: function(e) {
20050         e.stopPropagation();
20051         e.preventDefault();
20052     },
20053     
20054     picker : function()
20055     {
20056         return this.el.select('.datepicker', true).first();
20057     },
20058     
20059     fillTime: function()
20060     {    
20061         var time = this.pop.select('tbody', true).first();
20062         
20063         time.dom.innerHTML = '';
20064         
20065         time.createChild({
20066             tag: 'tr',
20067             cn: [
20068                 {
20069                     tag: 'td',
20070                     cn: [
20071                         {
20072                             tag: 'a',
20073                             href: '#',
20074                             cls: 'btn',
20075                             cn: [
20076                                 {
20077                                     tag: 'span',
20078                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
20079                                 }
20080                             ]
20081                         } 
20082                     ]
20083                 },
20084                 {
20085                     tag: 'td',
20086                     cls: 'separator'
20087                 },
20088                 {
20089                     tag: 'td',
20090                     cn: [
20091                         {
20092                             tag: 'a',
20093                             href: '#',
20094                             cls: 'btn',
20095                             cn: [
20096                                 {
20097                                     tag: 'span',
20098                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
20099                                 }
20100                             ]
20101                         }
20102                     ]
20103                 },
20104                 {
20105                     tag: 'td',
20106                     cls: 'separator'
20107                 }
20108             ]
20109         });
20110         
20111         time.createChild({
20112             tag: 'tr',
20113             cn: [
20114                 {
20115                     tag: 'td',
20116                     cn: [
20117                         {
20118                             tag: 'span',
20119                             cls: 'timepicker-hour',
20120                             html: '00'
20121                         }  
20122                     ]
20123                 },
20124                 {
20125                     tag: 'td',
20126                     cls: 'separator',
20127                     html: ':'
20128                 },
20129                 {
20130                     tag: 'td',
20131                     cn: [
20132                         {
20133                             tag: 'span',
20134                             cls: 'timepicker-minute',
20135                             html: '00'
20136                         }  
20137                     ]
20138                 },
20139                 {
20140                     tag: 'td',
20141                     cls: 'separator'
20142                 },
20143                 {
20144                     tag: 'td',
20145                     cn: [
20146                         {
20147                             tag: 'button',
20148                             type: 'button',
20149                             cls: 'btn btn-primary period',
20150                             html: 'AM'
20151                             
20152                         }
20153                     ]
20154                 }
20155             ]
20156         });
20157         
20158         time.createChild({
20159             tag: 'tr',
20160             cn: [
20161                 {
20162                     tag: 'td',
20163                     cn: [
20164                         {
20165                             tag: 'a',
20166                             href: '#',
20167                             cls: 'btn',
20168                             cn: [
20169                                 {
20170                                     tag: 'span',
20171                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
20172                                 }
20173                             ]
20174                         }
20175                     ]
20176                 },
20177                 {
20178                     tag: 'td',
20179                     cls: 'separator'
20180                 },
20181                 {
20182                     tag: 'td',
20183                     cn: [
20184                         {
20185                             tag: 'a',
20186                             href: '#',
20187                             cls: 'btn',
20188                             cn: [
20189                                 {
20190                                     tag: 'span',
20191                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
20192                                 }
20193                             ]
20194                         }
20195                     ]
20196                 },
20197                 {
20198                     tag: 'td',
20199                     cls: 'separator'
20200                 }
20201             ]
20202         });
20203         
20204     },
20205     
20206     update: function()
20207     {
20208         
20209         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20210         
20211         this.fill();
20212     },
20213     
20214     fill: function() 
20215     {
20216         var hours = this.time.getHours();
20217         var minutes = this.time.getMinutes();
20218         var period = 'AM';
20219         
20220         if(hours > 11){
20221             period = 'PM';
20222         }
20223         
20224         if(hours == 0){
20225             hours = 12;
20226         }
20227         
20228         
20229         if(hours > 12){
20230             hours = hours - 12;
20231         }
20232         
20233         if(hours < 10){
20234             hours = '0' + hours;
20235         }
20236         
20237         if(minutes < 10){
20238             minutes = '0' + minutes;
20239         }
20240         
20241         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20242         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20243         this.pop.select('button', true).first().dom.innerHTML = period;
20244         
20245     },
20246     
20247     place: function()
20248     {   
20249         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20250         
20251         var cls = ['bottom'];
20252         
20253         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20254             cls.pop();
20255             cls.push('top');
20256         }
20257         
20258         cls.push('right');
20259         
20260         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20261             cls.pop();
20262             cls.push('left');
20263         }
20264         
20265         this.picker().addClass(cls.join('-'));
20266         
20267         var _this = this;
20268         
20269         Roo.each(cls, function(c){
20270             if(c == 'bottom'){
20271                 _this.picker().setTop(_this.inputEl().getHeight());
20272                 return;
20273             }
20274             if(c == 'top'){
20275                 _this.picker().setTop(0 - _this.picker().getHeight());
20276                 return;
20277             }
20278             
20279             if(c == 'left'){
20280                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20281                 return;
20282             }
20283             if(c == 'right'){
20284                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20285                 return;
20286             }
20287         });
20288         
20289     },
20290   
20291     onFocus : function()
20292     {
20293         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20294         this.show();
20295     },
20296     
20297     onBlur : function()
20298     {
20299         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20300         this.hide();
20301     },
20302     
20303     show : function()
20304     {
20305         this.picker().show();
20306         this.pop.show();
20307         this.update();
20308         this.place();
20309         
20310         this.fireEvent('show', this, this.date);
20311     },
20312     
20313     hide : function()
20314     {
20315         this.picker().hide();
20316         this.pop.hide();
20317         
20318         this.fireEvent('hide', this, this.date);
20319     },
20320     
20321     setTime : function()
20322     {
20323         this.hide();
20324         this.setValue(this.time.format(this.format));
20325         
20326         this.fireEvent('select', this, this.date);
20327         
20328         
20329     },
20330     
20331     onMousedown: function(e){
20332         e.stopPropagation();
20333         e.preventDefault();
20334     },
20335     
20336     onIncrementHours: function()
20337     {
20338         Roo.log('onIncrementHours');
20339         this.time = this.time.add(Date.HOUR, 1);
20340         this.update();
20341         
20342     },
20343     
20344     onDecrementHours: function()
20345     {
20346         Roo.log('onDecrementHours');
20347         this.time = this.time.add(Date.HOUR, -1);
20348         this.update();
20349     },
20350     
20351     onIncrementMinutes: function()
20352     {
20353         Roo.log('onIncrementMinutes');
20354         this.time = this.time.add(Date.MINUTE, 1);
20355         this.update();
20356     },
20357     
20358     onDecrementMinutes: function()
20359     {
20360         Roo.log('onDecrementMinutes');
20361         this.time = this.time.add(Date.MINUTE, -1);
20362         this.update();
20363     },
20364     
20365     onTogglePeriod: function()
20366     {
20367         Roo.log('onTogglePeriod');
20368         this.time = this.time.add(Date.HOUR, 12);
20369         this.update();
20370     }
20371     
20372    
20373 });
20374
20375 Roo.apply(Roo.bootstrap.TimeField,  {
20376     
20377     content : {
20378         tag: 'tbody',
20379         cn: [
20380             {
20381                 tag: 'tr',
20382                 cn: [
20383                 {
20384                     tag: 'td',
20385                     colspan: '7'
20386                 }
20387                 ]
20388             }
20389         ]
20390     },
20391     
20392     footer : {
20393         tag: 'tfoot',
20394         cn: [
20395             {
20396                 tag: 'tr',
20397                 cn: [
20398                 {
20399                     tag: 'th',
20400                     colspan: '7',
20401                     cls: '',
20402                     cn: [
20403                         {
20404                             tag: 'button',
20405                             cls: 'btn btn-info ok',
20406                             html: 'OK'
20407                         }
20408                     ]
20409                 }
20410
20411                 ]
20412             }
20413         ]
20414     }
20415 });
20416
20417 Roo.apply(Roo.bootstrap.TimeField,  {
20418   
20419     template : {
20420         tag: 'div',
20421         cls: 'datepicker dropdown-menu',
20422         cn: [
20423             {
20424                 tag: 'div',
20425                 cls: 'datepicker-time',
20426                 cn: [
20427                 {
20428                     tag: 'table',
20429                     cls: 'table-condensed',
20430                     cn:[
20431                     Roo.bootstrap.TimeField.content,
20432                     Roo.bootstrap.TimeField.footer
20433                     ]
20434                 }
20435                 ]
20436             }
20437         ]
20438     }
20439 });
20440
20441  
20442
20443  /*
20444  * - LGPL
20445  *
20446  * MonthField
20447  * 
20448  */
20449
20450 /**
20451  * @class Roo.bootstrap.MonthField
20452  * @extends Roo.bootstrap.Input
20453  * Bootstrap MonthField class
20454  * 
20455  * @cfg {String} language default en
20456  * 
20457  * @constructor
20458  * Create a new MonthField
20459  * @param {Object} config The config object
20460  */
20461
20462 Roo.bootstrap.MonthField = function(config){
20463     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20464     
20465     this.addEvents({
20466         /**
20467          * @event show
20468          * Fires when this field show.
20469          * @param {Roo.bootstrap.MonthField} this
20470          * @param {Mixed} date The date value
20471          */
20472         show : true,
20473         /**
20474          * @event show
20475          * Fires when this field hide.
20476          * @param {Roo.bootstrap.MonthField} this
20477          * @param {Mixed} date The date value
20478          */
20479         hide : true,
20480         /**
20481          * @event select
20482          * Fires when select a date.
20483          * @param {Roo.bootstrap.MonthField} this
20484          * @param {String} oldvalue The old value
20485          * @param {String} newvalue The new value
20486          */
20487         select : true
20488     });
20489 };
20490
20491 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20492     
20493     onRender: function(ct, position)
20494     {
20495         
20496         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20497         
20498         this.language = this.language || 'en';
20499         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20500         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20501         
20502         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20503         this.isInline = false;
20504         this.isInput = true;
20505         this.component = this.el.select('.add-on', true).first() || false;
20506         this.component = (this.component && this.component.length === 0) ? false : this.component;
20507         this.hasInput = this.component && this.inputEL().length;
20508         
20509         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20510         
20511         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20512         
20513         this.picker().on('mousedown', this.onMousedown, this);
20514         this.picker().on('click', this.onClick, this);
20515         
20516         this.picker().addClass('datepicker-dropdown');
20517         
20518         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20519             v.setStyle('width', '189px');
20520         });
20521         
20522         this.fillMonths();
20523         
20524         this.update();
20525         
20526         if(this.isInline) {
20527             this.show();
20528         }
20529         
20530     },
20531     
20532     setValue: function(v, suppressEvent)
20533     {   
20534         var o = this.getValue();
20535         
20536         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20537         
20538         this.update();
20539
20540         if(suppressEvent !== true){
20541             this.fireEvent('select', this, o, v);
20542         }
20543         
20544     },
20545     
20546     getValue: function()
20547     {
20548         return this.value;
20549     },
20550     
20551     onClick: function(e) 
20552     {
20553         e.stopPropagation();
20554         e.preventDefault();
20555         
20556         var target = e.getTarget();
20557         
20558         if(target.nodeName.toLowerCase() === 'i'){
20559             target = Roo.get(target).dom.parentNode;
20560         }
20561         
20562         var nodeName = target.nodeName;
20563         var className = target.className;
20564         var html = target.innerHTML;
20565         
20566         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20567             return;
20568         }
20569         
20570         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20571         
20572         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20573         
20574         this.hide();
20575                         
20576     },
20577     
20578     picker : function()
20579     {
20580         return this.pickerEl;
20581     },
20582     
20583     fillMonths: function()
20584     {    
20585         var i = 0;
20586         var months = this.picker().select('>.datepicker-months td', true).first();
20587         
20588         months.dom.innerHTML = '';
20589         
20590         while (i < 12) {
20591             var month = {
20592                 tag: 'span',
20593                 cls: 'month',
20594                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20595             };
20596             
20597             months.createChild(month);
20598         }
20599         
20600     },
20601     
20602     update: function()
20603     {
20604         var _this = this;
20605         
20606         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20607             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20608         }
20609         
20610         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20611             e.removeClass('active');
20612             
20613             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20614                 e.addClass('active');
20615             }
20616         })
20617     },
20618     
20619     place: function()
20620     {
20621         if(this.isInline) {
20622             return;
20623         }
20624         
20625         this.picker().removeClass(['bottom', 'top']);
20626         
20627         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20628             /*
20629              * place to the top of element!
20630              *
20631              */
20632             
20633             this.picker().addClass('top');
20634             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20635             
20636             return;
20637         }
20638         
20639         this.picker().addClass('bottom');
20640         
20641         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20642     },
20643     
20644     onFocus : function()
20645     {
20646         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20647         this.show();
20648     },
20649     
20650     onBlur : function()
20651     {
20652         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20653         
20654         var d = this.inputEl().getValue();
20655         
20656         this.setValue(d);
20657                 
20658         this.hide();
20659     },
20660     
20661     show : function()
20662     {
20663         this.picker().show();
20664         this.picker().select('>.datepicker-months', true).first().show();
20665         this.update();
20666         this.place();
20667         
20668         this.fireEvent('show', this, this.date);
20669     },
20670     
20671     hide : function()
20672     {
20673         if(this.isInline) {
20674             return;
20675         }
20676         this.picker().hide();
20677         this.fireEvent('hide', this, this.date);
20678         
20679     },
20680     
20681     onMousedown: function(e)
20682     {
20683         e.stopPropagation();
20684         e.preventDefault();
20685     },
20686     
20687     keyup: function(e)
20688     {
20689         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20690         this.update();
20691     },
20692
20693     fireKey: function(e)
20694     {
20695         if (!this.picker().isVisible()){
20696             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20697                 this.show();
20698             }
20699             return;
20700         }
20701         
20702         var dir;
20703         
20704         switch(e.keyCode){
20705             case 27: // escape
20706                 this.hide();
20707                 e.preventDefault();
20708                 break;
20709             case 37: // left
20710             case 39: // right
20711                 dir = e.keyCode == 37 ? -1 : 1;
20712                 
20713                 this.vIndex = this.vIndex + dir;
20714                 
20715                 if(this.vIndex < 0){
20716                     this.vIndex = 0;
20717                 }
20718                 
20719                 if(this.vIndex > 11){
20720                     this.vIndex = 11;
20721                 }
20722                 
20723                 if(isNaN(this.vIndex)){
20724                     this.vIndex = 0;
20725                 }
20726                 
20727                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20728                 
20729                 break;
20730             case 38: // up
20731             case 40: // down
20732                 
20733                 dir = e.keyCode == 38 ? -1 : 1;
20734                 
20735                 this.vIndex = this.vIndex + dir * 4;
20736                 
20737                 if(this.vIndex < 0){
20738                     this.vIndex = 0;
20739                 }
20740                 
20741                 if(this.vIndex > 11){
20742                     this.vIndex = 11;
20743                 }
20744                 
20745                 if(isNaN(this.vIndex)){
20746                     this.vIndex = 0;
20747                 }
20748                 
20749                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20750                 break;
20751                 
20752             case 13: // enter
20753                 
20754                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20755                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20756                 }
20757                 
20758                 this.hide();
20759                 e.preventDefault();
20760                 break;
20761             case 9: // tab
20762                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20763                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20764                 }
20765                 this.hide();
20766                 break;
20767             case 16: // shift
20768             case 17: // ctrl
20769             case 18: // alt
20770                 break;
20771             default :
20772                 this.hide();
20773                 
20774         }
20775     },
20776     
20777     remove: function() 
20778     {
20779         this.picker().remove();
20780     }
20781    
20782 });
20783
20784 Roo.apply(Roo.bootstrap.MonthField,  {
20785     
20786     content : {
20787         tag: 'tbody',
20788         cn: [
20789         {
20790             tag: 'tr',
20791             cn: [
20792             {
20793                 tag: 'td',
20794                 colspan: '7'
20795             }
20796             ]
20797         }
20798         ]
20799     },
20800     
20801     dates:{
20802         en: {
20803             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20804             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20805         }
20806     }
20807 });
20808
20809 Roo.apply(Roo.bootstrap.MonthField,  {
20810   
20811     template : {
20812         tag: 'div',
20813         cls: 'datepicker dropdown-menu roo-dynamic',
20814         cn: [
20815             {
20816                 tag: 'div',
20817                 cls: 'datepicker-months',
20818                 cn: [
20819                 {
20820                     tag: 'table',
20821                     cls: 'table-condensed',
20822                     cn:[
20823                         Roo.bootstrap.DateField.content
20824                     ]
20825                 }
20826                 ]
20827             }
20828         ]
20829     }
20830 });
20831
20832  
20833
20834  
20835  /*
20836  * - LGPL
20837  *
20838  * CheckBox
20839  * 
20840  */
20841
20842 /**
20843  * @class Roo.bootstrap.CheckBox
20844  * @extends Roo.bootstrap.Input
20845  * Bootstrap CheckBox class
20846  * 
20847  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20848  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20849  * @cfg {String} boxLabel The text that appears beside the checkbox
20850  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20851  * @cfg {Boolean} checked initnal the element
20852  * @cfg {Boolean} inline inline the element (default false)
20853  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20854  * @cfg {String} tooltip label tooltip
20855  * 
20856  * @constructor
20857  * Create a new CheckBox
20858  * @param {Object} config The config object
20859  */
20860
20861 Roo.bootstrap.CheckBox = function(config){
20862     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20863    
20864     this.addEvents({
20865         /**
20866         * @event check
20867         * Fires when the element is checked or unchecked.
20868         * @param {Roo.bootstrap.CheckBox} this This input
20869         * @param {Boolean} checked The new checked value
20870         */
20871        check : true,
20872        /**
20873         * @event click
20874         * Fires when the element is click.
20875         * @param {Roo.bootstrap.CheckBox} this This input
20876         */
20877        click : true
20878     });
20879     
20880 };
20881
20882 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20883   
20884     inputType: 'checkbox',
20885     inputValue: 1,
20886     valueOff: 0,
20887     boxLabel: false,
20888     checked: false,
20889     weight : false,
20890     inline: false,
20891     tooltip : '',
20892     
20893     getAutoCreate : function()
20894     {
20895         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20896         
20897         var id = Roo.id();
20898         
20899         var cfg = {};
20900         
20901         cfg.cls = 'form-group ' + this.inputType; //input-group
20902         
20903         if(this.inline){
20904             cfg.cls += ' ' + this.inputType + '-inline';
20905         }
20906         
20907         var input =  {
20908             tag: 'input',
20909             id : id,
20910             type : this.inputType,
20911             value : this.inputValue,
20912             cls : 'roo-' + this.inputType, //'form-box',
20913             placeholder : this.placeholder || ''
20914             
20915         };
20916         
20917         if(this.inputType != 'radio'){
20918             var hidden =  {
20919                 tag: 'input',
20920                 type : 'hidden',
20921                 cls : 'roo-hidden-value',
20922                 value : this.checked ? this.inputValue : this.valueOff
20923             };
20924         }
20925         
20926             
20927         if (this.weight) { // Validity check?
20928             cfg.cls += " " + this.inputType + "-" + this.weight;
20929         }
20930         
20931         if (this.disabled) {
20932             input.disabled=true;
20933         }
20934         
20935         if(this.checked){
20936             input.checked = this.checked;
20937         }
20938         
20939         if (this.name) {
20940             
20941             input.name = this.name;
20942             
20943             if(this.inputType != 'radio'){
20944                 hidden.name = this.name;
20945                 input.name = '_hidden_' + this.name;
20946             }
20947         }
20948         
20949         if (this.size) {
20950             input.cls += ' input-' + this.size;
20951         }
20952         
20953         var settings=this;
20954         
20955         ['xs','sm','md','lg'].map(function(size){
20956             if (settings[size]) {
20957                 cfg.cls += ' col-' + size + '-' + settings[size];
20958             }
20959         });
20960         
20961         var inputblock = input;
20962          
20963         if (this.before || this.after) {
20964             
20965             inputblock = {
20966                 cls : 'input-group',
20967                 cn :  [] 
20968             };
20969             
20970             if (this.before) {
20971                 inputblock.cn.push({
20972                     tag :'span',
20973                     cls : 'input-group-addon',
20974                     html : this.before
20975                 });
20976             }
20977             
20978             inputblock.cn.push(input);
20979             
20980             if(this.inputType != 'radio'){
20981                 inputblock.cn.push(hidden);
20982             }
20983             
20984             if (this.after) {
20985                 inputblock.cn.push({
20986                     tag :'span',
20987                     cls : 'input-group-addon',
20988                     html : this.after
20989                 });
20990             }
20991             
20992         }
20993         
20994         if (align ==='left' && this.fieldLabel.length) {
20995 //                Roo.log("left and has label");
20996             cfg.cn = [
20997                 {
20998                     tag: 'label',
20999                     'for' :  id,
21000                     cls : 'control-label',
21001                     html : this.fieldLabel
21002                 },
21003                 {
21004                     cls : "", 
21005                     cn: [
21006                         inputblock
21007                     ]
21008                 }
21009             ];
21010             
21011             if(this.labelWidth > 12){
21012                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
21013             }
21014             
21015             if(this.labelWidth < 13 && this.labelmd == 0){
21016                 this.labelmd = this.labelWidth;
21017             }
21018             
21019             if(this.labellg > 0){
21020                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
21021                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
21022             }
21023             
21024             if(this.labelmd > 0){
21025                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
21026                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
21027             }
21028             
21029             if(this.labelsm > 0){
21030                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
21031                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
21032             }
21033             
21034             if(this.labelxs > 0){
21035                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
21036                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
21037             }
21038             
21039         } else if ( this.fieldLabel.length) {
21040 //                Roo.log(" label");
21041                 cfg.cn = [
21042                    
21043                     {
21044                         tag: this.boxLabel ? 'span' : 'label',
21045                         'for': id,
21046                         cls: 'control-label box-input-label',
21047                         //cls : 'input-group-addon',
21048                         html : this.fieldLabel
21049                     },
21050                     
21051                     inputblock
21052                     
21053                 ];
21054
21055         } else {
21056             
21057 //                Roo.log(" no label && no align");
21058                 cfg.cn = [  inputblock ] ;
21059                 
21060                 
21061         }
21062         
21063         if(this.boxLabel){
21064              var boxLabelCfg = {
21065                 tag: 'label',
21066                 //'for': id, // box label is handled by onclick - so no for...
21067                 cls: 'box-label',
21068                 html: this.boxLabel
21069             };
21070             
21071             if(this.tooltip){
21072                 boxLabelCfg.tooltip = this.tooltip;
21073             }
21074              
21075             cfg.cn.push(boxLabelCfg);
21076         }
21077         
21078         if(this.inputType != 'radio'){
21079             cfg.cn.push(hidden);
21080         }
21081         
21082         return cfg;
21083         
21084     },
21085     
21086     /**
21087      * return the real input element.
21088      */
21089     inputEl: function ()
21090     {
21091         return this.el.select('input.roo-' + this.inputType,true).first();
21092     },
21093     hiddenEl: function ()
21094     {
21095         return this.el.select('input.roo-hidden-value',true).first();
21096     },
21097     
21098     labelEl: function()
21099     {
21100         return this.el.select('label.control-label',true).first();
21101     },
21102     /* depricated... */
21103     
21104     label: function()
21105     {
21106         return this.labelEl();
21107     },
21108     
21109     boxLabelEl: function()
21110     {
21111         return this.el.select('label.box-label',true).first();
21112     },
21113     
21114     initEvents : function()
21115     {
21116 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
21117         
21118         this.inputEl().on('click', this.onClick,  this);
21119         
21120         if (this.boxLabel) { 
21121             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
21122         }
21123         
21124         this.startValue = this.getValue();
21125         
21126         if(this.groupId){
21127             Roo.bootstrap.CheckBox.register(this);
21128         }
21129     },
21130     
21131     onClick : function(e)
21132     {   
21133         if(this.fireEvent('click', this, e) !== false){
21134             this.setChecked(!this.checked);
21135         }
21136         
21137     },
21138     
21139     setChecked : function(state,suppressEvent)
21140     {
21141         this.startValue = this.getValue();
21142
21143         if(this.inputType == 'radio'){
21144             
21145             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21146                 e.dom.checked = false;
21147             });
21148             
21149             this.inputEl().dom.checked = true;
21150             
21151             this.inputEl().dom.value = this.inputValue;
21152             
21153             if(suppressEvent !== true){
21154                 this.fireEvent('check', this, true);
21155             }
21156             
21157             this.validate();
21158             
21159             return;
21160         }
21161         
21162         this.checked = state;
21163         
21164         this.inputEl().dom.checked = state;
21165         
21166         
21167         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
21168         
21169         if(suppressEvent !== true){
21170             this.fireEvent('check', this, state);
21171         }
21172         
21173         this.validate();
21174     },
21175     
21176     getValue : function()
21177     {
21178         if(this.inputType == 'radio'){
21179             return this.getGroupValue();
21180         }
21181         
21182         return this.hiddenEl().dom.value;
21183         
21184     },
21185     
21186     getGroupValue : function()
21187     {
21188         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21189             return '';
21190         }
21191         
21192         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21193     },
21194     
21195     setValue : function(v,suppressEvent)
21196     {
21197         if(this.inputType == 'radio'){
21198             this.setGroupValue(v, suppressEvent);
21199             return;
21200         }
21201         
21202         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21203         
21204         this.validate();
21205     },
21206     
21207     setGroupValue : function(v, suppressEvent)
21208     {
21209         this.startValue = this.getValue();
21210         
21211         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21212             e.dom.checked = false;
21213             
21214             if(e.dom.value == v){
21215                 e.dom.checked = true;
21216             }
21217         });
21218         
21219         if(suppressEvent !== true){
21220             this.fireEvent('check', this, true);
21221         }
21222
21223         this.validate();
21224         
21225         return;
21226     },
21227     
21228     validate : function()
21229     {
21230         if(this.getVisibilityEl().hasClass('hidden')){
21231             return true;
21232         }
21233         
21234         if(
21235                 this.disabled || 
21236                 (this.inputType == 'radio' && this.validateRadio()) ||
21237                 (this.inputType == 'checkbox' && this.validateCheckbox())
21238         ){
21239             this.markValid();
21240             return true;
21241         }
21242         
21243         this.markInvalid();
21244         return false;
21245     },
21246     
21247     validateRadio : function()
21248     {
21249         if(this.getVisibilityEl().hasClass('hidden')){
21250             return true;
21251         }
21252         
21253         if(this.allowBlank){
21254             return true;
21255         }
21256         
21257         var valid = false;
21258         
21259         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21260             if(!e.dom.checked){
21261                 return;
21262             }
21263             
21264             valid = true;
21265             
21266             return false;
21267         });
21268         
21269         return valid;
21270     },
21271     
21272     validateCheckbox : function()
21273     {
21274         if(!this.groupId){
21275             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21276             //return (this.getValue() == this.inputValue) ? true : false;
21277         }
21278         
21279         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21280         
21281         if(!group){
21282             return false;
21283         }
21284         
21285         var r = false;
21286         
21287         for(var i in group){
21288             if(group[i].el.isVisible(true)){
21289                 r = false;
21290                 break;
21291             }
21292             
21293             r = true;
21294         }
21295         
21296         for(var i in group){
21297             if(r){
21298                 break;
21299             }
21300             
21301             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21302         }
21303         
21304         return r;
21305     },
21306     
21307     /**
21308      * Mark this field as valid
21309      */
21310     markValid : function()
21311     {
21312         var _this = this;
21313         
21314         this.fireEvent('valid', this);
21315         
21316         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21317         
21318         if(this.groupId){
21319             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21320         }
21321         
21322         if(label){
21323             label.markValid();
21324         }
21325
21326         if(this.inputType == 'radio'){
21327             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21328                 var fg = e.findParent('.form-group', false, true);
21329                 if (Roo.bootstrap.version == 3) {
21330                     fg.removeClass([_this.invalidClass, _this.validClass]);
21331                     fg.addClass(_this.validClass);
21332                 } else {
21333                     fg.removeClass(['is-valid', 'is-invalid']);
21334                     fg.addClass('is-valid');
21335                 }
21336             });
21337             
21338             return;
21339         }
21340
21341         if(!this.groupId){
21342             var fg = this.el.findParent('.form-group', false, true);
21343             if (Roo.bootstrap.version == 3) {
21344                 fg.removeClass([this.invalidClass, this.validClass]);
21345                 fg.addClass(this.validClass);
21346             } else {
21347                 fg.removeClass(['is-valid', 'is-invalid']);
21348                 fg.addClass('is-valid');
21349             }
21350             return;
21351         }
21352         
21353         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21354         
21355         if(!group){
21356             return;
21357         }
21358         
21359         for(var i in group){
21360             var fg = group[i].el.findParent('.form-group', false, true);
21361             if (Roo.bootstrap.version == 3) {
21362                 fg.removeClass([this.invalidClass, this.validClass]);
21363                 fg.addClass(this.validClass);
21364             } else {
21365                 fg.removeClass(['is-valid', 'is-invalid']);
21366                 fg.addClass('is-valid');
21367             }
21368         }
21369     },
21370     
21371      /**
21372      * Mark this field as invalid
21373      * @param {String} msg The validation message
21374      */
21375     markInvalid : function(msg)
21376     {
21377         if(this.allowBlank){
21378             return;
21379         }
21380         
21381         var _this = this;
21382         
21383         this.fireEvent('invalid', this, msg);
21384         
21385         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21386         
21387         if(this.groupId){
21388             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21389         }
21390         
21391         if(label){
21392             label.markInvalid();
21393         }
21394             
21395         if(this.inputType == 'radio'){
21396             
21397             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21398                 var fg = e.findParent('.form-group', false, true);
21399                 if (Roo.bootstrap.version == 3) {
21400                     fg.removeClass([_this.invalidClass, _this.validClass]);
21401                     fg.addClass(_this.invalidClass);
21402                 } else {
21403                     fg.removeClass(['is-invalid', 'is-valid']);
21404                     fg.addClass('is-invalid');
21405                 }
21406             });
21407             
21408             return;
21409         }
21410         
21411         if(!this.groupId){
21412             var fg = this.el.findParent('.form-group', false, true);
21413             if (Roo.bootstrap.version == 3) {
21414                 fg.removeClass([_this.invalidClass, _this.validClass]);
21415                 fg.addClass(_this.invalidClass);
21416             } else {
21417                 fg.removeClass(['is-invalid', 'is-valid']);
21418                 fg.addClass('is-invalid');
21419             }
21420             return;
21421         }
21422         
21423         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21424         
21425         if(!group){
21426             return;
21427         }
21428         
21429         for(var i in group){
21430             var fg = group[i].el.findParent('.form-group', false, true);
21431             if (Roo.bootstrap.version == 3) {
21432                 fg.removeClass([_this.invalidClass, _this.validClass]);
21433                 fg.addClass(_this.invalidClass);
21434             } else {
21435                 fg.removeClass(['is-invalid', 'is-valid']);
21436                 fg.addClass('is-invalid');
21437             }
21438         }
21439         
21440     },
21441     
21442     clearInvalid : function()
21443     {
21444         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21445         
21446         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21447         
21448         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21449         
21450         if (label && label.iconEl) {
21451             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
21452             label.iconEl.removeClass(['is-invalid', 'is-valid']);
21453         }
21454     },
21455     
21456     disable : function()
21457     {
21458         if(this.inputType != 'radio'){
21459             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21460             return;
21461         }
21462         
21463         var _this = this;
21464         
21465         if(this.rendered){
21466             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21467                 _this.getActionEl().addClass(this.disabledClass);
21468                 e.dom.disabled = true;
21469             });
21470         }
21471         
21472         this.disabled = true;
21473         this.fireEvent("disable", this);
21474         return this;
21475     },
21476
21477     enable : function()
21478     {
21479         if(this.inputType != 'radio'){
21480             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21481             return;
21482         }
21483         
21484         var _this = this;
21485         
21486         if(this.rendered){
21487             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21488                 _this.getActionEl().removeClass(this.disabledClass);
21489                 e.dom.disabled = false;
21490             });
21491         }
21492         
21493         this.disabled = false;
21494         this.fireEvent("enable", this);
21495         return this;
21496     },
21497     
21498     setBoxLabel : function(v)
21499     {
21500         this.boxLabel = v;
21501         
21502         if(this.rendered){
21503             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21504         }
21505     }
21506
21507 });
21508
21509 Roo.apply(Roo.bootstrap.CheckBox, {
21510     
21511     groups: {},
21512     
21513      /**
21514     * register a CheckBox Group
21515     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21516     */
21517     register : function(checkbox)
21518     {
21519         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21520             this.groups[checkbox.groupId] = {};
21521         }
21522         
21523         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21524             return;
21525         }
21526         
21527         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21528         
21529     },
21530     /**
21531     * fetch a CheckBox Group based on the group ID
21532     * @param {string} the group ID
21533     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21534     */
21535     get: function(groupId) {
21536         if (typeof(this.groups[groupId]) == 'undefined') {
21537             return false;
21538         }
21539         
21540         return this.groups[groupId] ;
21541     }
21542     
21543     
21544 });
21545 /*
21546  * - LGPL
21547  *
21548  * RadioItem
21549  * 
21550  */
21551
21552 /**
21553  * @class Roo.bootstrap.Radio
21554  * @extends Roo.bootstrap.Component
21555  * Bootstrap Radio class
21556  * @cfg {String} boxLabel - the label associated
21557  * @cfg {String} value - the value of radio
21558  * 
21559  * @constructor
21560  * Create a new Radio
21561  * @param {Object} config The config object
21562  */
21563 Roo.bootstrap.Radio = function(config){
21564     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21565     
21566 };
21567
21568 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21569     
21570     boxLabel : '',
21571     
21572     value : '',
21573     
21574     getAutoCreate : function()
21575     {
21576         var cfg = {
21577             tag : 'div',
21578             cls : 'form-group radio',
21579             cn : [
21580                 {
21581                     tag : 'label',
21582                     cls : 'box-label',
21583                     html : this.boxLabel
21584                 }
21585             ]
21586         };
21587         
21588         return cfg;
21589     },
21590     
21591     initEvents : function() 
21592     {
21593         this.parent().register(this);
21594         
21595         this.el.on('click', this.onClick, this);
21596         
21597     },
21598     
21599     onClick : function(e)
21600     {
21601         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21602             this.setChecked(true);
21603         }
21604     },
21605     
21606     setChecked : function(state, suppressEvent)
21607     {
21608         this.parent().setValue(this.value, suppressEvent);
21609         
21610     },
21611     
21612     setBoxLabel : function(v)
21613     {
21614         this.boxLabel = v;
21615         
21616         if(this.rendered){
21617             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21618         }
21619     }
21620     
21621 });
21622  
21623
21624  /*
21625  * - LGPL
21626  *
21627  * Input
21628  * 
21629  */
21630
21631 /**
21632  * @class Roo.bootstrap.SecurePass
21633  * @extends Roo.bootstrap.Input
21634  * Bootstrap SecurePass class
21635  *
21636  * 
21637  * @constructor
21638  * Create a new SecurePass
21639  * @param {Object} config The config object
21640  */
21641  
21642 Roo.bootstrap.SecurePass = function (config) {
21643     // these go here, so the translation tool can replace them..
21644     this.errors = {
21645         PwdEmpty: "Please type a password, and then retype it to confirm.",
21646         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21647         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21648         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21649         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21650         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21651         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21652         TooWeak: "Your password is Too Weak."
21653     },
21654     this.meterLabel = "Password strength:";
21655     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21656     this.meterClass = [
21657         "roo-password-meter-tooweak", 
21658         "roo-password-meter-weak", 
21659         "roo-password-meter-medium", 
21660         "roo-password-meter-strong", 
21661         "roo-password-meter-grey"
21662     ];
21663     
21664     this.errors = {};
21665     
21666     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21667 }
21668
21669 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21670     /**
21671      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21672      * {
21673      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21674      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21675      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21676      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21677      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21678      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21679      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21680      * })
21681      */
21682     // private
21683     
21684     meterWidth: 300,
21685     errorMsg :'',    
21686     errors: false,
21687     imageRoot: '/',
21688     /**
21689      * @cfg {String/Object} Label for the strength meter (defaults to
21690      * 'Password strength:')
21691      */
21692     // private
21693     meterLabel: '',
21694     /**
21695      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21696      * ['Weak', 'Medium', 'Strong'])
21697      */
21698     // private    
21699     pwdStrengths: false,    
21700     // private
21701     strength: 0,
21702     // private
21703     _lastPwd: null,
21704     // private
21705     kCapitalLetter: 0,
21706     kSmallLetter: 1,
21707     kDigit: 2,
21708     kPunctuation: 3,
21709     
21710     insecure: false,
21711     // private
21712     initEvents: function ()
21713     {
21714         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21715
21716         if (this.el.is('input[type=password]') && Roo.isSafari) {
21717             this.el.on('keydown', this.SafariOnKeyDown, this);
21718         }
21719
21720         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21721     },
21722     // private
21723     onRender: function (ct, position)
21724     {
21725         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21726         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21727         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21728
21729         this.trigger.createChild({
21730                    cn: [
21731                     {
21732                     //id: 'PwdMeter',
21733                     tag: 'div',
21734                     cls: 'roo-password-meter-grey col-xs-12',
21735                     style: {
21736                         //width: 0,
21737                         //width: this.meterWidth + 'px'                                                
21738                         }
21739                     },
21740                     {                            
21741                          cls: 'roo-password-meter-text'                          
21742                     }
21743                 ]            
21744         });
21745
21746          
21747         if (this.hideTrigger) {
21748             this.trigger.setDisplayed(false);
21749         }
21750         this.setSize(this.width || '', this.height || '');
21751     },
21752     // private
21753     onDestroy: function ()
21754     {
21755         if (this.trigger) {
21756             this.trigger.removeAllListeners();
21757             this.trigger.remove();
21758         }
21759         if (this.wrap) {
21760             this.wrap.remove();
21761         }
21762         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21763     },
21764     // private
21765     checkStrength: function ()
21766     {
21767         var pwd = this.inputEl().getValue();
21768         if (pwd == this._lastPwd) {
21769             return;
21770         }
21771
21772         var strength;
21773         if (this.ClientSideStrongPassword(pwd)) {
21774             strength = 3;
21775         } else if (this.ClientSideMediumPassword(pwd)) {
21776             strength = 2;
21777         } else if (this.ClientSideWeakPassword(pwd)) {
21778             strength = 1;
21779         } else {
21780             strength = 0;
21781         }
21782         
21783         Roo.log('strength1: ' + strength);
21784         
21785         //var pm = this.trigger.child('div/div/div').dom;
21786         var pm = this.trigger.child('div/div');
21787         pm.removeClass(this.meterClass);
21788         pm.addClass(this.meterClass[strength]);
21789                 
21790         
21791         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21792                 
21793         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21794         
21795         this._lastPwd = pwd;
21796     },
21797     reset: function ()
21798     {
21799         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21800         
21801         this._lastPwd = '';
21802         
21803         var pm = this.trigger.child('div/div');
21804         pm.removeClass(this.meterClass);
21805         pm.addClass('roo-password-meter-grey');        
21806         
21807         
21808         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21809         
21810         pt.innerHTML = '';
21811         this.inputEl().dom.type='password';
21812     },
21813     // private
21814     validateValue: function (value)
21815     {
21816         
21817         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21818             return false;
21819         }
21820         if (value.length == 0) {
21821             if (this.allowBlank) {
21822                 this.clearInvalid();
21823                 return true;
21824             }
21825
21826             this.markInvalid(this.errors.PwdEmpty);
21827             this.errorMsg = this.errors.PwdEmpty;
21828             return false;
21829         }
21830         
21831         if(this.insecure){
21832             return true;
21833         }
21834         
21835         if ('[\x21-\x7e]*'.match(value)) {
21836             this.markInvalid(this.errors.PwdBadChar);
21837             this.errorMsg = this.errors.PwdBadChar;
21838             return false;
21839         }
21840         if (value.length < 6) {
21841             this.markInvalid(this.errors.PwdShort);
21842             this.errorMsg = this.errors.PwdShort;
21843             return false;
21844         }
21845         if (value.length > 16) {
21846             this.markInvalid(this.errors.PwdLong);
21847             this.errorMsg = this.errors.PwdLong;
21848             return false;
21849         }
21850         var strength;
21851         if (this.ClientSideStrongPassword(value)) {
21852             strength = 3;
21853         } else if (this.ClientSideMediumPassword(value)) {
21854             strength = 2;
21855         } else if (this.ClientSideWeakPassword(value)) {
21856             strength = 1;
21857         } else {
21858             strength = 0;
21859         }
21860
21861         
21862         if (strength < 2) {
21863             //this.markInvalid(this.errors.TooWeak);
21864             this.errorMsg = this.errors.TooWeak;
21865             //return false;
21866         }
21867         
21868         
21869         console.log('strength2: ' + strength);
21870         
21871         //var pm = this.trigger.child('div/div/div').dom;
21872         
21873         var pm = this.trigger.child('div/div');
21874         pm.removeClass(this.meterClass);
21875         pm.addClass(this.meterClass[strength]);
21876                 
21877         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21878                 
21879         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21880         
21881         this.errorMsg = ''; 
21882         return true;
21883     },
21884     // private
21885     CharacterSetChecks: function (type)
21886     {
21887         this.type = type;
21888         this.fResult = false;
21889     },
21890     // private
21891     isctype: function (character, type)
21892     {
21893         switch (type) {  
21894             case this.kCapitalLetter:
21895                 if (character >= 'A' && character <= 'Z') {
21896                     return true;
21897                 }
21898                 break;
21899             
21900             case this.kSmallLetter:
21901                 if (character >= 'a' && character <= 'z') {
21902                     return true;
21903                 }
21904                 break;
21905             
21906             case this.kDigit:
21907                 if (character >= '0' && character <= '9') {
21908                     return true;
21909                 }
21910                 break;
21911             
21912             case this.kPunctuation:
21913                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21914                     return true;
21915                 }
21916                 break;
21917             
21918             default:
21919                 return false;
21920         }
21921
21922     },
21923     // private
21924     IsLongEnough: function (pwd, size)
21925     {
21926         return !(pwd == null || isNaN(size) || pwd.length < size);
21927     },
21928     // private
21929     SpansEnoughCharacterSets: function (word, nb)
21930     {
21931         if (!this.IsLongEnough(word, nb))
21932         {
21933             return false;
21934         }
21935
21936         var characterSetChecks = new Array(
21937             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21938             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21939         );
21940         
21941         for (var index = 0; index < word.length; ++index) {
21942             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21943                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21944                     characterSetChecks[nCharSet].fResult = true;
21945                     break;
21946                 }
21947             }
21948         }
21949
21950         var nCharSets = 0;
21951         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21952             if (characterSetChecks[nCharSet].fResult) {
21953                 ++nCharSets;
21954             }
21955         }
21956
21957         if (nCharSets < nb) {
21958             return false;
21959         }
21960         return true;
21961     },
21962     // private
21963     ClientSideStrongPassword: function (pwd)
21964     {
21965         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21966     },
21967     // private
21968     ClientSideMediumPassword: function (pwd)
21969     {
21970         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21971     },
21972     // private
21973     ClientSideWeakPassword: function (pwd)
21974     {
21975         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21976     }
21977           
21978 })//<script type="text/javascript">
21979
21980 /*
21981  * Based  Ext JS Library 1.1.1
21982  * Copyright(c) 2006-2007, Ext JS, LLC.
21983  * LGPL
21984  *
21985  */
21986  
21987 /**
21988  * @class Roo.HtmlEditorCore
21989  * @extends Roo.Component
21990  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21991  *
21992  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21993  */
21994
21995 Roo.HtmlEditorCore = function(config){
21996     
21997     
21998     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21999     
22000     
22001     this.addEvents({
22002         /**
22003          * @event initialize
22004          * Fires when the editor is fully initialized (including the iframe)
22005          * @param {Roo.HtmlEditorCore} this
22006          */
22007         initialize: true,
22008         /**
22009          * @event activate
22010          * Fires when the editor is first receives the focus. Any insertion must wait
22011          * until after this event.
22012          * @param {Roo.HtmlEditorCore} this
22013          */
22014         activate: true,
22015          /**
22016          * @event beforesync
22017          * Fires before the textarea is updated with content from the editor iframe. Return false
22018          * to cancel the sync.
22019          * @param {Roo.HtmlEditorCore} this
22020          * @param {String} html
22021          */
22022         beforesync: true,
22023          /**
22024          * @event beforepush
22025          * Fires before the iframe editor is updated with content from the textarea. Return false
22026          * to cancel the push.
22027          * @param {Roo.HtmlEditorCore} this
22028          * @param {String} html
22029          */
22030         beforepush: true,
22031          /**
22032          * @event sync
22033          * Fires when the textarea is updated with content from the editor iframe.
22034          * @param {Roo.HtmlEditorCore} this
22035          * @param {String} html
22036          */
22037         sync: true,
22038          /**
22039          * @event push
22040          * Fires when the iframe editor is updated with content from the textarea.
22041          * @param {Roo.HtmlEditorCore} this
22042          * @param {String} html
22043          */
22044         push: true,
22045         
22046         /**
22047          * @event editorevent
22048          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22049          * @param {Roo.HtmlEditorCore} this
22050          */
22051         editorevent: true
22052         
22053     });
22054     
22055     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
22056     
22057     // defaults : white / black...
22058     this.applyBlacklists();
22059     
22060     
22061     
22062 };
22063
22064
22065 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
22066
22067
22068      /**
22069      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
22070      */
22071     
22072     owner : false,
22073     
22074      /**
22075      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22076      *                        Roo.resizable.
22077      */
22078     resizable : false,
22079      /**
22080      * @cfg {Number} height (in pixels)
22081      */   
22082     height: 300,
22083    /**
22084      * @cfg {Number} width (in pixels)
22085      */   
22086     width: 500,
22087     
22088     /**
22089      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22090      * 
22091      */
22092     stylesheets: false,
22093     
22094     // id of frame..
22095     frameId: false,
22096     
22097     // private properties
22098     validationEvent : false,
22099     deferHeight: true,
22100     initialized : false,
22101     activated : false,
22102     sourceEditMode : false,
22103     onFocus : Roo.emptyFn,
22104     iframePad:3,
22105     hideMode:'offsets',
22106     
22107     clearUp: true,
22108     
22109     // blacklist + whitelisted elements..
22110     black: false,
22111     white: false,
22112      
22113     bodyCls : '',
22114
22115     /**
22116      * Protected method that will not generally be called directly. It
22117      * is called when the editor initializes the iframe with HTML contents. Override this method if you
22118      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
22119      */
22120     getDocMarkup : function(){
22121         // body styles..
22122         var st = '';
22123         
22124         // inherit styels from page...?? 
22125         if (this.stylesheets === false) {
22126             
22127             Roo.get(document.head).select('style').each(function(node) {
22128                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22129             });
22130             
22131             Roo.get(document.head).select('link').each(function(node) { 
22132                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22133             });
22134             
22135         } else if (!this.stylesheets.length) {
22136                 // simple..
22137                 st = '<style type="text/css">' +
22138                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22139                    '</style>';
22140         } else { 
22141             st = '<style type="text/css">' +
22142                     this.stylesheets +
22143                 '</style>';
22144         }
22145         
22146         st +=  '<style type="text/css">' +
22147             'IMG { cursor: pointer } ' +
22148         '</style>';
22149
22150         var cls = 'roo-htmleditor-body';
22151         
22152         if(this.bodyCls.length){
22153             cls += ' ' + this.bodyCls;
22154         }
22155         
22156         return '<html><head>' + st  +
22157             //<style type="text/css">' +
22158             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22159             //'</style>' +
22160             ' </head><body class="' +  cls + '"></body></html>';
22161     },
22162
22163     // private
22164     onRender : function(ct, position)
22165     {
22166         var _t = this;
22167         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
22168         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
22169         
22170         
22171         this.el.dom.style.border = '0 none';
22172         this.el.dom.setAttribute('tabIndex', -1);
22173         this.el.addClass('x-hidden hide');
22174         
22175         
22176         
22177         if(Roo.isIE){ // fix IE 1px bogus margin
22178             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
22179         }
22180        
22181         
22182         this.frameId = Roo.id();
22183         
22184          
22185         
22186         var iframe = this.owner.wrap.createChild({
22187             tag: 'iframe',
22188             cls: 'form-control', // bootstrap..
22189             id: this.frameId,
22190             name: this.frameId,
22191             frameBorder : 'no',
22192             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
22193         }, this.el
22194         );
22195         
22196         
22197         this.iframe = iframe.dom;
22198
22199          this.assignDocWin();
22200         
22201         this.doc.designMode = 'on';
22202        
22203         this.doc.open();
22204         this.doc.write(this.getDocMarkup());
22205         this.doc.close();
22206
22207         
22208         var task = { // must defer to wait for browser to be ready
22209             run : function(){
22210                 //console.log("run task?" + this.doc.readyState);
22211                 this.assignDocWin();
22212                 if(this.doc.body || this.doc.readyState == 'complete'){
22213                     try {
22214                         this.doc.designMode="on";
22215                     } catch (e) {
22216                         return;
22217                     }
22218                     Roo.TaskMgr.stop(task);
22219                     this.initEditor.defer(10, this);
22220                 }
22221             },
22222             interval : 10,
22223             duration: 10000,
22224             scope: this
22225         };
22226         Roo.TaskMgr.start(task);
22227
22228     },
22229
22230     // private
22231     onResize : function(w, h)
22232     {
22233          Roo.log('resize: ' +w + ',' + h );
22234         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22235         if(!this.iframe){
22236             return;
22237         }
22238         if(typeof w == 'number'){
22239             
22240             this.iframe.style.width = w + 'px';
22241         }
22242         if(typeof h == 'number'){
22243             
22244             this.iframe.style.height = h + 'px';
22245             if(this.doc){
22246                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22247             }
22248         }
22249         
22250     },
22251
22252     /**
22253      * Toggles the editor between standard and source edit mode.
22254      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22255      */
22256     toggleSourceEdit : function(sourceEditMode){
22257         
22258         this.sourceEditMode = sourceEditMode === true;
22259         
22260         if(this.sourceEditMode){
22261  
22262             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
22263             
22264         }else{
22265             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22266             //this.iframe.className = '';
22267             this.deferFocus();
22268         }
22269         //this.setSize(this.owner.wrap.getSize());
22270         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22271     },
22272
22273     
22274   
22275
22276     /**
22277      * Protected method that will not generally be called directly. If you need/want
22278      * custom HTML cleanup, this is the method you should override.
22279      * @param {String} html The HTML to be cleaned
22280      * return {String} The cleaned HTML
22281      */
22282     cleanHtml : function(html){
22283         html = String(html);
22284         if(html.length > 5){
22285             if(Roo.isSafari){ // strip safari nonsense
22286                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22287             }
22288         }
22289         if(html == '&nbsp;'){
22290             html = '';
22291         }
22292         return html;
22293     },
22294
22295     /**
22296      * HTML Editor -> Textarea
22297      * Protected method that will not generally be called directly. Syncs the contents
22298      * of the editor iframe with the textarea.
22299      */
22300     syncValue : function(){
22301         if(this.initialized){
22302             var bd = (this.doc.body || this.doc.documentElement);
22303             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22304             var html = bd.innerHTML;
22305             if(Roo.isSafari){
22306                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22307                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22308                 if(m && m[1]){
22309                     html = '<div style="'+m[0]+'">' + html + '</div>';
22310                 }
22311             }
22312             html = this.cleanHtml(html);
22313             // fix up the special chars.. normaly like back quotes in word...
22314             // however we do not want to do this with chinese..
22315             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22316                 var cc = b.charCodeAt();
22317                 if (
22318                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22319                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22320                     (cc >= 0xf900 && cc < 0xfb00 )
22321                 ) {
22322                         return b;
22323                 }
22324                 return "&#"+cc+";" 
22325             });
22326             if(this.owner.fireEvent('beforesync', this, html) !== false){
22327                 this.el.dom.value = html;
22328                 this.owner.fireEvent('sync', this, html);
22329             }
22330         }
22331     },
22332
22333     /**
22334      * Protected method that will not generally be called directly. Pushes the value of the textarea
22335      * into the iframe editor.
22336      */
22337     pushValue : function(){
22338         if(this.initialized){
22339             var v = this.el.dom.value.trim();
22340             
22341 //            if(v.length < 1){
22342 //                v = '&#160;';
22343 //            }
22344             
22345             if(this.owner.fireEvent('beforepush', this, v) !== false){
22346                 var d = (this.doc.body || this.doc.documentElement);
22347                 d.innerHTML = v;
22348                 this.cleanUpPaste();
22349                 this.el.dom.value = d.innerHTML;
22350                 this.owner.fireEvent('push', this, v);
22351             }
22352         }
22353     },
22354
22355     // private
22356     deferFocus : function(){
22357         this.focus.defer(10, this);
22358     },
22359
22360     // doc'ed in Field
22361     focus : function(){
22362         if(this.win && !this.sourceEditMode){
22363             this.win.focus();
22364         }else{
22365             this.el.focus();
22366         }
22367     },
22368     
22369     assignDocWin: function()
22370     {
22371         var iframe = this.iframe;
22372         
22373          if(Roo.isIE){
22374             this.doc = iframe.contentWindow.document;
22375             this.win = iframe.contentWindow;
22376         } else {
22377 //            if (!Roo.get(this.frameId)) {
22378 //                return;
22379 //            }
22380 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22381 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22382             
22383             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22384                 return;
22385             }
22386             
22387             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22388             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22389         }
22390     },
22391     
22392     // private
22393     initEditor : function(){
22394         //console.log("INIT EDITOR");
22395         this.assignDocWin();
22396         
22397         
22398         
22399         this.doc.designMode="on";
22400         this.doc.open();
22401         this.doc.write(this.getDocMarkup());
22402         this.doc.close();
22403         
22404         var dbody = (this.doc.body || this.doc.documentElement);
22405         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22406         // this copies styles from the containing element into thsi one..
22407         // not sure why we need all of this..
22408         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22409         
22410         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22411         //ss['background-attachment'] = 'fixed'; // w3c
22412         dbody.bgProperties = 'fixed'; // ie
22413         //Roo.DomHelper.applyStyles(dbody, ss);
22414         Roo.EventManager.on(this.doc, {
22415             //'mousedown': this.onEditorEvent,
22416             'mouseup': this.onEditorEvent,
22417             'dblclick': this.onEditorEvent,
22418             'click': this.onEditorEvent,
22419             'keyup': this.onEditorEvent,
22420             buffer:100,
22421             scope: this
22422         });
22423         if(Roo.isGecko){
22424             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22425         }
22426         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22427             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22428         }
22429         this.initialized = true;
22430
22431         this.owner.fireEvent('initialize', this);
22432         this.pushValue();
22433     },
22434
22435     // private
22436     onDestroy : function(){
22437         
22438         
22439         
22440         if(this.rendered){
22441             
22442             //for (var i =0; i < this.toolbars.length;i++) {
22443             //    // fixme - ask toolbars for heights?
22444             //    this.toolbars[i].onDestroy();
22445            // }
22446             
22447             //this.wrap.dom.innerHTML = '';
22448             //this.wrap.remove();
22449         }
22450     },
22451
22452     // private
22453     onFirstFocus : function(){
22454         
22455         this.assignDocWin();
22456         
22457         
22458         this.activated = true;
22459          
22460     
22461         if(Roo.isGecko){ // prevent silly gecko errors
22462             this.win.focus();
22463             var s = this.win.getSelection();
22464             if(!s.focusNode || s.focusNode.nodeType != 3){
22465                 var r = s.getRangeAt(0);
22466                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22467                 r.collapse(true);
22468                 this.deferFocus();
22469             }
22470             try{
22471                 this.execCmd('useCSS', true);
22472                 this.execCmd('styleWithCSS', false);
22473             }catch(e){}
22474         }
22475         this.owner.fireEvent('activate', this);
22476     },
22477
22478     // private
22479     adjustFont: function(btn){
22480         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22481         //if(Roo.isSafari){ // safari
22482         //    adjust *= 2;
22483        // }
22484         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22485         if(Roo.isSafari){ // safari
22486             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22487             v =  (v < 10) ? 10 : v;
22488             v =  (v > 48) ? 48 : v;
22489             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22490             
22491         }
22492         
22493         
22494         v = Math.max(1, v+adjust);
22495         
22496         this.execCmd('FontSize', v  );
22497     },
22498
22499     onEditorEvent : function(e)
22500     {
22501         this.owner.fireEvent('editorevent', this, e);
22502       //  this.updateToolbar();
22503         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22504     },
22505
22506     insertTag : function(tg)
22507     {
22508         // could be a bit smarter... -> wrap the current selected tRoo..
22509         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22510             
22511             range = this.createRange(this.getSelection());
22512             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22513             wrappingNode.appendChild(range.extractContents());
22514             range.insertNode(wrappingNode);
22515
22516             return;
22517             
22518             
22519             
22520         }
22521         this.execCmd("formatblock",   tg);
22522         
22523     },
22524     
22525     insertText : function(txt)
22526     {
22527         
22528         
22529         var range = this.createRange();
22530         range.deleteContents();
22531                //alert(Sender.getAttribute('label'));
22532                
22533         range.insertNode(this.doc.createTextNode(txt));
22534     } ,
22535     
22536      
22537
22538     /**
22539      * Executes a Midas editor command on the editor document and performs necessary focus and
22540      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22541      * @param {String} cmd The Midas command
22542      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22543      */
22544     relayCmd : function(cmd, value){
22545         this.win.focus();
22546         this.execCmd(cmd, value);
22547         this.owner.fireEvent('editorevent', this);
22548         //this.updateToolbar();
22549         this.owner.deferFocus();
22550     },
22551
22552     /**
22553      * Executes a Midas editor command directly on the editor document.
22554      * For visual commands, you should use {@link #relayCmd} instead.
22555      * <b>This should only be called after the editor is initialized.</b>
22556      * @param {String} cmd The Midas command
22557      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22558      */
22559     execCmd : function(cmd, value){
22560         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22561         this.syncValue();
22562     },
22563  
22564  
22565    
22566     /**
22567      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22568      * to insert tRoo.
22569      * @param {String} text | dom node.. 
22570      */
22571     insertAtCursor : function(text)
22572     {
22573         
22574         if(!this.activated){
22575             return;
22576         }
22577         /*
22578         if(Roo.isIE){
22579             this.win.focus();
22580             var r = this.doc.selection.createRange();
22581             if(r){
22582                 r.collapse(true);
22583                 r.pasteHTML(text);
22584                 this.syncValue();
22585                 this.deferFocus();
22586             
22587             }
22588             return;
22589         }
22590         */
22591         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22592             this.win.focus();
22593             
22594             
22595             // from jquery ui (MIT licenced)
22596             var range, node;
22597             var win = this.win;
22598             
22599             if (win.getSelection && win.getSelection().getRangeAt) {
22600                 range = win.getSelection().getRangeAt(0);
22601                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22602                 range.insertNode(node);
22603             } else if (win.document.selection && win.document.selection.createRange) {
22604                 // no firefox support
22605                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22606                 win.document.selection.createRange().pasteHTML(txt);
22607             } else {
22608                 // no firefox support
22609                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22610                 this.execCmd('InsertHTML', txt);
22611             } 
22612             
22613             this.syncValue();
22614             
22615             this.deferFocus();
22616         }
22617     },
22618  // private
22619     mozKeyPress : function(e){
22620         if(e.ctrlKey){
22621             var c = e.getCharCode(), cmd;
22622           
22623             if(c > 0){
22624                 c = String.fromCharCode(c).toLowerCase();
22625                 switch(c){
22626                     case 'b':
22627                         cmd = 'bold';
22628                         break;
22629                     case 'i':
22630                         cmd = 'italic';
22631                         break;
22632                     
22633                     case 'u':
22634                         cmd = 'underline';
22635                         break;
22636                     
22637                     case 'v':
22638                         this.cleanUpPaste.defer(100, this);
22639                         return;
22640                         
22641                 }
22642                 if(cmd){
22643                     this.win.focus();
22644                     this.execCmd(cmd);
22645                     this.deferFocus();
22646                     e.preventDefault();
22647                 }
22648                 
22649             }
22650         }
22651     },
22652
22653     // private
22654     fixKeys : function(){ // load time branching for fastest keydown performance
22655         if(Roo.isIE){
22656             return function(e){
22657                 var k = e.getKey(), r;
22658                 if(k == e.TAB){
22659                     e.stopEvent();
22660                     r = this.doc.selection.createRange();
22661                     if(r){
22662                         r.collapse(true);
22663                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22664                         this.deferFocus();
22665                     }
22666                     return;
22667                 }
22668                 
22669                 if(k == e.ENTER){
22670                     r = this.doc.selection.createRange();
22671                     if(r){
22672                         var target = r.parentElement();
22673                         if(!target || target.tagName.toLowerCase() != 'li'){
22674                             e.stopEvent();
22675                             r.pasteHTML('<br />');
22676                             r.collapse(false);
22677                             r.select();
22678                         }
22679                     }
22680                 }
22681                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22682                     this.cleanUpPaste.defer(100, this);
22683                     return;
22684                 }
22685                 
22686                 
22687             };
22688         }else if(Roo.isOpera){
22689             return function(e){
22690                 var k = e.getKey();
22691                 if(k == e.TAB){
22692                     e.stopEvent();
22693                     this.win.focus();
22694                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22695                     this.deferFocus();
22696                 }
22697                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22698                     this.cleanUpPaste.defer(100, this);
22699                     return;
22700                 }
22701                 
22702             };
22703         }else if(Roo.isSafari){
22704             return function(e){
22705                 var k = e.getKey();
22706                 
22707                 if(k == e.TAB){
22708                     e.stopEvent();
22709                     this.execCmd('InsertText','\t');
22710                     this.deferFocus();
22711                     return;
22712                 }
22713                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22714                     this.cleanUpPaste.defer(100, this);
22715                     return;
22716                 }
22717                 
22718              };
22719         }
22720     }(),
22721     
22722     getAllAncestors: function()
22723     {
22724         var p = this.getSelectedNode();
22725         var a = [];
22726         if (!p) {
22727             a.push(p); // push blank onto stack..
22728             p = this.getParentElement();
22729         }
22730         
22731         
22732         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22733             a.push(p);
22734             p = p.parentNode;
22735         }
22736         a.push(this.doc.body);
22737         return a;
22738     },
22739     lastSel : false,
22740     lastSelNode : false,
22741     
22742     
22743     getSelection : function() 
22744     {
22745         this.assignDocWin();
22746         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22747     },
22748     
22749     getSelectedNode: function() 
22750     {
22751         // this may only work on Gecko!!!
22752         
22753         // should we cache this!!!!
22754         
22755         
22756         
22757          
22758         var range = this.createRange(this.getSelection()).cloneRange();
22759         
22760         if (Roo.isIE) {
22761             var parent = range.parentElement();
22762             while (true) {
22763                 var testRange = range.duplicate();
22764                 testRange.moveToElementText(parent);
22765                 if (testRange.inRange(range)) {
22766                     break;
22767                 }
22768                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22769                     break;
22770                 }
22771                 parent = parent.parentElement;
22772             }
22773             return parent;
22774         }
22775         
22776         // is ancestor a text element.
22777         var ac =  range.commonAncestorContainer;
22778         if (ac.nodeType == 3) {
22779             ac = ac.parentNode;
22780         }
22781         
22782         var ar = ac.childNodes;
22783          
22784         var nodes = [];
22785         var other_nodes = [];
22786         var has_other_nodes = false;
22787         for (var i=0;i<ar.length;i++) {
22788             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22789                 continue;
22790             }
22791             // fullly contained node.
22792             
22793             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22794                 nodes.push(ar[i]);
22795                 continue;
22796             }
22797             
22798             // probably selected..
22799             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22800                 other_nodes.push(ar[i]);
22801                 continue;
22802             }
22803             // outer..
22804             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22805                 continue;
22806             }
22807             
22808             
22809             has_other_nodes = true;
22810         }
22811         if (!nodes.length && other_nodes.length) {
22812             nodes= other_nodes;
22813         }
22814         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22815             return false;
22816         }
22817         
22818         return nodes[0];
22819     },
22820     createRange: function(sel)
22821     {
22822         // this has strange effects when using with 
22823         // top toolbar - not sure if it's a great idea.
22824         //this.editor.contentWindow.focus();
22825         if (typeof sel != "undefined") {
22826             try {
22827                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22828             } catch(e) {
22829                 return this.doc.createRange();
22830             }
22831         } else {
22832             return this.doc.createRange();
22833         }
22834     },
22835     getParentElement: function()
22836     {
22837         
22838         this.assignDocWin();
22839         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22840         
22841         var range = this.createRange(sel);
22842          
22843         try {
22844             var p = range.commonAncestorContainer;
22845             while (p.nodeType == 3) { // text node
22846                 p = p.parentNode;
22847             }
22848             return p;
22849         } catch (e) {
22850             return null;
22851         }
22852     
22853     },
22854     /***
22855      *
22856      * Range intersection.. the hard stuff...
22857      *  '-1' = before
22858      *  '0' = hits..
22859      *  '1' = after.
22860      *         [ -- selected range --- ]
22861      *   [fail]                        [fail]
22862      *
22863      *    basically..
22864      *      if end is before start or  hits it. fail.
22865      *      if start is after end or hits it fail.
22866      *
22867      *   if either hits (but other is outside. - then it's not 
22868      *   
22869      *    
22870      **/
22871     
22872     
22873     // @see http://www.thismuchiknow.co.uk/?p=64.
22874     rangeIntersectsNode : function(range, node)
22875     {
22876         var nodeRange = node.ownerDocument.createRange();
22877         try {
22878             nodeRange.selectNode(node);
22879         } catch (e) {
22880             nodeRange.selectNodeContents(node);
22881         }
22882     
22883         var rangeStartRange = range.cloneRange();
22884         rangeStartRange.collapse(true);
22885     
22886         var rangeEndRange = range.cloneRange();
22887         rangeEndRange.collapse(false);
22888     
22889         var nodeStartRange = nodeRange.cloneRange();
22890         nodeStartRange.collapse(true);
22891     
22892         var nodeEndRange = nodeRange.cloneRange();
22893         nodeEndRange.collapse(false);
22894     
22895         return rangeStartRange.compareBoundaryPoints(
22896                  Range.START_TO_START, nodeEndRange) == -1 &&
22897                rangeEndRange.compareBoundaryPoints(
22898                  Range.START_TO_START, nodeStartRange) == 1;
22899         
22900          
22901     },
22902     rangeCompareNode : function(range, node)
22903     {
22904         var nodeRange = node.ownerDocument.createRange();
22905         try {
22906             nodeRange.selectNode(node);
22907         } catch (e) {
22908             nodeRange.selectNodeContents(node);
22909         }
22910         
22911         
22912         range.collapse(true);
22913     
22914         nodeRange.collapse(true);
22915      
22916         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22917         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22918          
22919         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22920         
22921         var nodeIsBefore   =  ss == 1;
22922         var nodeIsAfter    = ee == -1;
22923         
22924         if (nodeIsBefore && nodeIsAfter) {
22925             return 0; // outer
22926         }
22927         if (!nodeIsBefore && nodeIsAfter) {
22928             return 1; //right trailed.
22929         }
22930         
22931         if (nodeIsBefore && !nodeIsAfter) {
22932             return 2;  // left trailed.
22933         }
22934         // fully contined.
22935         return 3;
22936     },
22937
22938     // private? - in a new class?
22939     cleanUpPaste :  function()
22940     {
22941         // cleans up the whole document..
22942         Roo.log('cleanuppaste');
22943         
22944         this.cleanUpChildren(this.doc.body);
22945         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22946         if (clean != this.doc.body.innerHTML) {
22947             this.doc.body.innerHTML = clean;
22948         }
22949         
22950     },
22951     
22952     cleanWordChars : function(input) {// change the chars to hex code
22953         var he = Roo.HtmlEditorCore;
22954         
22955         var output = input;
22956         Roo.each(he.swapCodes, function(sw) { 
22957             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22958             
22959             output = output.replace(swapper, sw[1]);
22960         });
22961         
22962         return output;
22963     },
22964     
22965     
22966     cleanUpChildren : function (n)
22967     {
22968         if (!n.childNodes.length) {
22969             return;
22970         }
22971         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22972            this.cleanUpChild(n.childNodes[i]);
22973         }
22974     },
22975     
22976     
22977         
22978     
22979     cleanUpChild : function (node)
22980     {
22981         var ed = this;
22982         //console.log(node);
22983         if (node.nodeName == "#text") {
22984             // clean up silly Windows -- stuff?
22985             return; 
22986         }
22987         if (node.nodeName == "#comment") {
22988             node.parentNode.removeChild(node);
22989             // clean up silly Windows -- stuff?
22990             return; 
22991         }
22992         var lcname = node.tagName.toLowerCase();
22993         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22994         // whitelist of tags..
22995         
22996         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22997             // remove node.
22998             node.parentNode.removeChild(node);
22999             return;
23000             
23001         }
23002         
23003         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
23004         
23005         // remove <a name=....> as rendering on yahoo mailer is borked with this.
23006         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
23007         
23008         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
23009         //    remove_keep_children = true;
23010         //}
23011         
23012         if (remove_keep_children) {
23013             this.cleanUpChildren(node);
23014             // inserts everything just before this node...
23015             while (node.childNodes.length) {
23016                 var cn = node.childNodes[0];
23017                 node.removeChild(cn);
23018                 node.parentNode.insertBefore(cn, node);
23019             }
23020             node.parentNode.removeChild(node);
23021             return;
23022         }
23023         
23024         if (!node.attributes || !node.attributes.length) {
23025             this.cleanUpChildren(node);
23026             return;
23027         }
23028         
23029         function cleanAttr(n,v)
23030         {
23031             
23032             if (v.match(/^\./) || v.match(/^\//)) {
23033                 return;
23034             }
23035             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
23036                 return;
23037             }
23038             if (v.match(/^#/)) {
23039                 return;
23040             }
23041 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
23042             node.removeAttribute(n);
23043             
23044         }
23045         
23046         var cwhite = this.cwhite;
23047         var cblack = this.cblack;
23048             
23049         function cleanStyle(n,v)
23050         {
23051             if (v.match(/expression/)) { //XSS?? should we even bother..
23052                 node.removeAttribute(n);
23053                 return;
23054             }
23055             
23056             var parts = v.split(/;/);
23057             var clean = [];
23058             
23059             Roo.each(parts, function(p) {
23060                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
23061                 if (!p.length) {
23062                     return true;
23063                 }
23064                 var l = p.split(':').shift().replace(/\s+/g,'');
23065                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
23066                 
23067                 if ( cwhite.length && cblack.indexOf(l) > -1) {
23068 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23069                     //node.removeAttribute(n);
23070                     return true;
23071                 }
23072                 //Roo.log()
23073                 // only allow 'c whitelisted system attributes'
23074                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
23075 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23076                     //node.removeAttribute(n);
23077                     return true;
23078                 }
23079                 
23080                 
23081                  
23082                 
23083                 clean.push(p);
23084                 return true;
23085             });
23086             if (clean.length) { 
23087                 node.setAttribute(n, clean.join(';'));
23088             } else {
23089                 node.removeAttribute(n);
23090             }
23091             
23092         }
23093         
23094         
23095         for (var i = node.attributes.length-1; i > -1 ; i--) {
23096             var a = node.attributes[i];
23097             //console.log(a);
23098             
23099             if (a.name.toLowerCase().substr(0,2)=='on')  {
23100                 node.removeAttribute(a.name);
23101                 continue;
23102             }
23103             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
23104                 node.removeAttribute(a.name);
23105                 continue;
23106             }
23107             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
23108                 cleanAttr(a.name,a.value); // fixme..
23109                 continue;
23110             }
23111             if (a.name == 'style') {
23112                 cleanStyle(a.name,a.value);
23113                 continue;
23114             }
23115             /// clean up MS crap..
23116             // tecnically this should be a list of valid class'es..
23117             
23118             
23119             if (a.name == 'class') {
23120                 if (a.value.match(/^Mso/)) {
23121                     node.className = '';
23122                 }
23123                 
23124                 if (a.value.match(/^body$/)) {
23125                     node.className = '';
23126                 }
23127                 continue;
23128             }
23129             
23130             // style cleanup!?
23131             // class cleanup?
23132             
23133         }
23134         
23135         
23136         this.cleanUpChildren(node);
23137         
23138         
23139     },
23140     
23141     /**
23142      * Clean up MS wordisms...
23143      */
23144     cleanWord : function(node)
23145     {
23146         
23147         
23148         if (!node) {
23149             this.cleanWord(this.doc.body);
23150             return;
23151         }
23152         if (node.nodeName == "#text") {
23153             // clean up silly Windows -- stuff?
23154             return; 
23155         }
23156         if (node.nodeName == "#comment") {
23157             node.parentNode.removeChild(node);
23158             // clean up silly Windows -- stuff?
23159             return; 
23160         }
23161         
23162         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
23163             node.parentNode.removeChild(node);
23164             return;
23165         }
23166         
23167         // remove - but keep children..
23168         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
23169             while (node.childNodes.length) {
23170                 var cn = node.childNodes[0];
23171                 node.removeChild(cn);
23172                 node.parentNode.insertBefore(cn, node);
23173             }
23174             node.parentNode.removeChild(node);
23175             this.iterateChildren(node, this.cleanWord);
23176             return;
23177         }
23178         // clean styles
23179         if (node.className.length) {
23180             
23181             var cn = node.className.split(/\W+/);
23182             var cna = [];
23183             Roo.each(cn, function(cls) {
23184                 if (cls.match(/Mso[a-zA-Z]+/)) {
23185                     return;
23186                 }
23187                 cna.push(cls);
23188             });
23189             node.className = cna.length ? cna.join(' ') : '';
23190             if (!cna.length) {
23191                 node.removeAttribute("class");
23192             }
23193         }
23194         
23195         if (node.hasAttribute("lang")) {
23196             node.removeAttribute("lang");
23197         }
23198         
23199         if (node.hasAttribute("style")) {
23200             
23201             var styles = node.getAttribute("style").split(";");
23202             var nstyle = [];
23203             Roo.each(styles, function(s) {
23204                 if (!s.match(/:/)) {
23205                     return;
23206                 }
23207                 var kv = s.split(":");
23208                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
23209                     return;
23210                 }
23211                 // what ever is left... we allow.
23212                 nstyle.push(s);
23213             });
23214             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23215             if (!nstyle.length) {
23216                 node.removeAttribute('style');
23217             }
23218         }
23219         this.iterateChildren(node, this.cleanWord);
23220         
23221         
23222         
23223     },
23224     /**
23225      * iterateChildren of a Node, calling fn each time, using this as the scole..
23226      * @param {DomNode} node node to iterate children of.
23227      * @param {Function} fn method of this class to call on each item.
23228      */
23229     iterateChildren : function(node, fn)
23230     {
23231         if (!node.childNodes.length) {
23232                 return;
23233         }
23234         for (var i = node.childNodes.length-1; i > -1 ; i--) {
23235            fn.call(this, node.childNodes[i])
23236         }
23237     },
23238     
23239     
23240     /**
23241      * cleanTableWidths.
23242      *
23243      * Quite often pasting from word etc.. results in tables with column and widths.
23244      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23245      *
23246      */
23247     cleanTableWidths : function(node)
23248     {
23249          
23250          
23251         if (!node) {
23252             this.cleanTableWidths(this.doc.body);
23253             return;
23254         }
23255         
23256         // ignore list...
23257         if (node.nodeName == "#text" || node.nodeName == "#comment") {
23258             return; 
23259         }
23260         Roo.log(node.tagName);
23261         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23262             this.iterateChildren(node, this.cleanTableWidths);
23263             return;
23264         }
23265         if (node.hasAttribute('width')) {
23266             node.removeAttribute('width');
23267         }
23268         
23269          
23270         if (node.hasAttribute("style")) {
23271             // pretty basic...
23272             
23273             var styles = node.getAttribute("style").split(";");
23274             var nstyle = [];
23275             Roo.each(styles, function(s) {
23276                 if (!s.match(/:/)) {
23277                     return;
23278                 }
23279                 var kv = s.split(":");
23280                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23281                     return;
23282                 }
23283                 // what ever is left... we allow.
23284                 nstyle.push(s);
23285             });
23286             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23287             if (!nstyle.length) {
23288                 node.removeAttribute('style');
23289             }
23290         }
23291         
23292         this.iterateChildren(node, this.cleanTableWidths);
23293         
23294         
23295     },
23296     
23297     
23298     
23299     
23300     domToHTML : function(currentElement, depth, nopadtext) {
23301         
23302         depth = depth || 0;
23303         nopadtext = nopadtext || false;
23304     
23305         if (!currentElement) {
23306             return this.domToHTML(this.doc.body);
23307         }
23308         
23309         //Roo.log(currentElement);
23310         var j;
23311         var allText = false;
23312         var nodeName = currentElement.nodeName;
23313         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23314         
23315         if  (nodeName == '#text') {
23316             
23317             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23318         }
23319         
23320         
23321         var ret = '';
23322         if (nodeName != 'BODY') {
23323              
23324             var i = 0;
23325             // Prints the node tagName, such as <A>, <IMG>, etc
23326             if (tagName) {
23327                 var attr = [];
23328                 for(i = 0; i < currentElement.attributes.length;i++) {
23329                     // quoting?
23330                     var aname = currentElement.attributes.item(i).name;
23331                     if (!currentElement.attributes.item(i).value.length) {
23332                         continue;
23333                     }
23334                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23335                 }
23336                 
23337                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23338             } 
23339             else {
23340                 
23341                 // eack
23342             }
23343         } else {
23344             tagName = false;
23345         }
23346         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23347             return ret;
23348         }
23349         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23350             nopadtext = true;
23351         }
23352         
23353         
23354         // Traverse the tree
23355         i = 0;
23356         var currentElementChild = currentElement.childNodes.item(i);
23357         var allText = true;
23358         var innerHTML  = '';
23359         lastnode = '';
23360         while (currentElementChild) {
23361             // Formatting code (indent the tree so it looks nice on the screen)
23362             var nopad = nopadtext;
23363             if (lastnode == 'SPAN') {
23364                 nopad  = true;
23365             }
23366             // text
23367             if  (currentElementChild.nodeName == '#text') {
23368                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23369                 toadd = nopadtext ? toadd : toadd.trim();
23370                 if (!nopad && toadd.length > 80) {
23371                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23372                 }
23373                 innerHTML  += toadd;
23374                 
23375                 i++;
23376                 currentElementChild = currentElement.childNodes.item(i);
23377                 lastNode = '';
23378                 continue;
23379             }
23380             allText = false;
23381             
23382             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23383                 
23384             // Recursively traverse the tree structure of the child node
23385             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23386             lastnode = currentElementChild.nodeName;
23387             i++;
23388             currentElementChild=currentElement.childNodes.item(i);
23389         }
23390         
23391         ret += innerHTML;
23392         
23393         if (!allText) {
23394                 // The remaining code is mostly for formatting the tree
23395             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23396         }
23397         
23398         
23399         if (tagName) {
23400             ret+= "</"+tagName+">";
23401         }
23402         return ret;
23403         
23404     },
23405         
23406     applyBlacklists : function()
23407     {
23408         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23409         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23410         
23411         this.white = [];
23412         this.black = [];
23413         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23414             if (b.indexOf(tag) > -1) {
23415                 return;
23416             }
23417             this.white.push(tag);
23418             
23419         }, this);
23420         
23421         Roo.each(w, function(tag) {
23422             if (b.indexOf(tag) > -1) {
23423                 return;
23424             }
23425             if (this.white.indexOf(tag) > -1) {
23426                 return;
23427             }
23428             this.white.push(tag);
23429             
23430         }, this);
23431         
23432         
23433         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23434             if (w.indexOf(tag) > -1) {
23435                 return;
23436             }
23437             this.black.push(tag);
23438             
23439         }, this);
23440         
23441         Roo.each(b, function(tag) {
23442             if (w.indexOf(tag) > -1) {
23443                 return;
23444             }
23445             if (this.black.indexOf(tag) > -1) {
23446                 return;
23447             }
23448             this.black.push(tag);
23449             
23450         }, this);
23451         
23452         
23453         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23454         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23455         
23456         this.cwhite = [];
23457         this.cblack = [];
23458         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23459             if (b.indexOf(tag) > -1) {
23460                 return;
23461             }
23462             this.cwhite.push(tag);
23463             
23464         }, this);
23465         
23466         Roo.each(w, function(tag) {
23467             if (b.indexOf(tag) > -1) {
23468                 return;
23469             }
23470             if (this.cwhite.indexOf(tag) > -1) {
23471                 return;
23472             }
23473             this.cwhite.push(tag);
23474             
23475         }, this);
23476         
23477         
23478         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23479             if (w.indexOf(tag) > -1) {
23480                 return;
23481             }
23482             this.cblack.push(tag);
23483             
23484         }, this);
23485         
23486         Roo.each(b, function(tag) {
23487             if (w.indexOf(tag) > -1) {
23488                 return;
23489             }
23490             if (this.cblack.indexOf(tag) > -1) {
23491                 return;
23492             }
23493             this.cblack.push(tag);
23494             
23495         }, this);
23496     },
23497     
23498     setStylesheets : function(stylesheets)
23499     {
23500         if(typeof(stylesheets) == 'string'){
23501             Roo.get(this.iframe.contentDocument.head).createChild({
23502                 tag : 'link',
23503                 rel : 'stylesheet',
23504                 type : 'text/css',
23505                 href : stylesheets
23506             });
23507             
23508             return;
23509         }
23510         var _this = this;
23511      
23512         Roo.each(stylesheets, function(s) {
23513             if(!s.length){
23514                 return;
23515             }
23516             
23517             Roo.get(_this.iframe.contentDocument.head).createChild({
23518                 tag : 'link',
23519                 rel : 'stylesheet',
23520                 type : 'text/css',
23521                 href : s
23522             });
23523         });
23524
23525         
23526     },
23527     
23528     removeStylesheets : function()
23529     {
23530         var _this = this;
23531         
23532         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23533             s.remove();
23534         });
23535     },
23536     
23537     setStyle : function(style)
23538     {
23539         Roo.get(this.iframe.contentDocument.head).createChild({
23540             tag : 'style',
23541             type : 'text/css',
23542             html : style
23543         });
23544
23545         return;
23546     }
23547     
23548     // hide stuff that is not compatible
23549     /**
23550      * @event blur
23551      * @hide
23552      */
23553     /**
23554      * @event change
23555      * @hide
23556      */
23557     /**
23558      * @event focus
23559      * @hide
23560      */
23561     /**
23562      * @event specialkey
23563      * @hide
23564      */
23565     /**
23566      * @cfg {String} fieldClass @hide
23567      */
23568     /**
23569      * @cfg {String} focusClass @hide
23570      */
23571     /**
23572      * @cfg {String} autoCreate @hide
23573      */
23574     /**
23575      * @cfg {String} inputType @hide
23576      */
23577     /**
23578      * @cfg {String} invalidClass @hide
23579      */
23580     /**
23581      * @cfg {String} invalidText @hide
23582      */
23583     /**
23584      * @cfg {String} msgFx @hide
23585      */
23586     /**
23587      * @cfg {String} validateOnBlur @hide
23588      */
23589 });
23590
23591 Roo.HtmlEditorCore.white = [
23592         'area', 'br', 'img', 'input', 'hr', 'wbr',
23593         
23594        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23595        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23596        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23597        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23598        'table',   'ul',         'xmp', 
23599        
23600        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23601       'thead',   'tr', 
23602      
23603       'dir', 'menu', 'ol', 'ul', 'dl',
23604        
23605       'embed',  'object'
23606 ];
23607
23608
23609 Roo.HtmlEditorCore.black = [
23610     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23611         'applet', // 
23612         'base',   'basefont', 'bgsound', 'blink',  'body', 
23613         'frame',  'frameset', 'head',    'html',   'ilayer', 
23614         'iframe', 'layer',  'link',     'meta',    'object',   
23615         'script', 'style' ,'title',  'xml' // clean later..
23616 ];
23617 Roo.HtmlEditorCore.clean = [
23618     'script', 'style', 'title', 'xml'
23619 ];
23620 Roo.HtmlEditorCore.remove = [
23621     'font'
23622 ];
23623 // attributes..
23624
23625 Roo.HtmlEditorCore.ablack = [
23626     'on'
23627 ];
23628     
23629 Roo.HtmlEditorCore.aclean = [ 
23630     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23631 ];
23632
23633 // protocols..
23634 Roo.HtmlEditorCore.pwhite= [
23635         'http',  'https',  'mailto'
23636 ];
23637
23638 // white listed style attributes.
23639 Roo.HtmlEditorCore.cwhite= [
23640       //  'text-align', /// default is to allow most things..
23641       
23642          
23643 //        'font-size'//??
23644 ];
23645
23646 // black listed style attributes.
23647 Roo.HtmlEditorCore.cblack= [
23648       //  'font-size' -- this can be set by the project 
23649 ];
23650
23651
23652 Roo.HtmlEditorCore.swapCodes   =[ 
23653     [    8211, "--" ], 
23654     [    8212, "--" ], 
23655     [    8216,  "'" ],  
23656     [    8217, "'" ],  
23657     [    8220, '"' ],  
23658     [    8221, '"' ],  
23659     [    8226, "*" ],  
23660     [    8230, "..." ]
23661 ]; 
23662
23663     /*
23664  * - LGPL
23665  *
23666  * HtmlEditor
23667  * 
23668  */
23669
23670 /**
23671  * @class Roo.bootstrap.HtmlEditor
23672  * @extends Roo.bootstrap.TextArea
23673  * Bootstrap HtmlEditor class
23674
23675  * @constructor
23676  * Create a new HtmlEditor
23677  * @param {Object} config The config object
23678  */
23679
23680 Roo.bootstrap.HtmlEditor = function(config){
23681     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23682     if (!this.toolbars) {
23683         this.toolbars = [];
23684     }
23685     
23686     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23687     this.addEvents({
23688             /**
23689              * @event initialize
23690              * Fires when the editor is fully initialized (including the iframe)
23691              * @param {HtmlEditor} this
23692              */
23693             initialize: true,
23694             /**
23695              * @event activate
23696              * Fires when the editor is first receives the focus. Any insertion must wait
23697              * until after this event.
23698              * @param {HtmlEditor} this
23699              */
23700             activate: true,
23701              /**
23702              * @event beforesync
23703              * Fires before the textarea is updated with content from the editor iframe. Return false
23704              * to cancel the sync.
23705              * @param {HtmlEditor} this
23706              * @param {String} html
23707              */
23708             beforesync: true,
23709              /**
23710              * @event beforepush
23711              * Fires before the iframe editor is updated with content from the textarea. Return false
23712              * to cancel the push.
23713              * @param {HtmlEditor} this
23714              * @param {String} html
23715              */
23716             beforepush: true,
23717              /**
23718              * @event sync
23719              * Fires when the textarea is updated with content from the editor iframe.
23720              * @param {HtmlEditor} this
23721              * @param {String} html
23722              */
23723             sync: true,
23724              /**
23725              * @event push
23726              * Fires when the iframe editor is updated with content from the textarea.
23727              * @param {HtmlEditor} this
23728              * @param {String} html
23729              */
23730             push: true,
23731              /**
23732              * @event editmodechange
23733              * Fires when the editor switches edit modes
23734              * @param {HtmlEditor} this
23735              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23736              */
23737             editmodechange: true,
23738             /**
23739              * @event editorevent
23740              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23741              * @param {HtmlEditor} this
23742              */
23743             editorevent: true,
23744             /**
23745              * @event firstfocus
23746              * Fires when on first focus - needed by toolbars..
23747              * @param {HtmlEditor} this
23748              */
23749             firstfocus: true,
23750             /**
23751              * @event autosave
23752              * Auto save the htmlEditor value as a file into Events
23753              * @param {HtmlEditor} this
23754              */
23755             autosave: true,
23756             /**
23757              * @event savedpreview
23758              * preview the saved version of htmlEditor
23759              * @param {HtmlEditor} this
23760              */
23761             savedpreview: true
23762         });
23763 };
23764
23765
23766 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23767     
23768     
23769       /**
23770      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23771      */
23772     toolbars : false,
23773     
23774      /**
23775     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23776     */
23777     btns : [],
23778    
23779      /**
23780      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23781      *                        Roo.resizable.
23782      */
23783     resizable : false,
23784      /**
23785      * @cfg {Number} height (in pixels)
23786      */   
23787     height: 300,
23788    /**
23789      * @cfg {Number} width (in pixels)
23790      */   
23791     width: false,
23792     
23793     /**
23794      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23795      * 
23796      */
23797     stylesheets: false,
23798     
23799     // id of frame..
23800     frameId: false,
23801     
23802     // private properties
23803     validationEvent : false,
23804     deferHeight: true,
23805     initialized : false,
23806     activated : false,
23807     
23808     onFocus : Roo.emptyFn,
23809     iframePad:3,
23810     hideMode:'offsets',
23811     
23812     tbContainer : false,
23813     
23814     bodyCls : '',
23815     
23816     toolbarContainer :function() {
23817         return this.wrap.select('.x-html-editor-tb',true).first();
23818     },
23819
23820     /**
23821      * Protected method that will not generally be called directly. It
23822      * is called when the editor creates its toolbar. Override this method if you need to
23823      * add custom toolbar buttons.
23824      * @param {HtmlEditor} editor
23825      */
23826     createToolbar : function(){
23827         Roo.log('renewing');
23828         Roo.log("create toolbars");
23829         
23830         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23831         this.toolbars[0].render(this.toolbarContainer());
23832         
23833         return;
23834         
23835 //        if (!editor.toolbars || !editor.toolbars.length) {
23836 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23837 //        }
23838 //        
23839 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23840 //            editor.toolbars[i] = Roo.factory(
23841 //                    typeof(editor.toolbars[i]) == 'string' ?
23842 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23843 //                Roo.bootstrap.HtmlEditor);
23844 //            editor.toolbars[i].init(editor);
23845 //        }
23846     },
23847
23848      
23849     // private
23850     onRender : function(ct, position)
23851     {
23852        // Roo.log("Call onRender: " + this.xtype);
23853         var _t = this;
23854         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23855       
23856         this.wrap = this.inputEl().wrap({
23857             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23858         });
23859         
23860         this.editorcore.onRender(ct, position);
23861          
23862         if (this.resizable) {
23863             this.resizeEl = new Roo.Resizable(this.wrap, {
23864                 pinned : true,
23865                 wrap: true,
23866                 dynamic : true,
23867                 minHeight : this.height,
23868                 height: this.height,
23869                 handles : this.resizable,
23870                 width: this.width,
23871                 listeners : {
23872                     resize : function(r, w, h) {
23873                         _t.onResize(w,h); // -something
23874                     }
23875                 }
23876             });
23877             
23878         }
23879         this.createToolbar(this);
23880        
23881         
23882         if(!this.width && this.resizable){
23883             this.setSize(this.wrap.getSize());
23884         }
23885         if (this.resizeEl) {
23886             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23887             // should trigger onReize..
23888         }
23889         
23890     },
23891
23892     // private
23893     onResize : function(w, h)
23894     {
23895         Roo.log('resize: ' +w + ',' + h );
23896         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23897         var ew = false;
23898         var eh = false;
23899         
23900         if(this.inputEl() ){
23901             if(typeof w == 'number'){
23902                 var aw = w - this.wrap.getFrameWidth('lr');
23903                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23904                 ew = aw;
23905             }
23906             if(typeof h == 'number'){
23907                  var tbh = -11;  // fixme it needs to tool bar size!
23908                 for (var i =0; i < this.toolbars.length;i++) {
23909                     // fixme - ask toolbars for heights?
23910                     tbh += this.toolbars[i].el.getHeight();
23911                     //if (this.toolbars[i].footer) {
23912                     //    tbh += this.toolbars[i].footer.el.getHeight();
23913                     //}
23914                 }
23915               
23916                 
23917                 
23918                 
23919                 
23920                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23921                 ah -= 5; // knock a few pixes off for look..
23922                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23923                 var eh = ah;
23924             }
23925         }
23926         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23927         this.editorcore.onResize(ew,eh);
23928         
23929     },
23930
23931     /**
23932      * Toggles the editor between standard and source edit mode.
23933      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23934      */
23935     toggleSourceEdit : function(sourceEditMode)
23936     {
23937         this.editorcore.toggleSourceEdit(sourceEditMode);
23938         
23939         if(this.editorcore.sourceEditMode){
23940             Roo.log('editor - showing textarea');
23941             
23942 //            Roo.log('in');
23943 //            Roo.log(this.syncValue());
23944             this.syncValue();
23945             this.inputEl().removeClass(['hide', 'x-hidden']);
23946             this.inputEl().dom.removeAttribute('tabIndex');
23947             this.inputEl().focus();
23948         }else{
23949             Roo.log('editor - hiding textarea');
23950 //            Roo.log('out')
23951 //            Roo.log(this.pushValue()); 
23952             this.pushValue();
23953             
23954             this.inputEl().addClass(['hide', 'x-hidden']);
23955             this.inputEl().dom.setAttribute('tabIndex', -1);
23956             //this.deferFocus();
23957         }
23958          
23959         if(this.resizable){
23960             this.setSize(this.wrap.getSize());
23961         }
23962         
23963         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23964     },
23965  
23966     // private (for BoxComponent)
23967     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23968
23969     // private (for BoxComponent)
23970     getResizeEl : function(){
23971         return this.wrap;
23972     },
23973
23974     // private (for BoxComponent)
23975     getPositionEl : function(){
23976         return this.wrap;
23977     },
23978
23979     // private
23980     initEvents : function(){
23981         this.originalValue = this.getValue();
23982     },
23983
23984 //    /**
23985 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23986 //     * @method
23987 //     */
23988 //    markInvalid : Roo.emptyFn,
23989 //    /**
23990 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23991 //     * @method
23992 //     */
23993 //    clearInvalid : Roo.emptyFn,
23994
23995     setValue : function(v){
23996         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23997         this.editorcore.pushValue();
23998     },
23999
24000      
24001     // private
24002     deferFocus : function(){
24003         this.focus.defer(10, this);
24004     },
24005
24006     // doc'ed in Field
24007     focus : function(){
24008         this.editorcore.focus();
24009         
24010     },
24011       
24012
24013     // private
24014     onDestroy : function(){
24015         
24016         
24017         
24018         if(this.rendered){
24019             
24020             for (var i =0; i < this.toolbars.length;i++) {
24021                 // fixme - ask toolbars for heights?
24022                 this.toolbars[i].onDestroy();
24023             }
24024             
24025             this.wrap.dom.innerHTML = '';
24026             this.wrap.remove();
24027         }
24028     },
24029
24030     // private
24031     onFirstFocus : function(){
24032         //Roo.log("onFirstFocus");
24033         this.editorcore.onFirstFocus();
24034          for (var i =0; i < this.toolbars.length;i++) {
24035             this.toolbars[i].onFirstFocus();
24036         }
24037         
24038     },
24039     
24040     // private
24041     syncValue : function()
24042     {   
24043         this.editorcore.syncValue();
24044     },
24045     
24046     pushValue : function()
24047     {   
24048         this.editorcore.pushValue();
24049     }
24050      
24051     
24052     // hide stuff that is not compatible
24053     /**
24054      * @event blur
24055      * @hide
24056      */
24057     /**
24058      * @event change
24059      * @hide
24060      */
24061     /**
24062      * @event focus
24063      * @hide
24064      */
24065     /**
24066      * @event specialkey
24067      * @hide
24068      */
24069     /**
24070      * @cfg {String} fieldClass @hide
24071      */
24072     /**
24073      * @cfg {String} focusClass @hide
24074      */
24075     /**
24076      * @cfg {String} autoCreate @hide
24077      */
24078     /**
24079      * @cfg {String} inputType @hide
24080      */
24081      
24082     /**
24083      * @cfg {String} invalidText @hide
24084      */
24085     /**
24086      * @cfg {String} msgFx @hide
24087      */
24088     /**
24089      * @cfg {String} validateOnBlur @hide
24090      */
24091 });
24092  
24093     
24094    
24095    
24096    
24097       
24098 Roo.namespace('Roo.bootstrap.htmleditor');
24099 /**
24100  * @class Roo.bootstrap.HtmlEditorToolbar1
24101  * Basic Toolbar
24102  * 
24103  * @example
24104  * Usage:
24105  *
24106  new Roo.bootstrap.HtmlEditor({
24107     ....
24108     toolbars : [
24109         new Roo.bootstrap.HtmlEditorToolbar1({
24110             disable : { fonts: 1 , format: 1, ..., ... , ...],
24111             btns : [ .... ]
24112         })
24113     }
24114      
24115  * 
24116  * @cfg {Object} disable List of elements to disable..
24117  * @cfg {Array} btns List of additional buttons.
24118  * 
24119  * 
24120  * NEEDS Extra CSS? 
24121  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24122  */
24123  
24124 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
24125 {
24126     
24127     Roo.apply(this, config);
24128     
24129     // default disabled, based on 'good practice'..
24130     this.disable = this.disable || {};
24131     Roo.applyIf(this.disable, {
24132         fontSize : true,
24133         colors : true,
24134         specialElements : true
24135     });
24136     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
24137     
24138     this.editor = config.editor;
24139     this.editorcore = config.editor.editorcore;
24140     
24141     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
24142     
24143     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24144     // dont call parent... till later.
24145 }
24146 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
24147      
24148     bar : true,
24149     
24150     editor : false,
24151     editorcore : false,
24152     
24153     
24154     formats : [
24155         "p" ,  
24156         "h1","h2","h3","h4","h5","h6", 
24157         "pre", "code", 
24158         "abbr", "acronym", "address", "cite", "samp", "var",
24159         'div','span'
24160     ],
24161     
24162     onRender : function(ct, position)
24163     {
24164        // Roo.log("Call onRender: " + this.xtype);
24165         
24166        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
24167        Roo.log(this.el);
24168        this.el.dom.style.marginBottom = '0';
24169        var _this = this;
24170        var editorcore = this.editorcore;
24171        var editor= this.editor;
24172        
24173        var children = [];
24174        var btn = function(id,cmd , toggle, handler, html){
24175        
24176             var  event = toggle ? 'toggle' : 'click';
24177        
24178             var a = {
24179                 size : 'sm',
24180                 xtype: 'Button',
24181                 xns: Roo.bootstrap,
24182                 //glyphicon : id,
24183                 fa: id,
24184                 cmd : id || cmd,
24185                 enableToggle:toggle !== false,
24186                 html : html || '',
24187                 pressed : toggle ? false : null,
24188                 listeners : {}
24189             };
24190             a.listeners[toggle ? 'toggle' : 'click'] = function() {
24191                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
24192             };
24193             children.push(a);
24194             return a;
24195        }
24196        
24197     //    var cb_box = function...
24198         
24199         var style = {
24200                 xtype: 'Button',
24201                 size : 'sm',
24202                 xns: Roo.bootstrap,
24203                 fa : 'font',
24204                 //html : 'submit'
24205                 menu : {
24206                     xtype: 'Menu',
24207                     xns: Roo.bootstrap,
24208                     items:  []
24209                 }
24210         };
24211         Roo.each(this.formats, function(f) {
24212             style.menu.items.push({
24213                 xtype :'MenuItem',
24214                 xns: Roo.bootstrap,
24215                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24216                 tagname : f,
24217                 listeners : {
24218                     click : function()
24219                     {
24220                         editorcore.insertTag(this.tagname);
24221                         editor.focus();
24222                     }
24223                 }
24224                 
24225             });
24226         });
24227         children.push(style);   
24228         
24229         btn('bold',false,true);
24230         btn('italic',false,true);
24231         btn('align-left', 'justifyleft',true);
24232         btn('align-center', 'justifycenter',true);
24233         btn('align-right' , 'justifyright',true);
24234         btn('link', false, false, function(btn) {
24235             //Roo.log("create link?");
24236             var url = prompt(this.createLinkText, this.defaultLinkValue);
24237             if(url && url != 'http:/'+'/'){
24238                 this.editorcore.relayCmd('createlink', url);
24239             }
24240         }),
24241         btn('list','insertunorderedlist',true);
24242         btn('pencil', false,true, function(btn){
24243                 Roo.log(this);
24244                 this.toggleSourceEdit(btn.pressed);
24245         });
24246         
24247         if (this.editor.btns.length > 0) {
24248             for (var i = 0; i<this.editor.btns.length; i++) {
24249                 children.push(this.editor.btns[i]);
24250             }
24251         }
24252         
24253         /*
24254         var cog = {
24255                 xtype: 'Button',
24256                 size : 'sm',
24257                 xns: Roo.bootstrap,
24258                 glyphicon : 'cog',
24259                 //html : 'submit'
24260                 menu : {
24261                     xtype: 'Menu',
24262                     xns: Roo.bootstrap,
24263                     items:  []
24264                 }
24265         };
24266         
24267         cog.menu.items.push({
24268             xtype :'MenuItem',
24269             xns: Roo.bootstrap,
24270             html : Clean styles,
24271             tagname : f,
24272             listeners : {
24273                 click : function()
24274                 {
24275                     editorcore.insertTag(this.tagname);
24276                     editor.focus();
24277                 }
24278             }
24279             
24280         });
24281        */
24282         
24283          
24284        this.xtype = 'NavSimplebar';
24285         
24286         for(var i=0;i< children.length;i++) {
24287             
24288             this.buttons.add(this.addxtypeChild(children[i]));
24289             
24290         }
24291         
24292         editor.on('editorevent', this.updateToolbar, this);
24293     },
24294     onBtnClick : function(id)
24295     {
24296        this.editorcore.relayCmd(id);
24297        this.editorcore.focus();
24298     },
24299     
24300     /**
24301      * Protected method that will not generally be called directly. It triggers
24302      * a toolbar update by reading the markup state of the current selection in the editor.
24303      */
24304     updateToolbar: function(){
24305
24306         if(!this.editorcore.activated){
24307             this.editor.onFirstFocus(); // is this neeed?
24308             return;
24309         }
24310
24311         var btns = this.buttons; 
24312         var doc = this.editorcore.doc;
24313         btns.get('bold').setActive(doc.queryCommandState('bold'));
24314         btns.get('italic').setActive(doc.queryCommandState('italic'));
24315         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24316         
24317         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24318         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24319         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24320         
24321         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24322         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24323          /*
24324         
24325         var ans = this.editorcore.getAllAncestors();
24326         if (this.formatCombo) {
24327             
24328             
24329             var store = this.formatCombo.store;
24330             this.formatCombo.setValue("");
24331             for (var i =0; i < ans.length;i++) {
24332                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24333                     // select it..
24334                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24335                     break;
24336                 }
24337             }
24338         }
24339         
24340         
24341         
24342         // hides menus... - so this cant be on a menu...
24343         Roo.bootstrap.MenuMgr.hideAll();
24344         */
24345         Roo.bootstrap.MenuMgr.hideAll();
24346         //this.editorsyncValue();
24347     },
24348     onFirstFocus: function() {
24349         this.buttons.each(function(item){
24350            item.enable();
24351         });
24352     },
24353     toggleSourceEdit : function(sourceEditMode){
24354         
24355           
24356         if(sourceEditMode){
24357             Roo.log("disabling buttons");
24358            this.buttons.each( function(item){
24359                 if(item.cmd != 'pencil'){
24360                     item.disable();
24361                 }
24362             });
24363           
24364         }else{
24365             Roo.log("enabling buttons");
24366             if(this.editorcore.initialized){
24367                 this.buttons.each( function(item){
24368                     item.enable();
24369                 });
24370             }
24371             
24372         }
24373         Roo.log("calling toggole on editor");
24374         // tell the editor that it's been pressed..
24375         this.editor.toggleSourceEdit(sourceEditMode);
24376        
24377     }
24378 });
24379
24380
24381
24382
24383
24384 /**
24385  * @class Roo.bootstrap.Table.AbstractSelectionModel
24386  * @extends Roo.util.Observable
24387  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24388  * implemented by descendant classes.  This class should not be directly instantiated.
24389  * @constructor
24390  */
24391 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24392     this.locked = false;
24393     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24394 };
24395
24396
24397 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24398     /** @ignore Called by the grid automatically. Do not call directly. */
24399     init : function(grid){
24400         this.grid = grid;
24401         this.initEvents();
24402     },
24403
24404     /**
24405      * Locks the selections.
24406      */
24407     lock : function(){
24408         this.locked = true;
24409     },
24410
24411     /**
24412      * Unlocks the selections.
24413      */
24414     unlock : function(){
24415         this.locked = false;
24416     },
24417
24418     /**
24419      * Returns true if the selections are locked.
24420      * @return {Boolean}
24421      */
24422     isLocked : function(){
24423         return this.locked;
24424     }
24425 });
24426 /**
24427  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24428  * @class Roo.bootstrap.Table.RowSelectionModel
24429  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24430  * It supports multiple selections and keyboard selection/navigation. 
24431  * @constructor
24432  * @param {Object} config
24433  */
24434
24435 Roo.bootstrap.Table.RowSelectionModel = function(config){
24436     Roo.apply(this, config);
24437     this.selections = new Roo.util.MixedCollection(false, function(o){
24438         return o.id;
24439     });
24440
24441     this.last = false;
24442     this.lastActive = false;
24443
24444     this.addEvents({
24445         /**
24446              * @event selectionchange
24447              * Fires when the selection changes
24448              * @param {SelectionModel} this
24449              */
24450             "selectionchange" : true,
24451         /**
24452              * @event afterselectionchange
24453              * Fires after the selection changes (eg. by key press or clicking)
24454              * @param {SelectionModel} this
24455              */
24456             "afterselectionchange" : true,
24457         /**
24458              * @event beforerowselect
24459              * Fires when a row is selected being selected, return false to cancel.
24460              * @param {SelectionModel} this
24461              * @param {Number} rowIndex The selected index
24462              * @param {Boolean} keepExisting False if other selections will be cleared
24463              */
24464             "beforerowselect" : true,
24465         /**
24466              * @event rowselect
24467              * Fires when a row is selected.
24468              * @param {SelectionModel} this
24469              * @param {Number} rowIndex The selected index
24470              * @param {Roo.data.Record} r The record
24471              */
24472             "rowselect" : true,
24473         /**
24474              * @event rowdeselect
24475              * Fires when a row is deselected.
24476              * @param {SelectionModel} this
24477              * @param {Number} rowIndex The selected index
24478              */
24479         "rowdeselect" : true
24480     });
24481     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24482     this.locked = false;
24483  };
24484
24485 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24486     /**
24487      * @cfg {Boolean} singleSelect
24488      * True to allow selection of only one row at a time (defaults to false)
24489      */
24490     singleSelect : false,
24491
24492     // private
24493     initEvents : function()
24494     {
24495
24496         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24497         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24498         //}else{ // allow click to work like normal
24499          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24500         //}
24501         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24502         this.grid.on("rowclick", this.handleMouseDown, this);
24503         
24504         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24505             "up" : function(e){
24506                 if(!e.shiftKey){
24507                     this.selectPrevious(e.shiftKey);
24508                 }else if(this.last !== false && this.lastActive !== false){
24509                     var last = this.last;
24510                     this.selectRange(this.last,  this.lastActive-1);
24511                     this.grid.getView().focusRow(this.lastActive);
24512                     if(last !== false){
24513                         this.last = last;
24514                     }
24515                 }else{
24516                     this.selectFirstRow();
24517                 }
24518                 this.fireEvent("afterselectionchange", this);
24519             },
24520             "down" : function(e){
24521                 if(!e.shiftKey){
24522                     this.selectNext(e.shiftKey);
24523                 }else if(this.last !== false && this.lastActive !== false){
24524                     var last = this.last;
24525                     this.selectRange(this.last,  this.lastActive+1);
24526                     this.grid.getView().focusRow(this.lastActive);
24527                     if(last !== false){
24528                         this.last = last;
24529                     }
24530                 }else{
24531                     this.selectFirstRow();
24532                 }
24533                 this.fireEvent("afterselectionchange", this);
24534             },
24535             scope: this
24536         });
24537         this.grid.store.on('load', function(){
24538             this.selections.clear();
24539         },this);
24540         /*
24541         var view = this.grid.view;
24542         view.on("refresh", this.onRefresh, this);
24543         view.on("rowupdated", this.onRowUpdated, this);
24544         view.on("rowremoved", this.onRemove, this);
24545         */
24546     },
24547
24548     // private
24549     onRefresh : function()
24550     {
24551         var ds = this.grid.store, i, v = this.grid.view;
24552         var s = this.selections;
24553         s.each(function(r){
24554             if((i = ds.indexOfId(r.id)) != -1){
24555                 v.onRowSelect(i);
24556             }else{
24557                 s.remove(r);
24558             }
24559         });
24560     },
24561
24562     // private
24563     onRemove : function(v, index, r){
24564         this.selections.remove(r);
24565     },
24566
24567     // private
24568     onRowUpdated : function(v, index, r){
24569         if(this.isSelected(r)){
24570             v.onRowSelect(index);
24571         }
24572     },
24573
24574     /**
24575      * Select records.
24576      * @param {Array} records The records to select
24577      * @param {Boolean} keepExisting (optional) True to keep existing selections
24578      */
24579     selectRecords : function(records, keepExisting)
24580     {
24581         if(!keepExisting){
24582             this.clearSelections();
24583         }
24584             var ds = this.grid.store;
24585         for(var i = 0, len = records.length; i < len; i++){
24586             this.selectRow(ds.indexOf(records[i]), true);
24587         }
24588     },
24589
24590     /**
24591      * Gets the number of selected rows.
24592      * @return {Number}
24593      */
24594     getCount : function(){
24595         return this.selections.length;
24596     },
24597
24598     /**
24599      * Selects the first row in the grid.
24600      */
24601     selectFirstRow : function(){
24602         this.selectRow(0);
24603     },
24604
24605     /**
24606      * Select the last row.
24607      * @param {Boolean} keepExisting (optional) True to keep existing selections
24608      */
24609     selectLastRow : function(keepExisting){
24610         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24611         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24612     },
24613
24614     /**
24615      * Selects the row immediately following the last selected row.
24616      * @param {Boolean} keepExisting (optional) True to keep existing selections
24617      */
24618     selectNext : function(keepExisting)
24619     {
24620             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24621             this.selectRow(this.last+1, keepExisting);
24622             this.grid.getView().focusRow(this.last);
24623         }
24624     },
24625
24626     /**
24627      * Selects the row that precedes the last selected row.
24628      * @param {Boolean} keepExisting (optional) True to keep existing selections
24629      */
24630     selectPrevious : function(keepExisting){
24631         if(this.last){
24632             this.selectRow(this.last-1, keepExisting);
24633             this.grid.getView().focusRow(this.last);
24634         }
24635     },
24636
24637     /**
24638      * Returns the selected records
24639      * @return {Array} Array of selected records
24640      */
24641     getSelections : function(){
24642         return [].concat(this.selections.items);
24643     },
24644
24645     /**
24646      * Returns the first selected record.
24647      * @return {Record}
24648      */
24649     getSelected : function(){
24650         return this.selections.itemAt(0);
24651     },
24652
24653
24654     /**
24655      * Clears all selections.
24656      */
24657     clearSelections : function(fast)
24658     {
24659         if(this.locked) {
24660             return;
24661         }
24662         if(fast !== true){
24663                 var ds = this.grid.store;
24664             var s = this.selections;
24665             s.each(function(r){
24666                 this.deselectRow(ds.indexOfId(r.id));
24667             }, this);
24668             s.clear();
24669         }else{
24670             this.selections.clear();
24671         }
24672         this.last = false;
24673     },
24674
24675
24676     /**
24677      * Selects all rows.
24678      */
24679     selectAll : function(){
24680         if(this.locked) {
24681             return;
24682         }
24683         this.selections.clear();
24684         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24685             this.selectRow(i, true);
24686         }
24687     },
24688
24689     /**
24690      * Returns True if there is a selection.
24691      * @return {Boolean}
24692      */
24693     hasSelection : function(){
24694         return this.selections.length > 0;
24695     },
24696
24697     /**
24698      * Returns True if the specified row is selected.
24699      * @param {Number/Record} record The record or index of the record to check
24700      * @return {Boolean}
24701      */
24702     isSelected : function(index){
24703             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24704         return (r && this.selections.key(r.id) ? true : false);
24705     },
24706
24707     /**
24708      * Returns True if the specified record id is selected.
24709      * @param {String} id The id of record to check
24710      * @return {Boolean}
24711      */
24712     isIdSelected : function(id){
24713         return (this.selections.key(id) ? true : false);
24714     },
24715
24716
24717     // private
24718     handleMouseDBClick : function(e, t){
24719         
24720     },
24721     // private
24722     handleMouseDown : function(e, t)
24723     {
24724             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24725         if(this.isLocked() || rowIndex < 0 ){
24726             return;
24727         };
24728         if(e.shiftKey && this.last !== false){
24729             var last = this.last;
24730             this.selectRange(last, rowIndex, e.ctrlKey);
24731             this.last = last; // reset the last
24732             t.focus();
24733     
24734         }else{
24735             var isSelected = this.isSelected(rowIndex);
24736             //Roo.log("select row:" + rowIndex);
24737             if(isSelected){
24738                 this.deselectRow(rowIndex);
24739             } else {
24740                         this.selectRow(rowIndex, true);
24741             }
24742     
24743             /*
24744                 if(e.button !== 0 && isSelected){
24745                 alert('rowIndex 2: ' + rowIndex);
24746                     view.focusRow(rowIndex);
24747                 }else if(e.ctrlKey && isSelected){
24748                     this.deselectRow(rowIndex);
24749                 }else if(!isSelected){
24750                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24751                     view.focusRow(rowIndex);
24752                 }
24753             */
24754         }
24755         this.fireEvent("afterselectionchange", this);
24756     },
24757     // private
24758     handleDragableRowClick :  function(grid, rowIndex, e) 
24759     {
24760         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24761             this.selectRow(rowIndex, false);
24762             grid.view.focusRow(rowIndex);
24763              this.fireEvent("afterselectionchange", this);
24764         }
24765     },
24766     
24767     /**
24768      * Selects multiple rows.
24769      * @param {Array} rows Array of the indexes of the row to select
24770      * @param {Boolean} keepExisting (optional) True to keep existing selections
24771      */
24772     selectRows : function(rows, keepExisting){
24773         if(!keepExisting){
24774             this.clearSelections();
24775         }
24776         for(var i = 0, len = rows.length; i < len; i++){
24777             this.selectRow(rows[i], true);
24778         }
24779     },
24780
24781     /**
24782      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24783      * @param {Number} startRow The index of the first row in the range
24784      * @param {Number} endRow The index of the last row in the range
24785      * @param {Boolean} keepExisting (optional) True to retain existing selections
24786      */
24787     selectRange : function(startRow, endRow, keepExisting){
24788         if(this.locked) {
24789             return;
24790         }
24791         if(!keepExisting){
24792             this.clearSelections();
24793         }
24794         if(startRow <= endRow){
24795             for(var i = startRow; i <= endRow; i++){
24796                 this.selectRow(i, true);
24797             }
24798         }else{
24799             for(var i = startRow; i >= endRow; i--){
24800                 this.selectRow(i, true);
24801             }
24802         }
24803     },
24804
24805     /**
24806      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24807      * @param {Number} startRow The index of the first row in the range
24808      * @param {Number} endRow The index of the last row in the range
24809      */
24810     deselectRange : function(startRow, endRow, preventViewNotify){
24811         if(this.locked) {
24812             return;
24813         }
24814         for(var i = startRow; i <= endRow; i++){
24815             this.deselectRow(i, preventViewNotify);
24816         }
24817     },
24818
24819     /**
24820      * Selects a row.
24821      * @param {Number} row The index of the row to select
24822      * @param {Boolean} keepExisting (optional) True to keep existing selections
24823      */
24824     selectRow : function(index, keepExisting, preventViewNotify)
24825     {
24826             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24827             return;
24828         }
24829         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24830             if(!keepExisting || this.singleSelect){
24831                 this.clearSelections();
24832             }
24833             
24834             var r = this.grid.store.getAt(index);
24835             //console.log('selectRow - record id :' + r.id);
24836             
24837             this.selections.add(r);
24838             this.last = this.lastActive = index;
24839             if(!preventViewNotify){
24840                 var proxy = new Roo.Element(
24841                                 this.grid.getRowDom(index)
24842                 );
24843                 proxy.addClass('bg-info info');
24844             }
24845             this.fireEvent("rowselect", this, index, r);
24846             this.fireEvent("selectionchange", this);
24847         }
24848     },
24849
24850     /**
24851      * Deselects a row.
24852      * @param {Number} row The index of the row to deselect
24853      */
24854     deselectRow : function(index, preventViewNotify)
24855     {
24856         if(this.locked) {
24857             return;
24858         }
24859         if(this.last == index){
24860             this.last = false;
24861         }
24862         if(this.lastActive == index){
24863             this.lastActive = false;
24864         }
24865         
24866         var r = this.grid.store.getAt(index);
24867         if (!r) {
24868             return;
24869         }
24870         
24871         this.selections.remove(r);
24872         //.console.log('deselectRow - record id :' + r.id);
24873         if(!preventViewNotify){
24874         
24875             var proxy = new Roo.Element(
24876                 this.grid.getRowDom(index)
24877             );
24878             proxy.removeClass('bg-info info');
24879         }
24880         this.fireEvent("rowdeselect", this, index);
24881         this.fireEvent("selectionchange", this);
24882     },
24883
24884     // private
24885     restoreLast : function(){
24886         if(this._last){
24887             this.last = this._last;
24888         }
24889     },
24890
24891     // private
24892     acceptsNav : function(row, col, cm){
24893         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24894     },
24895
24896     // private
24897     onEditorKey : function(field, e){
24898         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24899         if(k == e.TAB){
24900             e.stopEvent();
24901             ed.completeEdit();
24902             if(e.shiftKey){
24903                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24904             }else{
24905                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24906             }
24907         }else if(k == e.ENTER && !e.ctrlKey){
24908             e.stopEvent();
24909             ed.completeEdit();
24910             if(e.shiftKey){
24911                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24912             }else{
24913                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24914             }
24915         }else if(k == e.ESC){
24916             ed.cancelEdit();
24917         }
24918         if(newCell){
24919             g.startEditing(newCell[0], newCell[1]);
24920         }
24921     }
24922 });
24923 /*
24924  * Based on:
24925  * Ext JS Library 1.1.1
24926  * Copyright(c) 2006-2007, Ext JS, LLC.
24927  *
24928  * Originally Released Under LGPL - original licence link has changed is not relivant.
24929  *
24930  * Fork - LGPL
24931  * <script type="text/javascript">
24932  */
24933  
24934 /**
24935  * @class Roo.bootstrap.PagingToolbar
24936  * @extends Roo.bootstrap.NavSimplebar
24937  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24938  * @constructor
24939  * Create a new PagingToolbar
24940  * @param {Object} config The config object
24941  * @param {Roo.data.Store} store
24942  */
24943 Roo.bootstrap.PagingToolbar = function(config)
24944 {
24945     // old args format still supported... - xtype is prefered..
24946         // created from xtype...
24947     
24948     this.ds = config.dataSource;
24949     
24950     if (config.store && !this.ds) {
24951         this.store= Roo.factory(config.store, Roo.data);
24952         this.ds = this.store;
24953         this.ds.xmodule = this.xmodule || false;
24954     }
24955     
24956     this.toolbarItems = [];
24957     if (config.items) {
24958         this.toolbarItems = config.items;
24959     }
24960     
24961     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24962     
24963     this.cursor = 0;
24964     
24965     if (this.ds) { 
24966         this.bind(this.ds);
24967     }
24968     
24969     if (Roo.bootstrap.version == 4) {
24970         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24971     } else {
24972         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24973     }
24974     
24975 };
24976
24977 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24978     /**
24979      * @cfg {Roo.data.Store} dataSource
24980      * The underlying data store providing the paged data
24981      */
24982     /**
24983      * @cfg {String/HTMLElement/Element} container
24984      * container The id or element that will contain the toolbar
24985      */
24986     /**
24987      * @cfg {Boolean} displayInfo
24988      * True to display the displayMsg (defaults to false)
24989      */
24990     /**
24991      * @cfg {Number} pageSize
24992      * The number of records to display per page (defaults to 20)
24993      */
24994     pageSize: 20,
24995     /**
24996      * @cfg {String} displayMsg
24997      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24998      */
24999     displayMsg : 'Displaying {0} - {1} of {2}',
25000     /**
25001      * @cfg {String} emptyMsg
25002      * The message to display when no records are found (defaults to "No data to display")
25003      */
25004     emptyMsg : 'No data to display',
25005     /**
25006      * Customizable piece of the default paging text (defaults to "Page")
25007      * @type String
25008      */
25009     beforePageText : "Page",
25010     /**
25011      * Customizable piece of the default paging text (defaults to "of %0")
25012      * @type String
25013      */
25014     afterPageText : "of {0}",
25015     /**
25016      * Customizable piece of the default paging text (defaults to "First Page")
25017      * @type String
25018      */
25019     firstText : "First Page",
25020     /**
25021      * Customizable piece of the default paging text (defaults to "Previous Page")
25022      * @type String
25023      */
25024     prevText : "Previous Page",
25025     /**
25026      * Customizable piece of the default paging text (defaults to "Next Page")
25027      * @type String
25028      */
25029     nextText : "Next Page",
25030     /**
25031      * Customizable piece of the default paging text (defaults to "Last Page")
25032      * @type String
25033      */
25034     lastText : "Last Page",
25035     /**
25036      * Customizable piece of the default paging text (defaults to "Refresh")
25037      * @type String
25038      */
25039     refreshText : "Refresh",
25040
25041     buttons : false,
25042     // private
25043     onRender : function(ct, position) 
25044     {
25045         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
25046         this.navgroup.parentId = this.id;
25047         this.navgroup.onRender(this.el, null);
25048         // add the buttons to the navgroup
25049         
25050         if(this.displayInfo){
25051             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
25052             this.displayEl = this.el.select('.x-paging-info', true).first();
25053 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
25054 //            this.displayEl = navel.el.select('span',true).first();
25055         }
25056         
25057         var _this = this;
25058         
25059         if(this.buttons){
25060             Roo.each(_this.buttons, function(e){ // this might need to use render????
25061                Roo.factory(e).render(_this.el);
25062             });
25063         }
25064             
25065         Roo.each(_this.toolbarItems, function(e) {
25066             _this.navgroup.addItem(e);
25067         });
25068         
25069         
25070         this.first = this.navgroup.addItem({
25071             tooltip: this.firstText,
25072             cls: "prev btn-outline-secondary",
25073             html : ' <i class="fa fa-step-backward"></i>',
25074             disabled: true,
25075             preventDefault: true,
25076             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
25077         });
25078         
25079         this.prev =  this.navgroup.addItem({
25080             tooltip: this.prevText,
25081             cls: "prev btn-outline-secondary",
25082             html : ' <i class="fa fa-backward"></i>',
25083             disabled: true,
25084             preventDefault: true,
25085             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
25086         });
25087     //this.addSeparator();
25088         
25089         
25090         var field = this.navgroup.addItem( {
25091             tagtype : 'span',
25092             cls : 'x-paging-position  btn-outline-secondary',
25093              disabled: true,
25094             html : this.beforePageText  +
25095                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
25096                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
25097          } ); //?? escaped?
25098         
25099         this.field = field.el.select('input', true).first();
25100         this.field.on("keydown", this.onPagingKeydown, this);
25101         this.field.on("focus", function(){this.dom.select();});
25102     
25103     
25104         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
25105         //this.field.setHeight(18);
25106         //this.addSeparator();
25107         this.next = this.navgroup.addItem({
25108             tooltip: this.nextText,
25109             cls: "next btn-outline-secondary",
25110             html : ' <i class="fa fa-forward"></i>',
25111             disabled: true,
25112             preventDefault: true,
25113             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
25114         });
25115         this.last = this.navgroup.addItem({
25116             tooltip: this.lastText,
25117             html : ' <i class="fa fa-step-forward"></i>',
25118             cls: "next btn-outline-secondary",
25119             disabled: true,
25120             preventDefault: true,
25121             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
25122         });
25123     //this.addSeparator();
25124         this.loading = this.navgroup.addItem({
25125             tooltip: this.refreshText,
25126             cls: "btn-outline-secondary",
25127             html : ' <i class="fa fa-refresh"></i>',
25128             preventDefault: true,
25129             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
25130         });
25131         
25132     },
25133
25134     // private
25135     updateInfo : function(){
25136         if(this.displayEl){
25137             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
25138             var msg = count == 0 ?
25139                 this.emptyMsg :
25140                 String.format(
25141                     this.displayMsg,
25142                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
25143                 );
25144             this.displayEl.update(msg);
25145         }
25146     },
25147
25148     // private
25149     onLoad : function(ds, r, o)
25150     {
25151         this.cursor = o.params.start ? o.params.start : 0;
25152         
25153         var d = this.getPageData(),
25154             ap = d.activePage,
25155             ps = d.pages;
25156         
25157         
25158         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
25159         this.field.dom.value = ap;
25160         this.first.setDisabled(ap == 1);
25161         this.prev.setDisabled(ap == 1);
25162         this.next.setDisabled(ap == ps);
25163         this.last.setDisabled(ap == ps);
25164         this.loading.enable();
25165         this.updateInfo();
25166     },
25167
25168     // private
25169     getPageData : function(){
25170         var total = this.ds.getTotalCount();
25171         return {
25172             total : total,
25173             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
25174             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
25175         };
25176     },
25177
25178     // private
25179     onLoadError : function(){
25180         this.loading.enable();
25181     },
25182
25183     // private
25184     onPagingKeydown : function(e){
25185         var k = e.getKey();
25186         var d = this.getPageData();
25187         if(k == e.RETURN){
25188             var v = this.field.dom.value, pageNum;
25189             if(!v || isNaN(pageNum = parseInt(v, 10))){
25190                 this.field.dom.value = d.activePage;
25191                 return;
25192             }
25193             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
25194             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25195             e.stopEvent();
25196         }
25197         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))
25198         {
25199           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
25200           this.field.dom.value = pageNum;
25201           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
25202           e.stopEvent();
25203         }
25204         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
25205         {
25206           var v = this.field.dom.value, pageNum; 
25207           var increment = (e.shiftKey) ? 10 : 1;
25208           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
25209                 increment *= -1;
25210           }
25211           if(!v || isNaN(pageNum = parseInt(v, 10))) {
25212             this.field.dom.value = d.activePage;
25213             return;
25214           }
25215           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25216           {
25217             this.field.dom.value = parseInt(v, 10) + increment;
25218             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25219             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25220           }
25221           e.stopEvent();
25222         }
25223     },
25224
25225     // private
25226     beforeLoad : function(){
25227         if(this.loading){
25228             this.loading.disable();
25229         }
25230     },
25231
25232     // private
25233     onClick : function(which){
25234         
25235         var ds = this.ds;
25236         if (!ds) {
25237             return;
25238         }
25239         
25240         switch(which){
25241             case "first":
25242                 ds.load({params:{start: 0, limit: this.pageSize}});
25243             break;
25244             case "prev":
25245                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25246             break;
25247             case "next":
25248                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25249             break;
25250             case "last":
25251                 var total = ds.getTotalCount();
25252                 var extra = total % this.pageSize;
25253                 var lastStart = extra ? (total - extra) : total-this.pageSize;
25254                 ds.load({params:{start: lastStart, limit: this.pageSize}});
25255             break;
25256             case "refresh":
25257                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25258             break;
25259         }
25260     },
25261
25262     /**
25263      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25264      * @param {Roo.data.Store} store The data store to unbind
25265      */
25266     unbind : function(ds){
25267         ds.un("beforeload", this.beforeLoad, this);
25268         ds.un("load", this.onLoad, this);
25269         ds.un("loadexception", this.onLoadError, this);
25270         ds.un("remove", this.updateInfo, this);
25271         ds.un("add", this.updateInfo, this);
25272         this.ds = undefined;
25273     },
25274
25275     /**
25276      * Binds the paging toolbar to the specified {@link Roo.data.Store}
25277      * @param {Roo.data.Store} store The data store to bind
25278      */
25279     bind : function(ds){
25280         ds.on("beforeload", this.beforeLoad, this);
25281         ds.on("load", this.onLoad, this);
25282         ds.on("loadexception", this.onLoadError, this);
25283         ds.on("remove", this.updateInfo, this);
25284         ds.on("add", this.updateInfo, this);
25285         this.ds = ds;
25286     }
25287 });/*
25288  * - LGPL
25289  *
25290  * element
25291  * 
25292  */
25293
25294 /**
25295  * @class Roo.bootstrap.MessageBar
25296  * @extends Roo.bootstrap.Component
25297  * Bootstrap MessageBar class
25298  * @cfg {String} html contents of the MessageBar
25299  * @cfg {String} weight (info | success | warning | danger) default info
25300  * @cfg {String} beforeClass insert the bar before the given class
25301  * @cfg {Boolean} closable (true | false) default false
25302  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25303  * 
25304  * @constructor
25305  * Create a new Element
25306  * @param {Object} config The config object
25307  */
25308
25309 Roo.bootstrap.MessageBar = function(config){
25310     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25311 };
25312
25313 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25314     
25315     html: '',
25316     weight: 'info',
25317     closable: false,
25318     fixed: false,
25319     beforeClass: 'bootstrap-sticky-wrap',
25320     
25321     getAutoCreate : function(){
25322         
25323         var cfg = {
25324             tag: 'div',
25325             cls: 'alert alert-dismissable alert-' + this.weight,
25326             cn: [
25327                 {
25328                     tag: 'span',
25329                     cls: 'message',
25330                     html: this.html || ''
25331                 }
25332             ]
25333         };
25334         
25335         if(this.fixed){
25336             cfg.cls += ' alert-messages-fixed';
25337         }
25338         
25339         if(this.closable){
25340             cfg.cn.push({
25341                 tag: 'button',
25342                 cls: 'close',
25343                 html: 'x'
25344             });
25345         }
25346         
25347         return cfg;
25348     },
25349     
25350     onRender : function(ct, position)
25351     {
25352         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25353         
25354         if(!this.el){
25355             var cfg = Roo.apply({},  this.getAutoCreate());
25356             cfg.id = Roo.id();
25357             
25358             if (this.cls) {
25359                 cfg.cls += ' ' + this.cls;
25360             }
25361             if (this.style) {
25362                 cfg.style = this.style;
25363             }
25364             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25365             
25366             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25367         }
25368         
25369         this.el.select('>button.close').on('click', this.hide, this);
25370         
25371     },
25372     
25373     show : function()
25374     {
25375         if (!this.rendered) {
25376             this.render();
25377         }
25378         
25379         this.el.show();
25380         
25381         this.fireEvent('show', this);
25382         
25383     },
25384     
25385     hide : function()
25386     {
25387         if (!this.rendered) {
25388             this.render();
25389         }
25390         
25391         this.el.hide();
25392         
25393         this.fireEvent('hide', this);
25394     },
25395     
25396     update : function()
25397     {
25398 //        var e = this.el.dom.firstChild;
25399 //        
25400 //        if(this.closable){
25401 //            e = e.nextSibling;
25402 //        }
25403 //        
25404 //        e.data = this.html || '';
25405
25406         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25407     }
25408    
25409 });
25410
25411  
25412
25413      /*
25414  * - LGPL
25415  *
25416  * Graph
25417  * 
25418  */
25419
25420
25421 /**
25422  * @class Roo.bootstrap.Graph
25423  * @extends Roo.bootstrap.Component
25424  * Bootstrap Graph class
25425 > Prameters
25426  -sm {number} sm 4
25427  -md {number} md 5
25428  @cfg {String} graphtype  bar | vbar | pie
25429  @cfg {number} g_x coodinator | centre x (pie)
25430  @cfg {number} g_y coodinator | centre y (pie)
25431  @cfg {number} g_r radius (pie)
25432  @cfg {number} g_height height of the chart (respected by all elements in the set)
25433  @cfg {number} g_width width of the chart (respected by all elements in the set)
25434  @cfg {Object} title The title of the chart
25435     
25436  -{Array}  values
25437  -opts (object) options for the chart 
25438      o {
25439      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25440      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25441      o vgutter (number)
25442      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.
25443      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25444      o to
25445      o stretch (boolean)
25446      o }
25447  -opts (object) options for the pie
25448      o{
25449      o cut
25450      o startAngle (number)
25451      o endAngle (number)
25452      } 
25453  *
25454  * @constructor
25455  * Create a new Input
25456  * @param {Object} config The config object
25457  */
25458
25459 Roo.bootstrap.Graph = function(config){
25460     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25461     
25462     this.addEvents({
25463         // img events
25464         /**
25465          * @event click
25466          * The img click event for the img.
25467          * @param {Roo.EventObject} e
25468          */
25469         "click" : true
25470     });
25471 };
25472
25473 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25474     
25475     sm: 4,
25476     md: 5,
25477     graphtype: 'bar',
25478     g_height: 250,
25479     g_width: 400,
25480     g_x: 50,
25481     g_y: 50,
25482     g_r: 30,
25483     opts:{
25484         //g_colors: this.colors,
25485         g_type: 'soft',
25486         g_gutter: '20%'
25487
25488     },
25489     title : false,
25490
25491     getAutoCreate : function(){
25492         
25493         var cfg = {
25494             tag: 'div',
25495             html : null
25496         };
25497         
25498         
25499         return  cfg;
25500     },
25501
25502     onRender : function(ct,position){
25503         
25504         
25505         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25506         
25507         if (typeof(Raphael) == 'undefined') {
25508             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25509             return;
25510         }
25511         
25512         this.raphael = Raphael(this.el.dom);
25513         
25514                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25515                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25516                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25517                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25518                 /*
25519                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25520                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25521                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25522                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25523                 
25524                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25525                 r.barchart(330, 10, 300, 220, data1);
25526                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25527                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25528                 */
25529                 
25530                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25531                 // r.barchart(30, 30, 560, 250,  xdata, {
25532                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25533                 //     axis : "0 0 1 1",
25534                 //     axisxlabels :  xdata
25535                 //     //yvalues : cols,
25536                    
25537                 // });
25538 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25539 //        
25540 //        this.load(null,xdata,{
25541 //                axis : "0 0 1 1",
25542 //                axisxlabels :  xdata
25543 //                });
25544
25545     },
25546
25547     load : function(graphtype,xdata,opts)
25548     {
25549         this.raphael.clear();
25550         if(!graphtype) {
25551             graphtype = this.graphtype;
25552         }
25553         if(!opts){
25554             opts = this.opts;
25555         }
25556         var r = this.raphael,
25557             fin = function () {
25558                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25559             },
25560             fout = function () {
25561                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25562             },
25563             pfin = function() {
25564                 this.sector.stop();
25565                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25566
25567                 if (this.label) {
25568                     this.label[0].stop();
25569                     this.label[0].attr({ r: 7.5 });
25570                     this.label[1].attr({ "font-weight": 800 });
25571                 }
25572             },
25573             pfout = function() {
25574                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25575
25576                 if (this.label) {
25577                     this.label[0].animate({ r: 5 }, 500, "bounce");
25578                     this.label[1].attr({ "font-weight": 400 });
25579                 }
25580             };
25581
25582         switch(graphtype){
25583             case 'bar':
25584                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25585                 break;
25586             case 'hbar':
25587                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25588                 break;
25589             case 'pie':
25590 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25591 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25592 //            
25593                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25594                 
25595                 break;
25596
25597         }
25598         
25599         if(this.title){
25600             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25601         }
25602         
25603     },
25604     
25605     setTitle: function(o)
25606     {
25607         this.title = o;
25608     },
25609     
25610     initEvents: function() {
25611         
25612         if(!this.href){
25613             this.el.on('click', this.onClick, this);
25614         }
25615     },
25616     
25617     onClick : function(e)
25618     {
25619         Roo.log('img onclick');
25620         this.fireEvent('click', this, e);
25621     }
25622    
25623 });
25624
25625  
25626 /*
25627  * - LGPL
25628  *
25629  * numberBox
25630  * 
25631  */
25632 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25633
25634 /**
25635  * @class Roo.bootstrap.dash.NumberBox
25636  * @extends Roo.bootstrap.Component
25637  * Bootstrap NumberBox class
25638  * @cfg {String} headline Box headline
25639  * @cfg {String} content Box content
25640  * @cfg {String} icon Box icon
25641  * @cfg {String} footer Footer text
25642  * @cfg {String} fhref Footer href
25643  * 
25644  * @constructor
25645  * Create a new NumberBox
25646  * @param {Object} config The config object
25647  */
25648
25649
25650 Roo.bootstrap.dash.NumberBox = function(config){
25651     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25652     
25653 };
25654
25655 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25656     
25657     headline : '',
25658     content : '',
25659     icon : '',
25660     footer : '',
25661     fhref : '',
25662     ficon : '',
25663     
25664     getAutoCreate : function(){
25665         
25666         var cfg = {
25667             tag : 'div',
25668             cls : 'small-box ',
25669             cn : [
25670                 {
25671                     tag : 'div',
25672                     cls : 'inner',
25673                     cn :[
25674                         {
25675                             tag : 'h3',
25676                             cls : 'roo-headline',
25677                             html : this.headline
25678                         },
25679                         {
25680                             tag : 'p',
25681                             cls : 'roo-content',
25682                             html : this.content
25683                         }
25684                     ]
25685                 }
25686             ]
25687         };
25688         
25689         if(this.icon){
25690             cfg.cn.push({
25691                 tag : 'div',
25692                 cls : 'icon',
25693                 cn :[
25694                     {
25695                         tag : 'i',
25696                         cls : 'ion ' + this.icon
25697                     }
25698                 ]
25699             });
25700         }
25701         
25702         if(this.footer){
25703             var footer = {
25704                 tag : 'a',
25705                 cls : 'small-box-footer',
25706                 href : this.fhref || '#',
25707                 html : this.footer
25708             };
25709             
25710             cfg.cn.push(footer);
25711             
25712         }
25713         
25714         return  cfg;
25715     },
25716
25717     onRender : function(ct,position){
25718         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25719
25720
25721        
25722                 
25723     },
25724
25725     setHeadline: function (value)
25726     {
25727         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25728     },
25729     
25730     setFooter: function (value, href)
25731     {
25732         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25733         
25734         if(href){
25735             this.el.select('a.small-box-footer',true).first().attr('href', href);
25736         }
25737         
25738     },
25739
25740     setContent: function (value)
25741     {
25742         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25743     },
25744
25745     initEvents: function() 
25746     {   
25747         
25748     }
25749     
25750 });
25751
25752  
25753 /*
25754  * - LGPL
25755  *
25756  * TabBox
25757  * 
25758  */
25759 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25760
25761 /**
25762  * @class Roo.bootstrap.dash.TabBox
25763  * @extends Roo.bootstrap.Component
25764  * Bootstrap TabBox class
25765  * @cfg {String} title Title of the TabBox
25766  * @cfg {String} icon Icon of the TabBox
25767  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25768  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25769  * 
25770  * @constructor
25771  * Create a new TabBox
25772  * @param {Object} config The config object
25773  */
25774
25775
25776 Roo.bootstrap.dash.TabBox = function(config){
25777     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25778     this.addEvents({
25779         // raw events
25780         /**
25781          * @event addpane
25782          * When a pane is added
25783          * @param {Roo.bootstrap.dash.TabPane} pane
25784          */
25785         "addpane" : true,
25786         /**
25787          * @event activatepane
25788          * When a pane is activated
25789          * @param {Roo.bootstrap.dash.TabPane} pane
25790          */
25791         "activatepane" : true
25792         
25793          
25794     });
25795     
25796     this.panes = [];
25797 };
25798
25799 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25800
25801     title : '',
25802     icon : false,
25803     showtabs : true,
25804     tabScrollable : false,
25805     
25806     getChildContainer : function()
25807     {
25808         return this.el.select('.tab-content', true).first();
25809     },
25810     
25811     getAutoCreate : function(){
25812         
25813         var header = {
25814             tag: 'li',
25815             cls: 'pull-left header',
25816             html: this.title,
25817             cn : []
25818         };
25819         
25820         if(this.icon){
25821             header.cn.push({
25822                 tag: 'i',
25823                 cls: 'fa ' + this.icon
25824             });
25825         }
25826         
25827         var h = {
25828             tag: 'ul',
25829             cls: 'nav nav-tabs pull-right',
25830             cn: [
25831                 header
25832             ]
25833         };
25834         
25835         if(this.tabScrollable){
25836             h = {
25837                 tag: 'div',
25838                 cls: 'tab-header',
25839                 cn: [
25840                     {
25841                         tag: 'ul',
25842                         cls: 'nav nav-tabs pull-right',
25843                         cn: [
25844                             header
25845                         ]
25846                     }
25847                 ]
25848             };
25849         }
25850         
25851         var cfg = {
25852             tag: 'div',
25853             cls: 'nav-tabs-custom',
25854             cn: [
25855                 h,
25856                 {
25857                     tag: 'div',
25858                     cls: 'tab-content no-padding',
25859                     cn: []
25860                 }
25861             ]
25862         };
25863
25864         return  cfg;
25865     },
25866     initEvents : function()
25867     {
25868         //Roo.log('add add pane handler');
25869         this.on('addpane', this.onAddPane, this);
25870     },
25871      /**
25872      * Updates the box title
25873      * @param {String} html to set the title to.
25874      */
25875     setTitle : function(value)
25876     {
25877         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25878     },
25879     onAddPane : function(pane)
25880     {
25881         this.panes.push(pane);
25882         //Roo.log('addpane');
25883         //Roo.log(pane);
25884         // tabs are rendere left to right..
25885         if(!this.showtabs){
25886             return;
25887         }
25888         
25889         var ctr = this.el.select('.nav-tabs', true).first();
25890          
25891          
25892         var existing = ctr.select('.nav-tab',true);
25893         var qty = existing.getCount();;
25894         
25895         
25896         var tab = ctr.createChild({
25897             tag : 'li',
25898             cls : 'nav-tab' + (qty ? '' : ' active'),
25899             cn : [
25900                 {
25901                     tag : 'a',
25902                     href:'#',
25903                     html : pane.title
25904                 }
25905             ]
25906         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25907         pane.tab = tab;
25908         
25909         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25910         if (!qty) {
25911             pane.el.addClass('active');
25912         }
25913         
25914                 
25915     },
25916     onTabClick : function(ev,un,ob,pane)
25917     {
25918         //Roo.log('tab - prev default');
25919         ev.preventDefault();
25920         
25921         
25922         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25923         pane.tab.addClass('active');
25924         //Roo.log(pane.title);
25925         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25926         // technically we should have a deactivate event.. but maybe add later.
25927         // and it should not de-activate the selected tab...
25928         this.fireEvent('activatepane', pane);
25929         pane.el.addClass('active');
25930         pane.fireEvent('activate');
25931         
25932         
25933     },
25934     
25935     getActivePane : function()
25936     {
25937         var r = false;
25938         Roo.each(this.panes, function(p) {
25939             if(p.el.hasClass('active')){
25940                 r = p;
25941                 return false;
25942             }
25943             
25944             return;
25945         });
25946         
25947         return r;
25948     }
25949     
25950     
25951 });
25952
25953  
25954 /*
25955  * - LGPL
25956  *
25957  * Tab pane
25958  * 
25959  */
25960 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25961 /**
25962  * @class Roo.bootstrap.TabPane
25963  * @extends Roo.bootstrap.Component
25964  * Bootstrap TabPane class
25965  * @cfg {Boolean} active (false | true) Default false
25966  * @cfg {String} title title of panel
25967
25968  * 
25969  * @constructor
25970  * Create a new TabPane
25971  * @param {Object} config The config object
25972  */
25973
25974 Roo.bootstrap.dash.TabPane = function(config){
25975     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25976     
25977     this.addEvents({
25978         // raw events
25979         /**
25980          * @event activate
25981          * When a pane is activated
25982          * @param {Roo.bootstrap.dash.TabPane} pane
25983          */
25984         "activate" : true
25985          
25986     });
25987 };
25988
25989 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25990     
25991     active : false,
25992     title : '',
25993     
25994     // the tabBox that this is attached to.
25995     tab : false,
25996      
25997     getAutoCreate : function() 
25998     {
25999         var cfg = {
26000             tag: 'div',
26001             cls: 'tab-pane'
26002         };
26003         
26004         if(this.active){
26005             cfg.cls += ' active';
26006         }
26007         
26008         return cfg;
26009     },
26010     initEvents  : function()
26011     {
26012         //Roo.log('trigger add pane handler');
26013         this.parent().fireEvent('addpane', this)
26014     },
26015     
26016      /**
26017      * Updates the tab title 
26018      * @param {String} html to set the title to.
26019      */
26020     setTitle: function(str)
26021     {
26022         if (!this.tab) {
26023             return;
26024         }
26025         this.title = str;
26026         this.tab.select('a', true).first().dom.innerHTML = str;
26027         
26028     }
26029     
26030     
26031     
26032 });
26033
26034  
26035
26036
26037  /*
26038  * - LGPL
26039  *
26040  * menu
26041  * 
26042  */
26043 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26044
26045 /**
26046  * @class Roo.bootstrap.menu.Menu
26047  * @extends Roo.bootstrap.Component
26048  * Bootstrap Menu class - container for Menu
26049  * @cfg {String} html Text of the menu
26050  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
26051  * @cfg {String} icon Font awesome icon
26052  * @cfg {String} pos Menu align to (top | bottom) default bottom
26053  * 
26054  * 
26055  * @constructor
26056  * Create a new Menu
26057  * @param {Object} config The config object
26058  */
26059
26060
26061 Roo.bootstrap.menu.Menu = function(config){
26062     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
26063     
26064     this.addEvents({
26065         /**
26066          * @event beforeshow
26067          * Fires before this menu is displayed
26068          * @param {Roo.bootstrap.menu.Menu} this
26069          */
26070         beforeshow : true,
26071         /**
26072          * @event beforehide
26073          * Fires before this menu is hidden
26074          * @param {Roo.bootstrap.menu.Menu} this
26075          */
26076         beforehide : true,
26077         /**
26078          * @event show
26079          * Fires after this menu is displayed
26080          * @param {Roo.bootstrap.menu.Menu} this
26081          */
26082         show : true,
26083         /**
26084          * @event hide
26085          * Fires after this menu is hidden
26086          * @param {Roo.bootstrap.menu.Menu} this
26087          */
26088         hide : true,
26089         /**
26090          * @event click
26091          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
26092          * @param {Roo.bootstrap.menu.Menu} this
26093          * @param {Roo.EventObject} e
26094          */
26095         click : true
26096     });
26097     
26098 };
26099
26100 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
26101     
26102     submenu : false,
26103     html : '',
26104     weight : 'default',
26105     icon : false,
26106     pos : 'bottom',
26107     
26108     
26109     getChildContainer : function() {
26110         if(this.isSubMenu){
26111             return this.el;
26112         }
26113         
26114         return this.el.select('ul.dropdown-menu', true).first();  
26115     },
26116     
26117     getAutoCreate : function()
26118     {
26119         var text = [
26120             {
26121                 tag : 'span',
26122                 cls : 'roo-menu-text',
26123                 html : this.html
26124             }
26125         ];
26126         
26127         if(this.icon){
26128             text.unshift({
26129                 tag : 'i',
26130                 cls : 'fa ' + this.icon
26131             })
26132         }
26133         
26134         
26135         var cfg = {
26136             tag : 'div',
26137             cls : 'btn-group',
26138             cn : [
26139                 {
26140                     tag : 'button',
26141                     cls : 'dropdown-button btn btn-' + this.weight,
26142                     cn : text
26143                 },
26144                 {
26145                     tag : 'button',
26146                     cls : 'dropdown-toggle btn btn-' + this.weight,
26147                     cn : [
26148                         {
26149                             tag : 'span',
26150                             cls : 'caret'
26151                         }
26152                     ]
26153                 },
26154                 {
26155                     tag : 'ul',
26156                     cls : 'dropdown-menu'
26157                 }
26158             ]
26159             
26160         };
26161         
26162         if(this.pos == 'top'){
26163             cfg.cls += ' dropup';
26164         }
26165         
26166         if(this.isSubMenu){
26167             cfg = {
26168                 tag : 'ul',
26169                 cls : 'dropdown-menu'
26170             }
26171         }
26172         
26173         return cfg;
26174     },
26175     
26176     onRender : function(ct, position)
26177     {
26178         this.isSubMenu = ct.hasClass('dropdown-submenu');
26179         
26180         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
26181     },
26182     
26183     initEvents : function() 
26184     {
26185         if(this.isSubMenu){
26186             return;
26187         }
26188         
26189         this.hidden = true;
26190         
26191         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
26192         this.triggerEl.on('click', this.onTriggerPress, this);
26193         
26194         this.buttonEl = this.el.select('button.dropdown-button', true).first();
26195         this.buttonEl.on('click', this.onClick, this);
26196         
26197     },
26198     
26199     list : function()
26200     {
26201         if(this.isSubMenu){
26202             return this.el;
26203         }
26204         
26205         return this.el.select('ul.dropdown-menu', true).first();
26206     },
26207     
26208     onClick : function(e)
26209     {
26210         this.fireEvent("click", this, e);
26211     },
26212     
26213     onTriggerPress  : function(e)
26214     {   
26215         if (this.isVisible()) {
26216             this.hide();
26217         } else {
26218             this.show();
26219         }
26220     },
26221     
26222     isVisible : function(){
26223         return !this.hidden;
26224     },
26225     
26226     show : function()
26227     {
26228         this.fireEvent("beforeshow", this);
26229         
26230         this.hidden = false;
26231         this.el.addClass('open');
26232         
26233         Roo.get(document).on("mouseup", this.onMouseUp, this);
26234         
26235         this.fireEvent("show", this);
26236         
26237         
26238     },
26239     
26240     hide : function()
26241     {
26242         this.fireEvent("beforehide", this);
26243         
26244         this.hidden = true;
26245         this.el.removeClass('open');
26246         
26247         Roo.get(document).un("mouseup", this.onMouseUp);
26248         
26249         this.fireEvent("hide", this);
26250     },
26251     
26252     onMouseUp : function()
26253     {
26254         this.hide();
26255     }
26256     
26257 });
26258
26259  
26260  /*
26261  * - LGPL
26262  *
26263  * menu item
26264  * 
26265  */
26266 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26267
26268 /**
26269  * @class Roo.bootstrap.menu.Item
26270  * @extends Roo.bootstrap.Component
26271  * Bootstrap MenuItem class
26272  * @cfg {Boolean} submenu (true | false) default false
26273  * @cfg {String} html text of the item
26274  * @cfg {String} href the link
26275  * @cfg {Boolean} disable (true | false) default false
26276  * @cfg {Boolean} preventDefault (true | false) default true
26277  * @cfg {String} icon Font awesome icon
26278  * @cfg {String} pos Submenu align to (left | right) default right 
26279  * 
26280  * 
26281  * @constructor
26282  * Create a new Item
26283  * @param {Object} config The config object
26284  */
26285
26286
26287 Roo.bootstrap.menu.Item = function(config){
26288     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26289     this.addEvents({
26290         /**
26291          * @event mouseover
26292          * Fires when the mouse is hovering over this menu
26293          * @param {Roo.bootstrap.menu.Item} this
26294          * @param {Roo.EventObject} e
26295          */
26296         mouseover : true,
26297         /**
26298          * @event mouseout
26299          * Fires when the mouse exits this menu
26300          * @param {Roo.bootstrap.menu.Item} this
26301          * @param {Roo.EventObject} e
26302          */
26303         mouseout : true,
26304         // raw events
26305         /**
26306          * @event click
26307          * The raw click event for the entire grid.
26308          * @param {Roo.EventObject} e
26309          */
26310         click : true
26311     });
26312 };
26313
26314 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26315     
26316     submenu : false,
26317     href : '',
26318     html : '',
26319     preventDefault: true,
26320     disable : false,
26321     icon : false,
26322     pos : 'right',
26323     
26324     getAutoCreate : function()
26325     {
26326         var text = [
26327             {
26328                 tag : 'span',
26329                 cls : 'roo-menu-item-text',
26330                 html : this.html
26331             }
26332         ];
26333         
26334         if(this.icon){
26335             text.unshift({
26336                 tag : 'i',
26337                 cls : 'fa ' + this.icon
26338             })
26339         }
26340         
26341         var cfg = {
26342             tag : 'li',
26343             cn : [
26344                 {
26345                     tag : 'a',
26346                     href : this.href || '#',
26347                     cn : text
26348                 }
26349             ]
26350         };
26351         
26352         if(this.disable){
26353             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26354         }
26355         
26356         if(this.submenu){
26357             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26358             
26359             if(this.pos == 'left'){
26360                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26361             }
26362         }
26363         
26364         return cfg;
26365     },
26366     
26367     initEvents : function() 
26368     {
26369         this.el.on('mouseover', this.onMouseOver, this);
26370         this.el.on('mouseout', this.onMouseOut, this);
26371         
26372         this.el.select('a', true).first().on('click', this.onClick, this);
26373         
26374     },
26375     
26376     onClick : function(e)
26377     {
26378         if(this.preventDefault){
26379             e.preventDefault();
26380         }
26381         
26382         this.fireEvent("click", this, e);
26383     },
26384     
26385     onMouseOver : function(e)
26386     {
26387         if(this.submenu && this.pos == 'left'){
26388             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26389         }
26390         
26391         this.fireEvent("mouseover", this, e);
26392     },
26393     
26394     onMouseOut : function(e)
26395     {
26396         this.fireEvent("mouseout", this, e);
26397     }
26398 });
26399
26400  
26401
26402  /*
26403  * - LGPL
26404  *
26405  * menu separator
26406  * 
26407  */
26408 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26409
26410 /**
26411  * @class Roo.bootstrap.menu.Separator
26412  * @extends Roo.bootstrap.Component
26413  * Bootstrap Separator class
26414  * 
26415  * @constructor
26416  * Create a new Separator
26417  * @param {Object} config The config object
26418  */
26419
26420
26421 Roo.bootstrap.menu.Separator = function(config){
26422     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26423 };
26424
26425 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26426     
26427     getAutoCreate : function(){
26428         var cfg = {
26429             tag : 'li',
26430             cls: 'divider'
26431         };
26432         
26433         return cfg;
26434     }
26435    
26436 });
26437
26438  
26439
26440  /*
26441  * - LGPL
26442  *
26443  * Tooltip
26444  * 
26445  */
26446
26447 /**
26448  * @class Roo.bootstrap.Tooltip
26449  * Bootstrap Tooltip class
26450  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26451  * to determine which dom element triggers the tooltip.
26452  * 
26453  * It needs to add support for additional attributes like tooltip-position
26454  * 
26455  * @constructor
26456  * Create a new Toolti
26457  * @param {Object} config The config object
26458  */
26459
26460 Roo.bootstrap.Tooltip = function(config){
26461     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26462     
26463     this.alignment = Roo.bootstrap.Tooltip.alignment;
26464     
26465     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26466         this.alignment = config.alignment;
26467     }
26468     
26469 };
26470
26471 Roo.apply(Roo.bootstrap.Tooltip, {
26472     /**
26473      * @function init initialize tooltip monitoring.
26474      * @static
26475      */
26476     currentEl : false,
26477     currentTip : false,
26478     currentRegion : false,
26479     
26480     //  init : delay?
26481     
26482     init : function()
26483     {
26484         Roo.get(document).on('mouseover', this.enter ,this);
26485         Roo.get(document).on('mouseout', this.leave, this);
26486          
26487         
26488         this.currentTip = new Roo.bootstrap.Tooltip();
26489     },
26490     
26491     enter : function(ev)
26492     {
26493         var dom = ev.getTarget();
26494         
26495         //Roo.log(['enter',dom]);
26496         var el = Roo.fly(dom);
26497         if (this.currentEl) {
26498             //Roo.log(dom);
26499             //Roo.log(this.currentEl);
26500             //Roo.log(this.currentEl.contains(dom));
26501             if (this.currentEl == el) {
26502                 return;
26503             }
26504             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26505                 return;
26506             }
26507
26508         }
26509         
26510         if (this.currentTip.el) {
26511             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26512         }    
26513         //Roo.log(ev);
26514         
26515         if(!el || el.dom == document){
26516             return;
26517         }
26518         
26519         var bindEl = el;
26520         
26521         // you can not look for children, as if el is the body.. then everythign is the child..
26522         if (!el.attr('tooltip')) { //
26523             if (!el.select("[tooltip]").elements.length) {
26524                 return;
26525             }
26526             // is the mouse over this child...?
26527             bindEl = el.select("[tooltip]").first();
26528             var xy = ev.getXY();
26529             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26530                 //Roo.log("not in region.");
26531                 return;
26532             }
26533             //Roo.log("child element over..");
26534             
26535         }
26536         this.currentEl = bindEl;
26537         this.currentTip.bind(bindEl);
26538         this.currentRegion = Roo.lib.Region.getRegion(dom);
26539         this.currentTip.enter();
26540         
26541     },
26542     leave : function(ev)
26543     {
26544         var dom = ev.getTarget();
26545         //Roo.log(['leave',dom]);
26546         if (!this.currentEl) {
26547             return;
26548         }
26549         
26550         
26551         if (dom != this.currentEl.dom) {
26552             return;
26553         }
26554         var xy = ev.getXY();
26555         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26556             return;
26557         }
26558         // only activate leave if mouse cursor is outside... bounding box..
26559         
26560         
26561         
26562         
26563         if (this.currentTip) {
26564             this.currentTip.leave();
26565         }
26566         //Roo.log('clear currentEl');
26567         this.currentEl = false;
26568         
26569         
26570     },
26571     alignment : {
26572         'left' : ['r-l', [-2,0], 'right'],
26573         'right' : ['l-r', [2,0], 'left'],
26574         'bottom' : ['t-b', [0,2], 'top'],
26575         'top' : [ 'b-t', [0,-2], 'bottom']
26576     }
26577     
26578 });
26579
26580
26581 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26582     
26583     
26584     bindEl : false,
26585     
26586     delay : null, // can be { show : 300 , hide: 500}
26587     
26588     timeout : null,
26589     
26590     hoverState : null, //???
26591     
26592     placement : 'bottom', 
26593     
26594     alignment : false,
26595     
26596     getAutoCreate : function(){
26597     
26598         var cfg = {
26599            cls : 'tooltip',
26600            role : 'tooltip',
26601            cn : [
26602                 {
26603                     cls : 'tooltip-arrow'
26604                 },
26605                 {
26606                     cls : 'tooltip-inner'
26607                 }
26608            ]
26609         };
26610         
26611         return cfg;
26612     },
26613     bind : function(el)
26614     {
26615         this.bindEl = el;
26616     },
26617       
26618     
26619     enter : function () {
26620        
26621         if (this.timeout != null) {
26622             clearTimeout(this.timeout);
26623         }
26624         
26625         this.hoverState = 'in';
26626          //Roo.log("enter - show");
26627         if (!this.delay || !this.delay.show) {
26628             this.show();
26629             return;
26630         }
26631         var _t = this;
26632         this.timeout = setTimeout(function () {
26633             if (_t.hoverState == 'in') {
26634                 _t.show();
26635             }
26636         }, this.delay.show);
26637     },
26638     leave : function()
26639     {
26640         clearTimeout(this.timeout);
26641     
26642         this.hoverState = 'out';
26643          if (!this.delay || !this.delay.hide) {
26644             this.hide();
26645             return;
26646         }
26647        
26648         var _t = this;
26649         this.timeout = setTimeout(function () {
26650             //Roo.log("leave - timeout");
26651             
26652             if (_t.hoverState == 'out') {
26653                 _t.hide();
26654                 Roo.bootstrap.Tooltip.currentEl = false;
26655             }
26656         }, delay);
26657     },
26658     
26659     show : function (msg)
26660     {
26661         if (!this.el) {
26662             this.render(document.body);
26663         }
26664         // set content.
26665         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26666         
26667         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26668         
26669         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26670         
26671         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26672         
26673         var placement = typeof this.placement == 'function' ?
26674             this.placement.call(this, this.el, on_el) :
26675             this.placement;
26676             
26677         var autoToken = /\s?auto?\s?/i;
26678         var autoPlace = autoToken.test(placement);
26679         if (autoPlace) {
26680             placement = placement.replace(autoToken, '') || 'top';
26681         }
26682         
26683         //this.el.detach()
26684         //this.el.setXY([0,0]);
26685         this.el.show();
26686         //this.el.dom.style.display='block';
26687         
26688         //this.el.appendTo(on_el);
26689         
26690         var p = this.getPosition();
26691         var box = this.el.getBox();
26692         
26693         if (autoPlace) {
26694             // fixme..
26695         }
26696         
26697         var align = this.alignment[placement];
26698         
26699         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26700         
26701         if(placement == 'top' || placement == 'bottom'){
26702             if(xy[0] < 0){
26703                 placement = 'right';
26704             }
26705             
26706             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26707                 placement = 'left';
26708             }
26709             
26710             var scroll = Roo.select('body', true).first().getScroll();
26711             
26712             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26713                 placement = 'top';
26714             }
26715             
26716             align = this.alignment[placement];
26717         }
26718         
26719         this.el.alignTo(this.bindEl, align[0],align[1]);
26720         //var arrow = this.el.select('.arrow',true).first();
26721         //arrow.set(align[2], 
26722         
26723         this.el.addClass(placement);
26724         
26725         this.el.addClass('in fade');
26726         
26727         this.hoverState = null;
26728         
26729         if (this.el.hasClass('fade')) {
26730             // fade it?
26731         }
26732         
26733     },
26734     hide : function()
26735     {
26736          
26737         if (!this.el) {
26738             return;
26739         }
26740         //this.el.setXY([0,0]);
26741         this.el.removeClass('in');
26742         //this.el.hide();
26743         
26744     }
26745     
26746 });
26747  
26748
26749  /*
26750  * - LGPL
26751  *
26752  * Location Picker
26753  * 
26754  */
26755
26756 /**
26757  * @class Roo.bootstrap.LocationPicker
26758  * @extends Roo.bootstrap.Component
26759  * Bootstrap LocationPicker class
26760  * @cfg {Number} latitude Position when init default 0
26761  * @cfg {Number} longitude Position when init default 0
26762  * @cfg {Number} zoom default 15
26763  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26764  * @cfg {Boolean} mapTypeControl default false
26765  * @cfg {Boolean} disableDoubleClickZoom default false
26766  * @cfg {Boolean} scrollwheel default true
26767  * @cfg {Boolean} streetViewControl default false
26768  * @cfg {Number} radius default 0
26769  * @cfg {String} locationName
26770  * @cfg {Boolean} draggable default true
26771  * @cfg {Boolean} enableAutocomplete default false
26772  * @cfg {Boolean} enableReverseGeocode default true
26773  * @cfg {String} markerTitle
26774  * 
26775  * @constructor
26776  * Create a new LocationPicker
26777  * @param {Object} config The config object
26778  */
26779
26780
26781 Roo.bootstrap.LocationPicker = function(config){
26782     
26783     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26784     
26785     this.addEvents({
26786         /**
26787          * @event initial
26788          * Fires when the picker initialized.
26789          * @param {Roo.bootstrap.LocationPicker} this
26790          * @param {Google Location} location
26791          */
26792         initial : true,
26793         /**
26794          * @event positionchanged
26795          * Fires when the picker position changed.
26796          * @param {Roo.bootstrap.LocationPicker} this
26797          * @param {Google Location} location
26798          */
26799         positionchanged : true,
26800         /**
26801          * @event resize
26802          * Fires when the map resize.
26803          * @param {Roo.bootstrap.LocationPicker} this
26804          */
26805         resize : true,
26806         /**
26807          * @event show
26808          * Fires when the map show.
26809          * @param {Roo.bootstrap.LocationPicker} this
26810          */
26811         show : true,
26812         /**
26813          * @event hide
26814          * Fires when the map hide.
26815          * @param {Roo.bootstrap.LocationPicker} this
26816          */
26817         hide : true,
26818         /**
26819          * @event mapClick
26820          * Fires when click the map.
26821          * @param {Roo.bootstrap.LocationPicker} this
26822          * @param {Map event} e
26823          */
26824         mapClick : true,
26825         /**
26826          * @event mapRightClick
26827          * Fires when right click the map.
26828          * @param {Roo.bootstrap.LocationPicker} this
26829          * @param {Map event} e
26830          */
26831         mapRightClick : true,
26832         /**
26833          * @event markerClick
26834          * Fires when click the marker.
26835          * @param {Roo.bootstrap.LocationPicker} this
26836          * @param {Map event} e
26837          */
26838         markerClick : true,
26839         /**
26840          * @event markerRightClick
26841          * Fires when right click the marker.
26842          * @param {Roo.bootstrap.LocationPicker} this
26843          * @param {Map event} e
26844          */
26845         markerRightClick : true,
26846         /**
26847          * @event OverlayViewDraw
26848          * Fires when OverlayView Draw
26849          * @param {Roo.bootstrap.LocationPicker} this
26850          */
26851         OverlayViewDraw : true,
26852         /**
26853          * @event OverlayViewOnAdd
26854          * Fires when OverlayView Draw
26855          * @param {Roo.bootstrap.LocationPicker} this
26856          */
26857         OverlayViewOnAdd : true,
26858         /**
26859          * @event OverlayViewOnRemove
26860          * Fires when OverlayView Draw
26861          * @param {Roo.bootstrap.LocationPicker} this
26862          */
26863         OverlayViewOnRemove : true,
26864         /**
26865          * @event OverlayViewShow
26866          * Fires when OverlayView Draw
26867          * @param {Roo.bootstrap.LocationPicker} this
26868          * @param {Pixel} cpx
26869          */
26870         OverlayViewShow : true,
26871         /**
26872          * @event OverlayViewHide
26873          * Fires when OverlayView Draw
26874          * @param {Roo.bootstrap.LocationPicker} this
26875          */
26876         OverlayViewHide : true,
26877         /**
26878          * @event loadexception
26879          * Fires when load google lib failed.
26880          * @param {Roo.bootstrap.LocationPicker} this
26881          */
26882         loadexception : true
26883     });
26884         
26885 };
26886
26887 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26888     
26889     gMapContext: false,
26890     
26891     latitude: 0,
26892     longitude: 0,
26893     zoom: 15,
26894     mapTypeId: false,
26895     mapTypeControl: false,
26896     disableDoubleClickZoom: false,
26897     scrollwheel: true,
26898     streetViewControl: false,
26899     radius: 0,
26900     locationName: '',
26901     draggable: true,
26902     enableAutocomplete: false,
26903     enableReverseGeocode: true,
26904     markerTitle: '',
26905     
26906     getAutoCreate: function()
26907     {
26908
26909         var cfg = {
26910             tag: 'div',
26911             cls: 'roo-location-picker'
26912         };
26913         
26914         return cfg
26915     },
26916     
26917     initEvents: function(ct, position)
26918     {       
26919         if(!this.el.getWidth() || this.isApplied()){
26920             return;
26921         }
26922         
26923         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26924         
26925         this.initial();
26926     },
26927     
26928     initial: function()
26929     {
26930         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26931             this.fireEvent('loadexception', this);
26932             return;
26933         }
26934         
26935         if(!this.mapTypeId){
26936             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26937         }
26938         
26939         this.gMapContext = this.GMapContext();
26940         
26941         this.initOverlayView();
26942         
26943         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26944         
26945         var _this = this;
26946                 
26947         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26948             _this.setPosition(_this.gMapContext.marker.position);
26949         });
26950         
26951         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26952             _this.fireEvent('mapClick', this, event);
26953             
26954         });
26955
26956         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26957             _this.fireEvent('mapRightClick', this, event);
26958             
26959         });
26960         
26961         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26962             _this.fireEvent('markerClick', this, event);
26963             
26964         });
26965
26966         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26967             _this.fireEvent('markerRightClick', this, event);
26968             
26969         });
26970         
26971         this.setPosition(this.gMapContext.location);
26972         
26973         this.fireEvent('initial', this, this.gMapContext.location);
26974     },
26975     
26976     initOverlayView: function()
26977     {
26978         var _this = this;
26979         
26980         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26981             
26982             draw: function()
26983             {
26984                 _this.fireEvent('OverlayViewDraw', _this);
26985             },
26986             
26987             onAdd: function()
26988             {
26989                 _this.fireEvent('OverlayViewOnAdd', _this);
26990             },
26991             
26992             onRemove: function()
26993             {
26994                 _this.fireEvent('OverlayViewOnRemove', _this);
26995             },
26996             
26997             show: function(cpx)
26998             {
26999                 _this.fireEvent('OverlayViewShow', _this, cpx);
27000             },
27001             
27002             hide: function()
27003             {
27004                 _this.fireEvent('OverlayViewHide', _this);
27005             }
27006             
27007         });
27008     },
27009     
27010     fromLatLngToContainerPixel: function(event)
27011     {
27012         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
27013     },
27014     
27015     isApplied: function() 
27016     {
27017         return this.getGmapContext() == false ? false : true;
27018     },
27019     
27020     getGmapContext: function() 
27021     {
27022         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
27023     },
27024     
27025     GMapContext: function() 
27026     {
27027         var position = new google.maps.LatLng(this.latitude, this.longitude);
27028         
27029         var _map = new google.maps.Map(this.el.dom, {
27030             center: position,
27031             zoom: this.zoom,
27032             mapTypeId: this.mapTypeId,
27033             mapTypeControl: this.mapTypeControl,
27034             disableDoubleClickZoom: this.disableDoubleClickZoom,
27035             scrollwheel: this.scrollwheel,
27036             streetViewControl: this.streetViewControl,
27037             locationName: this.locationName,
27038             draggable: this.draggable,
27039             enableAutocomplete: this.enableAutocomplete,
27040             enableReverseGeocode: this.enableReverseGeocode
27041         });
27042         
27043         var _marker = new google.maps.Marker({
27044             position: position,
27045             map: _map,
27046             title: this.markerTitle,
27047             draggable: this.draggable
27048         });
27049         
27050         return {
27051             map: _map,
27052             marker: _marker,
27053             circle: null,
27054             location: position,
27055             radius: this.radius,
27056             locationName: this.locationName,
27057             addressComponents: {
27058                 formatted_address: null,
27059                 addressLine1: null,
27060                 addressLine2: null,
27061                 streetName: null,
27062                 streetNumber: null,
27063                 city: null,
27064                 district: null,
27065                 state: null,
27066                 stateOrProvince: null
27067             },
27068             settings: this,
27069             domContainer: this.el.dom,
27070             geodecoder: new google.maps.Geocoder()
27071         };
27072     },
27073     
27074     drawCircle: function(center, radius, options) 
27075     {
27076         if (this.gMapContext.circle != null) {
27077             this.gMapContext.circle.setMap(null);
27078         }
27079         if (radius > 0) {
27080             radius *= 1;
27081             options = Roo.apply({}, options, {
27082                 strokeColor: "#0000FF",
27083                 strokeOpacity: .35,
27084                 strokeWeight: 2,
27085                 fillColor: "#0000FF",
27086                 fillOpacity: .2
27087             });
27088             
27089             options.map = this.gMapContext.map;
27090             options.radius = radius;
27091             options.center = center;
27092             this.gMapContext.circle = new google.maps.Circle(options);
27093             return this.gMapContext.circle;
27094         }
27095         
27096         return null;
27097     },
27098     
27099     setPosition: function(location) 
27100     {
27101         this.gMapContext.location = location;
27102         this.gMapContext.marker.setPosition(location);
27103         this.gMapContext.map.panTo(location);
27104         this.drawCircle(location, this.gMapContext.radius, {});
27105         
27106         var _this = this;
27107         
27108         if (this.gMapContext.settings.enableReverseGeocode) {
27109             this.gMapContext.geodecoder.geocode({
27110                 latLng: this.gMapContext.location
27111             }, function(results, status) {
27112                 
27113                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
27114                     _this.gMapContext.locationName = results[0].formatted_address;
27115                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
27116                     
27117                     _this.fireEvent('positionchanged', this, location);
27118                 }
27119             });
27120             
27121             return;
27122         }
27123         
27124         this.fireEvent('positionchanged', this, location);
27125     },
27126     
27127     resize: function()
27128     {
27129         google.maps.event.trigger(this.gMapContext.map, "resize");
27130         
27131         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
27132         
27133         this.fireEvent('resize', this);
27134     },
27135     
27136     setPositionByLatLng: function(latitude, longitude)
27137     {
27138         this.setPosition(new google.maps.LatLng(latitude, longitude));
27139     },
27140     
27141     getCurrentPosition: function() 
27142     {
27143         return {
27144             latitude: this.gMapContext.location.lat(),
27145             longitude: this.gMapContext.location.lng()
27146         };
27147     },
27148     
27149     getAddressName: function() 
27150     {
27151         return this.gMapContext.locationName;
27152     },
27153     
27154     getAddressComponents: function() 
27155     {
27156         return this.gMapContext.addressComponents;
27157     },
27158     
27159     address_component_from_google_geocode: function(address_components) 
27160     {
27161         var result = {};
27162         
27163         for (var i = 0; i < address_components.length; i++) {
27164             var component = address_components[i];
27165             if (component.types.indexOf("postal_code") >= 0) {
27166                 result.postalCode = component.short_name;
27167             } else if (component.types.indexOf("street_number") >= 0) {
27168                 result.streetNumber = component.short_name;
27169             } else if (component.types.indexOf("route") >= 0) {
27170                 result.streetName = component.short_name;
27171             } else if (component.types.indexOf("neighborhood") >= 0) {
27172                 result.city = component.short_name;
27173             } else if (component.types.indexOf("locality") >= 0) {
27174                 result.city = component.short_name;
27175             } else if (component.types.indexOf("sublocality") >= 0) {
27176                 result.district = component.short_name;
27177             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
27178                 result.stateOrProvince = component.short_name;
27179             } else if (component.types.indexOf("country") >= 0) {
27180                 result.country = component.short_name;
27181             }
27182         }
27183         
27184         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
27185         result.addressLine2 = "";
27186         return result;
27187     },
27188     
27189     setZoomLevel: function(zoom)
27190     {
27191         this.gMapContext.map.setZoom(zoom);
27192     },
27193     
27194     show: function()
27195     {
27196         if(!this.el){
27197             return;
27198         }
27199         
27200         this.el.show();
27201         
27202         this.resize();
27203         
27204         this.fireEvent('show', this);
27205     },
27206     
27207     hide: function()
27208     {
27209         if(!this.el){
27210             return;
27211         }
27212         
27213         this.el.hide();
27214         
27215         this.fireEvent('hide', this);
27216     }
27217     
27218 });
27219
27220 Roo.apply(Roo.bootstrap.LocationPicker, {
27221     
27222     OverlayView : function(map, options)
27223     {
27224         options = options || {};
27225         
27226         this.setMap(map);
27227     }
27228     
27229     
27230 });/**
27231  * @class Roo.bootstrap.Alert
27232  * @extends Roo.bootstrap.Component
27233  * Bootstrap Alert class - shows an alert area box
27234  * eg
27235  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
27236   Enter a valid email address
27237 </div>
27238  * @licence LGPL
27239  * @cfg {String} title The title of alert
27240  * @cfg {String} html The content of alert
27241  * @cfg {String} weight (  success | info | warning | danger )
27242  * @cfg {String} faicon font-awesomeicon
27243  * 
27244  * @constructor
27245  * Create a new alert
27246  * @param {Object} config The config object
27247  */
27248
27249
27250 Roo.bootstrap.Alert = function(config){
27251     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27252     
27253 };
27254
27255 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
27256     
27257     title: '',
27258     html: '',
27259     weight: false,
27260     faicon: false,
27261     
27262     getAutoCreate : function()
27263     {
27264         
27265         var cfg = {
27266             tag : 'div',
27267             cls : 'alert',
27268             cn : [
27269                 {
27270                     tag : 'i',
27271                     cls : 'roo-alert-icon'
27272                     
27273                 },
27274                 {
27275                     tag : 'b',
27276                     cls : 'roo-alert-title',
27277                     html : this.title
27278                 },
27279                 {
27280                     tag : 'span',
27281                     cls : 'roo-alert-text',
27282                     html : this.html
27283                 }
27284             ]
27285         };
27286         
27287         if(this.faicon){
27288             cfg.cn[0].cls += ' fa ' + this.faicon;
27289         }
27290         
27291         if(this.weight){
27292             cfg.cls += ' alert-' + this.weight;
27293         }
27294         
27295         return cfg;
27296     },
27297     
27298     initEvents: function() 
27299     {
27300         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27301     },
27302     
27303     setTitle : function(str)
27304     {
27305         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27306     },
27307     
27308     setText : function(str)
27309     {
27310         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27311     },
27312     
27313     setWeight : function(weight)
27314     {
27315         if(this.weight){
27316             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27317         }
27318         
27319         this.weight = weight;
27320         
27321         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27322     },
27323     
27324     setIcon : function(icon)
27325     {
27326         if(this.faicon){
27327             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27328         }
27329         
27330         this.faicon = icon;
27331         
27332         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27333     },
27334     
27335     hide: function() 
27336     {
27337         this.el.hide();   
27338     },
27339     
27340     show: function() 
27341     {  
27342         this.el.show();   
27343     }
27344     
27345 });
27346
27347  
27348 /*
27349 * Licence: LGPL
27350 */
27351
27352 /**
27353  * @class Roo.bootstrap.UploadCropbox
27354  * @extends Roo.bootstrap.Component
27355  * Bootstrap UploadCropbox class
27356  * @cfg {String} emptyText show when image has been loaded
27357  * @cfg {String} rotateNotify show when image too small to rotate
27358  * @cfg {Number} errorTimeout default 3000
27359  * @cfg {Number} minWidth default 300
27360  * @cfg {Number} minHeight default 300
27361  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27362  * @cfg {Boolean} isDocument (true|false) default false
27363  * @cfg {String} url action url
27364  * @cfg {String} paramName default 'imageUpload'
27365  * @cfg {String} method default POST
27366  * @cfg {Boolean} loadMask (true|false) default true
27367  * @cfg {Boolean} loadingText default 'Loading...'
27368  * 
27369  * @constructor
27370  * Create a new UploadCropbox
27371  * @param {Object} config The config object
27372  */
27373
27374 Roo.bootstrap.UploadCropbox = function(config){
27375     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27376     
27377     this.addEvents({
27378         /**
27379          * @event beforeselectfile
27380          * Fire before select file
27381          * @param {Roo.bootstrap.UploadCropbox} this
27382          */
27383         "beforeselectfile" : true,
27384         /**
27385          * @event initial
27386          * Fire after initEvent
27387          * @param {Roo.bootstrap.UploadCropbox} this
27388          */
27389         "initial" : true,
27390         /**
27391          * @event crop
27392          * Fire after initEvent
27393          * @param {Roo.bootstrap.UploadCropbox} this
27394          * @param {String} data
27395          */
27396         "crop" : true,
27397         /**
27398          * @event prepare
27399          * Fire when preparing the file data
27400          * @param {Roo.bootstrap.UploadCropbox} this
27401          * @param {Object} file
27402          */
27403         "prepare" : true,
27404         /**
27405          * @event exception
27406          * Fire when get exception
27407          * @param {Roo.bootstrap.UploadCropbox} this
27408          * @param {XMLHttpRequest} xhr
27409          */
27410         "exception" : true,
27411         /**
27412          * @event beforeloadcanvas
27413          * Fire before load the canvas
27414          * @param {Roo.bootstrap.UploadCropbox} this
27415          * @param {String} src
27416          */
27417         "beforeloadcanvas" : true,
27418         /**
27419          * @event trash
27420          * Fire when trash image
27421          * @param {Roo.bootstrap.UploadCropbox} this
27422          */
27423         "trash" : true,
27424         /**
27425          * @event download
27426          * Fire when download the image
27427          * @param {Roo.bootstrap.UploadCropbox} this
27428          */
27429         "download" : true,
27430         /**
27431          * @event footerbuttonclick
27432          * Fire when footerbuttonclick
27433          * @param {Roo.bootstrap.UploadCropbox} this
27434          * @param {String} type
27435          */
27436         "footerbuttonclick" : true,
27437         /**
27438          * @event resize
27439          * Fire when resize
27440          * @param {Roo.bootstrap.UploadCropbox} this
27441          */
27442         "resize" : true,
27443         /**
27444          * @event rotate
27445          * Fire when rotate the image
27446          * @param {Roo.bootstrap.UploadCropbox} this
27447          * @param {String} pos
27448          */
27449         "rotate" : true,
27450         /**
27451          * @event inspect
27452          * Fire when inspect the file
27453          * @param {Roo.bootstrap.UploadCropbox} this
27454          * @param {Object} file
27455          */
27456         "inspect" : true,
27457         /**
27458          * @event upload
27459          * Fire when xhr upload the file
27460          * @param {Roo.bootstrap.UploadCropbox} this
27461          * @param {Object} data
27462          */
27463         "upload" : true,
27464         /**
27465          * @event arrange
27466          * Fire when arrange the file data
27467          * @param {Roo.bootstrap.UploadCropbox} this
27468          * @param {Object} formData
27469          */
27470         "arrange" : true
27471     });
27472     
27473     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27474 };
27475
27476 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27477     
27478     emptyText : 'Click to upload image',
27479     rotateNotify : 'Image is too small to rotate',
27480     errorTimeout : 3000,
27481     scale : 0,
27482     baseScale : 1,
27483     rotate : 0,
27484     dragable : false,
27485     pinching : false,
27486     mouseX : 0,
27487     mouseY : 0,
27488     cropData : false,
27489     minWidth : 300,
27490     minHeight : 300,
27491     file : false,
27492     exif : {},
27493     baseRotate : 1,
27494     cropType : 'image/jpeg',
27495     buttons : false,
27496     canvasLoaded : false,
27497     isDocument : false,
27498     method : 'POST',
27499     paramName : 'imageUpload',
27500     loadMask : true,
27501     loadingText : 'Loading...',
27502     maskEl : false,
27503     
27504     getAutoCreate : function()
27505     {
27506         var cfg = {
27507             tag : 'div',
27508             cls : 'roo-upload-cropbox',
27509             cn : [
27510                 {
27511                     tag : 'input',
27512                     cls : 'roo-upload-cropbox-selector',
27513                     type : 'file'
27514                 },
27515                 {
27516                     tag : 'div',
27517                     cls : 'roo-upload-cropbox-body',
27518                     style : 'cursor:pointer',
27519                     cn : [
27520                         {
27521                             tag : 'div',
27522                             cls : 'roo-upload-cropbox-preview'
27523                         },
27524                         {
27525                             tag : 'div',
27526                             cls : 'roo-upload-cropbox-thumb'
27527                         },
27528                         {
27529                             tag : 'div',
27530                             cls : 'roo-upload-cropbox-empty-notify',
27531                             html : this.emptyText
27532                         },
27533                         {
27534                             tag : 'div',
27535                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27536                             html : this.rotateNotify
27537                         }
27538                     ]
27539                 },
27540                 {
27541                     tag : 'div',
27542                     cls : 'roo-upload-cropbox-footer',
27543                     cn : {
27544                         tag : 'div',
27545                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27546                         cn : []
27547                     }
27548                 }
27549             ]
27550         };
27551         
27552         return cfg;
27553     },
27554     
27555     onRender : function(ct, position)
27556     {
27557         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27558         
27559         if (this.buttons.length) {
27560             
27561             Roo.each(this.buttons, function(bb) {
27562                 
27563                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27564                 
27565                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27566                 
27567             }, this);
27568         }
27569         
27570         if(this.loadMask){
27571             this.maskEl = this.el;
27572         }
27573     },
27574     
27575     initEvents : function()
27576     {
27577         this.urlAPI = (window.createObjectURL && window) || 
27578                                 (window.URL && URL.revokeObjectURL && URL) || 
27579                                 (window.webkitURL && webkitURL);
27580                         
27581         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27582         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27583         
27584         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27585         this.selectorEl.hide();
27586         
27587         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27588         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27589         
27590         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27591         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27592         this.thumbEl.hide();
27593         
27594         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27595         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27596         
27597         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27598         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27599         this.errorEl.hide();
27600         
27601         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27602         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27603         this.footerEl.hide();
27604         
27605         this.setThumbBoxSize();
27606         
27607         this.bind();
27608         
27609         this.resize();
27610         
27611         this.fireEvent('initial', this);
27612     },
27613
27614     bind : function()
27615     {
27616         var _this = this;
27617         
27618         window.addEventListener("resize", function() { _this.resize(); } );
27619         
27620         this.bodyEl.on('click', this.beforeSelectFile, this);
27621         
27622         if(Roo.isTouch){
27623             this.bodyEl.on('touchstart', this.onTouchStart, this);
27624             this.bodyEl.on('touchmove', this.onTouchMove, this);
27625             this.bodyEl.on('touchend', this.onTouchEnd, this);
27626         }
27627         
27628         if(!Roo.isTouch){
27629             this.bodyEl.on('mousedown', this.onMouseDown, this);
27630             this.bodyEl.on('mousemove', this.onMouseMove, this);
27631             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27632             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27633             Roo.get(document).on('mouseup', this.onMouseUp, this);
27634         }
27635         
27636         this.selectorEl.on('change', this.onFileSelected, this);
27637     },
27638     
27639     reset : function()
27640     {    
27641         this.scale = 0;
27642         this.baseScale = 1;
27643         this.rotate = 0;
27644         this.baseRotate = 1;
27645         this.dragable = false;
27646         this.pinching = false;
27647         this.mouseX = 0;
27648         this.mouseY = 0;
27649         this.cropData = false;
27650         this.notifyEl.dom.innerHTML = this.emptyText;
27651         
27652         this.selectorEl.dom.value = '';
27653         
27654     },
27655     
27656     resize : function()
27657     {
27658         if(this.fireEvent('resize', this) != false){
27659             this.setThumbBoxPosition();
27660             this.setCanvasPosition();
27661         }
27662     },
27663     
27664     onFooterButtonClick : function(e, el, o, type)
27665     {
27666         switch (type) {
27667             case 'rotate-left' :
27668                 this.onRotateLeft(e);
27669                 break;
27670             case 'rotate-right' :
27671                 this.onRotateRight(e);
27672                 break;
27673             case 'picture' :
27674                 this.beforeSelectFile(e);
27675                 break;
27676             case 'trash' :
27677                 this.trash(e);
27678                 break;
27679             case 'crop' :
27680                 this.crop(e);
27681                 break;
27682             case 'download' :
27683                 this.download(e);
27684                 break;
27685             default :
27686                 break;
27687         }
27688         
27689         this.fireEvent('footerbuttonclick', this, type);
27690     },
27691     
27692     beforeSelectFile : function(e)
27693     {
27694         e.preventDefault();
27695         
27696         if(this.fireEvent('beforeselectfile', this) != false){
27697             this.selectorEl.dom.click();
27698         }
27699     },
27700     
27701     onFileSelected : function(e)
27702     {
27703         e.preventDefault();
27704         
27705         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27706             return;
27707         }
27708         
27709         var file = this.selectorEl.dom.files[0];
27710         
27711         if(this.fireEvent('inspect', this, file) != false){
27712             this.prepare(file);
27713         }
27714         
27715     },
27716     
27717     trash : function(e)
27718     {
27719         this.fireEvent('trash', this);
27720     },
27721     
27722     download : function(e)
27723     {
27724         this.fireEvent('download', this);
27725     },
27726     
27727     loadCanvas : function(src)
27728     {   
27729         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27730             
27731             this.reset();
27732             
27733             this.imageEl = document.createElement('img');
27734             
27735             var _this = this;
27736             
27737             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27738             
27739             this.imageEl.src = src;
27740         }
27741     },
27742     
27743     onLoadCanvas : function()
27744     {   
27745         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27746         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27747         
27748         this.bodyEl.un('click', this.beforeSelectFile, this);
27749         
27750         this.notifyEl.hide();
27751         this.thumbEl.show();
27752         this.footerEl.show();
27753         
27754         this.baseRotateLevel();
27755         
27756         if(this.isDocument){
27757             this.setThumbBoxSize();
27758         }
27759         
27760         this.setThumbBoxPosition();
27761         
27762         this.baseScaleLevel();
27763         
27764         this.draw();
27765         
27766         this.resize();
27767         
27768         this.canvasLoaded = true;
27769         
27770         if(this.loadMask){
27771             this.maskEl.unmask();
27772         }
27773         
27774     },
27775     
27776     setCanvasPosition : function()
27777     {   
27778         if(!this.canvasEl){
27779             return;
27780         }
27781         
27782         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27783         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27784         
27785         this.previewEl.setLeft(pw);
27786         this.previewEl.setTop(ph);
27787         
27788     },
27789     
27790     onMouseDown : function(e)
27791     {   
27792         e.stopEvent();
27793         
27794         this.dragable = true;
27795         this.pinching = false;
27796         
27797         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27798             this.dragable = false;
27799             return;
27800         }
27801         
27802         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27803         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27804         
27805     },
27806     
27807     onMouseMove : function(e)
27808     {   
27809         e.stopEvent();
27810         
27811         if(!this.canvasLoaded){
27812             return;
27813         }
27814         
27815         if (!this.dragable){
27816             return;
27817         }
27818         
27819         var minX = Math.ceil(this.thumbEl.getLeft(true));
27820         var minY = Math.ceil(this.thumbEl.getTop(true));
27821         
27822         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27823         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27824         
27825         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27826         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27827         
27828         x = x - this.mouseX;
27829         y = y - this.mouseY;
27830         
27831         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27832         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27833         
27834         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27835         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27836         
27837         this.previewEl.setLeft(bgX);
27838         this.previewEl.setTop(bgY);
27839         
27840         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27841         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27842     },
27843     
27844     onMouseUp : function(e)
27845     {   
27846         e.stopEvent();
27847         
27848         this.dragable = false;
27849     },
27850     
27851     onMouseWheel : function(e)
27852     {   
27853         e.stopEvent();
27854         
27855         this.startScale = this.scale;
27856         
27857         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27858         
27859         if(!this.zoomable()){
27860             this.scale = this.startScale;
27861             return;
27862         }
27863         
27864         this.draw();
27865         
27866         return;
27867     },
27868     
27869     zoomable : function()
27870     {
27871         var minScale = this.thumbEl.getWidth() / this.minWidth;
27872         
27873         if(this.minWidth < this.minHeight){
27874             minScale = this.thumbEl.getHeight() / this.minHeight;
27875         }
27876         
27877         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27878         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27879         
27880         if(
27881                 this.isDocument &&
27882                 (this.rotate == 0 || this.rotate == 180) && 
27883                 (
27884                     width > this.imageEl.OriginWidth || 
27885                     height > this.imageEl.OriginHeight ||
27886                     (width < this.minWidth && height < this.minHeight)
27887                 )
27888         ){
27889             return false;
27890         }
27891         
27892         if(
27893                 this.isDocument &&
27894                 (this.rotate == 90 || this.rotate == 270) && 
27895                 (
27896                     width > this.imageEl.OriginWidth || 
27897                     height > this.imageEl.OriginHeight ||
27898                     (width < this.minHeight && height < this.minWidth)
27899                 )
27900         ){
27901             return false;
27902         }
27903         
27904         if(
27905                 !this.isDocument &&
27906                 (this.rotate == 0 || this.rotate == 180) && 
27907                 (
27908                     width < this.minWidth || 
27909                     width > this.imageEl.OriginWidth || 
27910                     height < this.minHeight || 
27911                     height > this.imageEl.OriginHeight
27912                 )
27913         ){
27914             return false;
27915         }
27916         
27917         if(
27918                 !this.isDocument &&
27919                 (this.rotate == 90 || this.rotate == 270) && 
27920                 (
27921                     width < this.minHeight || 
27922                     width > this.imageEl.OriginWidth || 
27923                     height < this.minWidth || 
27924                     height > this.imageEl.OriginHeight
27925                 )
27926         ){
27927             return false;
27928         }
27929         
27930         return true;
27931         
27932     },
27933     
27934     onRotateLeft : function(e)
27935     {   
27936         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27937             
27938             var minScale = this.thumbEl.getWidth() / this.minWidth;
27939             
27940             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27941             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27942             
27943             this.startScale = this.scale;
27944             
27945             while (this.getScaleLevel() < minScale){
27946             
27947                 this.scale = this.scale + 1;
27948                 
27949                 if(!this.zoomable()){
27950                     break;
27951                 }
27952                 
27953                 if(
27954                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27955                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27956                 ){
27957                     continue;
27958                 }
27959                 
27960                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27961
27962                 this.draw();
27963                 
27964                 return;
27965             }
27966             
27967             this.scale = this.startScale;
27968             
27969             this.onRotateFail();
27970             
27971             return false;
27972         }
27973         
27974         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27975
27976         if(this.isDocument){
27977             this.setThumbBoxSize();
27978             this.setThumbBoxPosition();
27979             this.setCanvasPosition();
27980         }
27981         
27982         this.draw();
27983         
27984         this.fireEvent('rotate', this, 'left');
27985         
27986     },
27987     
27988     onRotateRight : function(e)
27989     {
27990         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27991             
27992             var minScale = this.thumbEl.getWidth() / this.minWidth;
27993         
27994             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27995             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27996             
27997             this.startScale = this.scale;
27998             
27999             while (this.getScaleLevel() < minScale){
28000             
28001                 this.scale = this.scale + 1;
28002                 
28003                 if(!this.zoomable()){
28004                     break;
28005                 }
28006                 
28007                 if(
28008                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28009                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28010                 ){
28011                     continue;
28012                 }
28013                 
28014                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28015
28016                 this.draw();
28017                 
28018                 return;
28019             }
28020             
28021             this.scale = this.startScale;
28022             
28023             this.onRotateFail();
28024             
28025             return false;
28026         }
28027         
28028         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28029
28030         if(this.isDocument){
28031             this.setThumbBoxSize();
28032             this.setThumbBoxPosition();
28033             this.setCanvasPosition();
28034         }
28035         
28036         this.draw();
28037         
28038         this.fireEvent('rotate', this, 'right');
28039     },
28040     
28041     onRotateFail : function()
28042     {
28043         this.errorEl.show(true);
28044         
28045         var _this = this;
28046         
28047         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
28048     },
28049     
28050     draw : function()
28051     {
28052         this.previewEl.dom.innerHTML = '';
28053         
28054         var canvasEl = document.createElement("canvas");
28055         
28056         var contextEl = canvasEl.getContext("2d");
28057         
28058         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28059         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28060         var center = this.imageEl.OriginWidth / 2;
28061         
28062         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
28063             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28064             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28065             center = this.imageEl.OriginHeight / 2;
28066         }
28067         
28068         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
28069         
28070         contextEl.translate(center, center);
28071         contextEl.rotate(this.rotate * Math.PI / 180);
28072
28073         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28074         
28075         this.canvasEl = document.createElement("canvas");
28076         
28077         this.contextEl = this.canvasEl.getContext("2d");
28078         
28079         switch (this.rotate) {
28080             case 0 :
28081                 
28082                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28083                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28084                 
28085                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28086                 
28087                 break;
28088             case 90 : 
28089                 
28090                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28091                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28092                 
28093                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28094                     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);
28095                     break;
28096                 }
28097                 
28098                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28099                 
28100                 break;
28101             case 180 :
28102                 
28103                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28104                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28105                 
28106                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28107                     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);
28108                     break;
28109                 }
28110                 
28111                 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);
28112                 
28113                 break;
28114             case 270 :
28115                 
28116                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28117                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28118         
28119                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28120                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28121                     break;
28122                 }
28123                 
28124                 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);
28125                 
28126                 break;
28127             default : 
28128                 break;
28129         }
28130         
28131         this.previewEl.appendChild(this.canvasEl);
28132         
28133         this.setCanvasPosition();
28134     },
28135     
28136     crop : function()
28137     {
28138         if(!this.canvasLoaded){
28139             return;
28140         }
28141         
28142         var imageCanvas = document.createElement("canvas");
28143         
28144         var imageContext = imageCanvas.getContext("2d");
28145         
28146         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28147         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28148         
28149         var center = imageCanvas.width / 2;
28150         
28151         imageContext.translate(center, center);
28152         
28153         imageContext.rotate(this.rotate * Math.PI / 180);
28154         
28155         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28156         
28157         var canvas = document.createElement("canvas");
28158         
28159         var context = canvas.getContext("2d");
28160                 
28161         canvas.width = this.minWidth;
28162         canvas.height = this.minHeight;
28163
28164         switch (this.rotate) {
28165             case 0 :
28166                 
28167                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28168                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28169                 
28170                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28171                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28172                 
28173                 var targetWidth = this.minWidth - 2 * x;
28174                 var targetHeight = this.minHeight - 2 * y;
28175                 
28176                 var scale = 1;
28177                 
28178                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28179                     scale = targetWidth / width;
28180                 }
28181                 
28182                 if(x > 0 && y == 0){
28183                     scale = targetHeight / height;
28184                 }
28185                 
28186                 if(x > 0 && y > 0){
28187                     scale = targetWidth / width;
28188                     
28189                     if(width < height){
28190                         scale = targetHeight / height;
28191                     }
28192                 }
28193                 
28194                 context.scale(scale, scale);
28195                 
28196                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28197                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28198
28199                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28200                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28201
28202                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28203                 
28204                 break;
28205             case 90 : 
28206                 
28207                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28208                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28209                 
28210                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28211                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28212                 
28213                 var targetWidth = this.minWidth - 2 * x;
28214                 var targetHeight = this.minHeight - 2 * y;
28215                 
28216                 var scale = 1;
28217                 
28218                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28219                     scale = targetWidth / width;
28220                 }
28221                 
28222                 if(x > 0 && y == 0){
28223                     scale = targetHeight / height;
28224                 }
28225                 
28226                 if(x > 0 && y > 0){
28227                     scale = targetWidth / width;
28228                     
28229                     if(width < height){
28230                         scale = targetHeight / height;
28231                     }
28232                 }
28233                 
28234                 context.scale(scale, scale);
28235                 
28236                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28237                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28238
28239                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28240                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28241                 
28242                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28243                 
28244                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28245                 
28246                 break;
28247             case 180 :
28248                 
28249                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28250                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28251                 
28252                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28253                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28254                 
28255                 var targetWidth = this.minWidth - 2 * x;
28256                 var targetHeight = this.minHeight - 2 * y;
28257                 
28258                 var scale = 1;
28259                 
28260                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28261                     scale = targetWidth / width;
28262                 }
28263                 
28264                 if(x > 0 && y == 0){
28265                     scale = targetHeight / height;
28266                 }
28267                 
28268                 if(x > 0 && y > 0){
28269                     scale = targetWidth / width;
28270                     
28271                     if(width < height){
28272                         scale = targetHeight / height;
28273                     }
28274                 }
28275                 
28276                 context.scale(scale, scale);
28277                 
28278                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28279                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28280
28281                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28282                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28283
28284                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28285                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28286                 
28287                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28288                 
28289                 break;
28290             case 270 :
28291                 
28292                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28293                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28294                 
28295                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28296                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28297                 
28298                 var targetWidth = this.minWidth - 2 * x;
28299                 var targetHeight = this.minHeight - 2 * y;
28300                 
28301                 var scale = 1;
28302                 
28303                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28304                     scale = targetWidth / width;
28305                 }
28306                 
28307                 if(x > 0 && y == 0){
28308                     scale = targetHeight / height;
28309                 }
28310                 
28311                 if(x > 0 && y > 0){
28312                     scale = targetWidth / width;
28313                     
28314                     if(width < height){
28315                         scale = targetHeight / height;
28316                     }
28317                 }
28318                 
28319                 context.scale(scale, scale);
28320                 
28321                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28322                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28323
28324                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28325                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28326                 
28327                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28328                 
28329                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28330                 
28331                 break;
28332             default : 
28333                 break;
28334         }
28335         
28336         this.cropData = canvas.toDataURL(this.cropType);
28337         
28338         if(this.fireEvent('crop', this, this.cropData) !== false){
28339             this.process(this.file, this.cropData);
28340         }
28341         
28342         return;
28343         
28344     },
28345     
28346     setThumbBoxSize : function()
28347     {
28348         var width, height;
28349         
28350         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28351             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28352             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28353             
28354             this.minWidth = width;
28355             this.minHeight = height;
28356             
28357             if(this.rotate == 90 || this.rotate == 270){
28358                 this.minWidth = height;
28359                 this.minHeight = width;
28360             }
28361         }
28362         
28363         height = 300;
28364         width = Math.ceil(this.minWidth * height / this.minHeight);
28365         
28366         if(this.minWidth > this.minHeight){
28367             width = 300;
28368             height = Math.ceil(this.minHeight * width / this.minWidth);
28369         }
28370         
28371         this.thumbEl.setStyle({
28372             width : width + 'px',
28373             height : height + 'px'
28374         });
28375
28376         return;
28377             
28378     },
28379     
28380     setThumbBoxPosition : function()
28381     {
28382         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28383         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28384         
28385         this.thumbEl.setLeft(x);
28386         this.thumbEl.setTop(y);
28387         
28388     },
28389     
28390     baseRotateLevel : function()
28391     {
28392         this.baseRotate = 1;
28393         
28394         if(
28395                 typeof(this.exif) != 'undefined' &&
28396                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28397                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28398         ){
28399             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28400         }
28401         
28402         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28403         
28404     },
28405     
28406     baseScaleLevel : function()
28407     {
28408         var width, height;
28409         
28410         if(this.isDocument){
28411             
28412             if(this.baseRotate == 6 || this.baseRotate == 8){
28413             
28414                 height = this.thumbEl.getHeight();
28415                 this.baseScale = height / this.imageEl.OriginWidth;
28416
28417                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28418                     width = this.thumbEl.getWidth();
28419                     this.baseScale = width / this.imageEl.OriginHeight;
28420                 }
28421
28422                 return;
28423             }
28424
28425             height = this.thumbEl.getHeight();
28426             this.baseScale = height / this.imageEl.OriginHeight;
28427
28428             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28429                 width = this.thumbEl.getWidth();
28430                 this.baseScale = width / this.imageEl.OriginWidth;
28431             }
28432
28433             return;
28434         }
28435         
28436         if(this.baseRotate == 6 || this.baseRotate == 8){
28437             
28438             width = this.thumbEl.getHeight();
28439             this.baseScale = width / this.imageEl.OriginHeight;
28440             
28441             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28442                 height = this.thumbEl.getWidth();
28443                 this.baseScale = height / this.imageEl.OriginHeight;
28444             }
28445             
28446             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28447                 height = this.thumbEl.getWidth();
28448                 this.baseScale = height / this.imageEl.OriginHeight;
28449                 
28450                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28451                     width = this.thumbEl.getHeight();
28452                     this.baseScale = width / this.imageEl.OriginWidth;
28453                 }
28454             }
28455             
28456             return;
28457         }
28458         
28459         width = this.thumbEl.getWidth();
28460         this.baseScale = width / this.imageEl.OriginWidth;
28461         
28462         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28463             height = this.thumbEl.getHeight();
28464             this.baseScale = height / this.imageEl.OriginHeight;
28465         }
28466         
28467         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28468             
28469             height = this.thumbEl.getHeight();
28470             this.baseScale = height / this.imageEl.OriginHeight;
28471             
28472             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28473                 width = this.thumbEl.getWidth();
28474                 this.baseScale = width / this.imageEl.OriginWidth;
28475             }
28476             
28477         }
28478         
28479         return;
28480     },
28481     
28482     getScaleLevel : function()
28483     {
28484         return this.baseScale * Math.pow(1.1, this.scale);
28485     },
28486     
28487     onTouchStart : function(e)
28488     {
28489         if(!this.canvasLoaded){
28490             this.beforeSelectFile(e);
28491             return;
28492         }
28493         
28494         var touches = e.browserEvent.touches;
28495         
28496         if(!touches){
28497             return;
28498         }
28499         
28500         if(touches.length == 1){
28501             this.onMouseDown(e);
28502             return;
28503         }
28504         
28505         if(touches.length != 2){
28506             return;
28507         }
28508         
28509         var coords = [];
28510         
28511         for(var i = 0, finger; finger = touches[i]; i++){
28512             coords.push(finger.pageX, finger.pageY);
28513         }
28514         
28515         var x = Math.pow(coords[0] - coords[2], 2);
28516         var y = Math.pow(coords[1] - coords[3], 2);
28517         
28518         this.startDistance = Math.sqrt(x + y);
28519         
28520         this.startScale = this.scale;
28521         
28522         this.pinching = true;
28523         this.dragable = false;
28524         
28525     },
28526     
28527     onTouchMove : function(e)
28528     {
28529         if(!this.pinching && !this.dragable){
28530             return;
28531         }
28532         
28533         var touches = e.browserEvent.touches;
28534         
28535         if(!touches){
28536             return;
28537         }
28538         
28539         if(this.dragable){
28540             this.onMouseMove(e);
28541             return;
28542         }
28543         
28544         var coords = [];
28545         
28546         for(var i = 0, finger; finger = touches[i]; i++){
28547             coords.push(finger.pageX, finger.pageY);
28548         }
28549         
28550         var x = Math.pow(coords[0] - coords[2], 2);
28551         var y = Math.pow(coords[1] - coords[3], 2);
28552         
28553         this.endDistance = Math.sqrt(x + y);
28554         
28555         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28556         
28557         if(!this.zoomable()){
28558             this.scale = this.startScale;
28559             return;
28560         }
28561         
28562         this.draw();
28563         
28564     },
28565     
28566     onTouchEnd : function(e)
28567     {
28568         this.pinching = false;
28569         this.dragable = false;
28570         
28571     },
28572     
28573     process : function(file, crop)
28574     {
28575         if(this.loadMask){
28576             this.maskEl.mask(this.loadingText);
28577         }
28578         
28579         this.xhr = new XMLHttpRequest();
28580         
28581         file.xhr = this.xhr;
28582
28583         this.xhr.open(this.method, this.url, true);
28584         
28585         var headers = {
28586             "Accept": "application/json",
28587             "Cache-Control": "no-cache",
28588             "X-Requested-With": "XMLHttpRequest"
28589         };
28590         
28591         for (var headerName in headers) {
28592             var headerValue = headers[headerName];
28593             if (headerValue) {
28594                 this.xhr.setRequestHeader(headerName, headerValue);
28595             }
28596         }
28597         
28598         var _this = this;
28599         
28600         this.xhr.onload = function()
28601         {
28602             _this.xhrOnLoad(_this.xhr);
28603         }
28604         
28605         this.xhr.onerror = function()
28606         {
28607             _this.xhrOnError(_this.xhr);
28608         }
28609         
28610         var formData = new FormData();
28611
28612         formData.append('returnHTML', 'NO');
28613         
28614         if(crop){
28615             formData.append('crop', crop);
28616         }
28617         
28618         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28619             formData.append(this.paramName, file, file.name);
28620         }
28621         
28622         if(typeof(file.filename) != 'undefined'){
28623             formData.append('filename', file.filename);
28624         }
28625         
28626         if(typeof(file.mimetype) != 'undefined'){
28627             formData.append('mimetype', file.mimetype);
28628         }
28629         
28630         if(this.fireEvent('arrange', this, formData) != false){
28631             this.xhr.send(formData);
28632         };
28633     },
28634     
28635     xhrOnLoad : function(xhr)
28636     {
28637         if(this.loadMask){
28638             this.maskEl.unmask();
28639         }
28640         
28641         if (xhr.readyState !== 4) {
28642             this.fireEvent('exception', this, xhr);
28643             return;
28644         }
28645
28646         var response = Roo.decode(xhr.responseText);
28647         
28648         if(!response.success){
28649             this.fireEvent('exception', this, xhr);
28650             return;
28651         }
28652         
28653         var response = Roo.decode(xhr.responseText);
28654         
28655         this.fireEvent('upload', this, response);
28656         
28657     },
28658     
28659     xhrOnError : function()
28660     {
28661         if(this.loadMask){
28662             this.maskEl.unmask();
28663         }
28664         
28665         Roo.log('xhr on error');
28666         
28667         var response = Roo.decode(xhr.responseText);
28668           
28669         Roo.log(response);
28670         
28671     },
28672     
28673     prepare : function(file)
28674     {   
28675         if(this.loadMask){
28676             this.maskEl.mask(this.loadingText);
28677         }
28678         
28679         this.file = false;
28680         this.exif = {};
28681         
28682         if(typeof(file) === 'string'){
28683             this.loadCanvas(file);
28684             return;
28685         }
28686         
28687         if(!file || !this.urlAPI){
28688             return;
28689         }
28690         
28691         this.file = file;
28692         this.cropType = file.type;
28693         
28694         var _this = this;
28695         
28696         if(this.fireEvent('prepare', this, this.file) != false){
28697             
28698             var reader = new FileReader();
28699             
28700             reader.onload = function (e) {
28701                 if (e.target.error) {
28702                     Roo.log(e.target.error);
28703                     return;
28704                 }
28705                 
28706                 var buffer = e.target.result,
28707                     dataView = new DataView(buffer),
28708                     offset = 2,
28709                     maxOffset = dataView.byteLength - 4,
28710                     markerBytes,
28711                     markerLength;
28712                 
28713                 if (dataView.getUint16(0) === 0xffd8) {
28714                     while (offset < maxOffset) {
28715                         markerBytes = dataView.getUint16(offset);
28716                         
28717                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28718                             markerLength = dataView.getUint16(offset + 2) + 2;
28719                             if (offset + markerLength > dataView.byteLength) {
28720                                 Roo.log('Invalid meta data: Invalid segment size.');
28721                                 break;
28722                             }
28723                             
28724                             if(markerBytes == 0xffe1){
28725                                 _this.parseExifData(
28726                                     dataView,
28727                                     offset,
28728                                     markerLength
28729                                 );
28730                             }
28731                             
28732                             offset += markerLength;
28733                             
28734                             continue;
28735                         }
28736                         
28737                         break;
28738                     }
28739                     
28740                 }
28741                 
28742                 var url = _this.urlAPI.createObjectURL(_this.file);
28743                 
28744                 _this.loadCanvas(url);
28745                 
28746                 return;
28747             }
28748             
28749             reader.readAsArrayBuffer(this.file);
28750             
28751         }
28752         
28753     },
28754     
28755     parseExifData : function(dataView, offset, length)
28756     {
28757         var tiffOffset = offset + 10,
28758             littleEndian,
28759             dirOffset;
28760     
28761         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28762             // No Exif data, might be XMP data instead
28763             return;
28764         }
28765         
28766         // Check for the ASCII code for "Exif" (0x45786966):
28767         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28768             // No Exif data, might be XMP data instead
28769             return;
28770         }
28771         if (tiffOffset + 8 > dataView.byteLength) {
28772             Roo.log('Invalid Exif data: Invalid segment size.');
28773             return;
28774         }
28775         // Check for the two null bytes:
28776         if (dataView.getUint16(offset + 8) !== 0x0000) {
28777             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28778             return;
28779         }
28780         // Check the byte alignment:
28781         switch (dataView.getUint16(tiffOffset)) {
28782         case 0x4949:
28783             littleEndian = true;
28784             break;
28785         case 0x4D4D:
28786             littleEndian = false;
28787             break;
28788         default:
28789             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28790             return;
28791         }
28792         // Check for the TIFF tag marker (0x002A):
28793         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28794             Roo.log('Invalid Exif data: Missing TIFF marker.');
28795             return;
28796         }
28797         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28798         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28799         
28800         this.parseExifTags(
28801             dataView,
28802             tiffOffset,
28803             tiffOffset + dirOffset,
28804             littleEndian
28805         );
28806     },
28807     
28808     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28809     {
28810         var tagsNumber,
28811             dirEndOffset,
28812             i;
28813         if (dirOffset + 6 > dataView.byteLength) {
28814             Roo.log('Invalid Exif data: Invalid directory offset.');
28815             return;
28816         }
28817         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28818         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28819         if (dirEndOffset + 4 > dataView.byteLength) {
28820             Roo.log('Invalid Exif data: Invalid directory size.');
28821             return;
28822         }
28823         for (i = 0; i < tagsNumber; i += 1) {
28824             this.parseExifTag(
28825                 dataView,
28826                 tiffOffset,
28827                 dirOffset + 2 + 12 * i, // tag offset
28828                 littleEndian
28829             );
28830         }
28831         // Return the offset to the next directory:
28832         return dataView.getUint32(dirEndOffset, littleEndian);
28833     },
28834     
28835     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28836     {
28837         var tag = dataView.getUint16(offset, littleEndian);
28838         
28839         this.exif[tag] = this.getExifValue(
28840             dataView,
28841             tiffOffset,
28842             offset,
28843             dataView.getUint16(offset + 2, littleEndian), // tag type
28844             dataView.getUint32(offset + 4, littleEndian), // tag length
28845             littleEndian
28846         );
28847     },
28848     
28849     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28850     {
28851         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28852             tagSize,
28853             dataOffset,
28854             values,
28855             i,
28856             str,
28857             c;
28858     
28859         if (!tagType) {
28860             Roo.log('Invalid Exif data: Invalid tag type.');
28861             return;
28862         }
28863         
28864         tagSize = tagType.size * length;
28865         // Determine if the value is contained in the dataOffset bytes,
28866         // or if the value at the dataOffset is a pointer to the actual data:
28867         dataOffset = tagSize > 4 ?
28868                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28869         if (dataOffset + tagSize > dataView.byteLength) {
28870             Roo.log('Invalid Exif data: Invalid data offset.');
28871             return;
28872         }
28873         if (length === 1) {
28874             return tagType.getValue(dataView, dataOffset, littleEndian);
28875         }
28876         values = [];
28877         for (i = 0; i < length; i += 1) {
28878             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28879         }
28880         
28881         if (tagType.ascii) {
28882             str = '';
28883             // Concatenate the chars:
28884             for (i = 0; i < values.length; i += 1) {
28885                 c = values[i];
28886                 // Ignore the terminating NULL byte(s):
28887                 if (c === '\u0000') {
28888                     break;
28889                 }
28890                 str += c;
28891             }
28892             return str;
28893         }
28894         return values;
28895     }
28896     
28897 });
28898
28899 Roo.apply(Roo.bootstrap.UploadCropbox, {
28900     tags : {
28901         'Orientation': 0x0112
28902     },
28903     
28904     Orientation: {
28905             1: 0, //'top-left',
28906 //            2: 'top-right',
28907             3: 180, //'bottom-right',
28908 //            4: 'bottom-left',
28909 //            5: 'left-top',
28910             6: 90, //'right-top',
28911 //            7: 'right-bottom',
28912             8: 270 //'left-bottom'
28913     },
28914     
28915     exifTagTypes : {
28916         // byte, 8-bit unsigned int:
28917         1: {
28918             getValue: function (dataView, dataOffset) {
28919                 return dataView.getUint8(dataOffset);
28920             },
28921             size: 1
28922         },
28923         // ascii, 8-bit byte:
28924         2: {
28925             getValue: function (dataView, dataOffset) {
28926                 return String.fromCharCode(dataView.getUint8(dataOffset));
28927             },
28928             size: 1,
28929             ascii: true
28930         },
28931         // short, 16 bit int:
28932         3: {
28933             getValue: function (dataView, dataOffset, littleEndian) {
28934                 return dataView.getUint16(dataOffset, littleEndian);
28935             },
28936             size: 2
28937         },
28938         // long, 32 bit int:
28939         4: {
28940             getValue: function (dataView, dataOffset, littleEndian) {
28941                 return dataView.getUint32(dataOffset, littleEndian);
28942             },
28943             size: 4
28944         },
28945         // rational = two long values, first is numerator, second is denominator:
28946         5: {
28947             getValue: function (dataView, dataOffset, littleEndian) {
28948                 return dataView.getUint32(dataOffset, littleEndian) /
28949                     dataView.getUint32(dataOffset + 4, littleEndian);
28950             },
28951             size: 8
28952         },
28953         // slong, 32 bit signed int:
28954         9: {
28955             getValue: function (dataView, dataOffset, littleEndian) {
28956                 return dataView.getInt32(dataOffset, littleEndian);
28957             },
28958             size: 4
28959         },
28960         // srational, two slongs, first is numerator, second is denominator:
28961         10: {
28962             getValue: function (dataView, dataOffset, littleEndian) {
28963                 return dataView.getInt32(dataOffset, littleEndian) /
28964                     dataView.getInt32(dataOffset + 4, littleEndian);
28965             },
28966             size: 8
28967         }
28968     },
28969     
28970     footer : {
28971         STANDARD : [
28972             {
28973                 tag : 'div',
28974                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28975                 action : 'rotate-left',
28976                 cn : [
28977                     {
28978                         tag : 'button',
28979                         cls : 'btn btn-default',
28980                         html : '<i class="fa fa-undo"></i>'
28981                     }
28982                 ]
28983             },
28984             {
28985                 tag : 'div',
28986                 cls : 'btn-group roo-upload-cropbox-picture',
28987                 action : 'picture',
28988                 cn : [
28989                     {
28990                         tag : 'button',
28991                         cls : 'btn btn-default',
28992                         html : '<i class="fa fa-picture-o"></i>'
28993                     }
28994                 ]
28995             },
28996             {
28997                 tag : 'div',
28998                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28999                 action : 'rotate-right',
29000                 cn : [
29001                     {
29002                         tag : 'button',
29003                         cls : 'btn btn-default',
29004                         html : '<i class="fa fa-repeat"></i>'
29005                     }
29006                 ]
29007             }
29008         ],
29009         DOCUMENT : [
29010             {
29011                 tag : 'div',
29012                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29013                 action : 'rotate-left',
29014                 cn : [
29015                     {
29016                         tag : 'button',
29017                         cls : 'btn btn-default',
29018                         html : '<i class="fa fa-undo"></i>'
29019                     }
29020                 ]
29021             },
29022             {
29023                 tag : 'div',
29024                 cls : 'btn-group roo-upload-cropbox-download',
29025                 action : 'download',
29026                 cn : [
29027                     {
29028                         tag : 'button',
29029                         cls : 'btn btn-default',
29030                         html : '<i class="fa fa-download"></i>'
29031                     }
29032                 ]
29033             },
29034             {
29035                 tag : 'div',
29036                 cls : 'btn-group roo-upload-cropbox-crop',
29037                 action : 'crop',
29038                 cn : [
29039                     {
29040                         tag : 'button',
29041                         cls : 'btn btn-default',
29042                         html : '<i class="fa fa-crop"></i>'
29043                     }
29044                 ]
29045             },
29046             {
29047                 tag : 'div',
29048                 cls : 'btn-group roo-upload-cropbox-trash',
29049                 action : 'trash',
29050                 cn : [
29051                     {
29052                         tag : 'button',
29053                         cls : 'btn btn-default',
29054                         html : '<i class="fa fa-trash"></i>'
29055                     }
29056                 ]
29057             },
29058             {
29059                 tag : 'div',
29060                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29061                 action : 'rotate-right',
29062                 cn : [
29063                     {
29064                         tag : 'button',
29065                         cls : 'btn btn-default',
29066                         html : '<i class="fa fa-repeat"></i>'
29067                     }
29068                 ]
29069             }
29070         ],
29071         ROTATOR : [
29072             {
29073                 tag : 'div',
29074                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29075                 action : 'rotate-left',
29076                 cn : [
29077                     {
29078                         tag : 'button',
29079                         cls : 'btn btn-default',
29080                         html : '<i class="fa fa-undo"></i>'
29081                     }
29082                 ]
29083             },
29084             {
29085                 tag : 'div',
29086                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29087                 action : 'rotate-right',
29088                 cn : [
29089                     {
29090                         tag : 'button',
29091                         cls : 'btn btn-default',
29092                         html : '<i class="fa fa-repeat"></i>'
29093                     }
29094                 ]
29095             }
29096         ]
29097     }
29098 });
29099
29100 /*
29101 * Licence: LGPL
29102 */
29103
29104 /**
29105  * @class Roo.bootstrap.DocumentManager
29106  * @extends Roo.bootstrap.Component
29107  * Bootstrap DocumentManager class
29108  * @cfg {String} paramName default 'imageUpload'
29109  * @cfg {String} toolTipName default 'filename'
29110  * @cfg {String} method default POST
29111  * @cfg {String} url action url
29112  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
29113  * @cfg {Boolean} multiple multiple upload default true
29114  * @cfg {Number} thumbSize default 300
29115  * @cfg {String} fieldLabel
29116  * @cfg {Number} labelWidth default 4
29117  * @cfg {String} labelAlign (left|top) default left
29118  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
29119 * @cfg {Number} labellg set the width of label (1-12)
29120  * @cfg {Number} labelmd set the width of label (1-12)
29121  * @cfg {Number} labelsm set the width of label (1-12)
29122  * @cfg {Number} labelxs set the width of label (1-12)
29123  * 
29124  * @constructor
29125  * Create a new DocumentManager
29126  * @param {Object} config The config object
29127  */
29128
29129 Roo.bootstrap.DocumentManager = function(config){
29130     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
29131     
29132     this.files = [];
29133     this.delegates = [];
29134     
29135     this.addEvents({
29136         /**
29137          * @event initial
29138          * Fire when initial the DocumentManager
29139          * @param {Roo.bootstrap.DocumentManager} this
29140          */
29141         "initial" : true,
29142         /**
29143          * @event inspect
29144          * inspect selected file
29145          * @param {Roo.bootstrap.DocumentManager} this
29146          * @param {File} file
29147          */
29148         "inspect" : true,
29149         /**
29150          * @event exception
29151          * Fire when xhr load exception
29152          * @param {Roo.bootstrap.DocumentManager} this
29153          * @param {XMLHttpRequest} xhr
29154          */
29155         "exception" : true,
29156         /**
29157          * @event afterupload
29158          * Fire when xhr load exception
29159          * @param {Roo.bootstrap.DocumentManager} this
29160          * @param {XMLHttpRequest} xhr
29161          */
29162         "afterupload" : true,
29163         /**
29164          * @event prepare
29165          * prepare the form data
29166          * @param {Roo.bootstrap.DocumentManager} this
29167          * @param {Object} formData
29168          */
29169         "prepare" : true,
29170         /**
29171          * @event remove
29172          * Fire when remove the file
29173          * @param {Roo.bootstrap.DocumentManager} this
29174          * @param {Object} file
29175          */
29176         "remove" : true,
29177         /**
29178          * @event refresh
29179          * Fire after refresh the file
29180          * @param {Roo.bootstrap.DocumentManager} this
29181          */
29182         "refresh" : true,
29183         /**
29184          * @event click
29185          * Fire after click the image
29186          * @param {Roo.bootstrap.DocumentManager} this
29187          * @param {Object} file
29188          */
29189         "click" : true,
29190         /**
29191          * @event edit
29192          * Fire when upload a image and editable set to true
29193          * @param {Roo.bootstrap.DocumentManager} this
29194          * @param {Object} file
29195          */
29196         "edit" : true,
29197         /**
29198          * @event beforeselectfile
29199          * Fire before select file
29200          * @param {Roo.bootstrap.DocumentManager} this
29201          */
29202         "beforeselectfile" : true,
29203         /**
29204          * @event process
29205          * Fire before process file
29206          * @param {Roo.bootstrap.DocumentManager} this
29207          * @param {Object} file
29208          */
29209         "process" : true,
29210         /**
29211          * @event previewrendered
29212          * Fire when preview rendered
29213          * @param {Roo.bootstrap.DocumentManager} this
29214          * @param {Object} file
29215          */
29216         "previewrendered" : true,
29217         /**
29218          */
29219         "previewResize" : true
29220         
29221     });
29222 };
29223
29224 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
29225     
29226     boxes : 0,
29227     inputName : '',
29228     thumbSize : 300,
29229     multiple : true,
29230     files : false,
29231     method : 'POST',
29232     url : '',
29233     paramName : 'imageUpload',
29234     toolTipName : 'filename',
29235     fieldLabel : '',
29236     labelWidth : 4,
29237     labelAlign : 'left',
29238     editable : true,
29239     delegates : false,
29240     xhr : false, 
29241     
29242     labellg : 0,
29243     labelmd : 0,
29244     labelsm : 0,
29245     labelxs : 0,
29246     
29247     getAutoCreate : function()
29248     {   
29249         var managerWidget = {
29250             tag : 'div',
29251             cls : 'roo-document-manager',
29252             cn : [
29253                 {
29254                     tag : 'input',
29255                     cls : 'roo-document-manager-selector',
29256                     type : 'file'
29257                 },
29258                 {
29259                     tag : 'div',
29260                     cls : 'roo-document-manager-uploader',
29261                     cn : [
29262                         {
29263                             tag : 'div',
29264                             cls : 'roo-document-manager-upload-btn',
29265                             html : '<i class="fa fa-plus"></i>'
29266                         }
29267                     ]
29268                     
29269                 }
29270             ]
29271         };
29272         
29273         var content = [
29274             {
29275                 tag : 'div',
29276                 cls : 'column col-md-12',
29277                 cn : managerWidget
29278             }
29279         ];
29280         
29281         if(this.fieldLabel.length){
29282             
29283             content = [
29284                 {
29285                     tag : 'div',
29286                     cls : 'column col-md-12',
29287                     html : this.fieldLabel
29288                 },
29289                 {
29290                     tag : 'div',
29291                     cls : 'column col-md-12',
29292                     cn : managerWidget
29293                 }
29294             ];
29295
29296             if(this.labelAlign == 'left'){
29297                 content = [
29298                     {
29299                         tag : 'div',
29300                         cls : 'column',
29301                         html : this.fieldLabel
29302                     },
29303                     {
29304                         tag : 'div',
29305                         cls : 'column',
29306                         cn : managerWidget
29307                     }
29308                 ];
29309                 
29310                 if(this.labelWidth > 12){
29311                     content[0].style = "width: " + this.labelWidth + 'px';
29312                 }
29313
29314                 if(this.labelWidth < 13 && this.labelmd == 0){
29315                     this.labelmd = this.labelWidth;
29316                 }
29317
29318                 if(this.labellg > 0){
29319                     content[0].cls += ' col-lg-' + this.labellg;
29320                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29321                 }
29322
29323                 if(this.labelmd > 0){
29324                     content[0].cls += ' col-md-' + this.labelmd;
29325                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29326                 }
29327
29328                 if(this.labelsm > 0){
29329                     content[0].cls += ' col-sm-' + this.labelsm;
29330                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29331                 }
29332
29333                 if(this.labelxs > 0){
29334                     content[0].cls += ' col-xs-' + this.labelxs;
29335                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29336                 }
29337                 
29338             }
29339         }
29340         
29341         var cfg = {
29342             tag : 'div',
29343             cls : 'row clearfix',
29344             cn : content
29345         };
29346         
29347         return cfg;
29348         
29349     },
29350     
29351     initEvents : function()
29352     {
29353         this.managerEl = this.el.select('.roo-document-manager', true).first();
29354         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29355         
29356         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29357         this.selectorEl.hide();
29358         
29359         if(this.multiple){
29360             this.selectorEl.attr('multiple', 'multiple');
29361         }
29362         
29363         this.selectorEl.on('change', this.onFileSelected, this);
29364         
29365         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29366         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29367         
29368         this.uploader.on('click', this.onUploaderClick, this);
29369         
29370         this.renderProgressDialog();
29371         
29372         var _this = this;
29373         
29374         window.addEventListener("resize", function() { _this.refresh(); } );
29375         
29376         this.fireEvent('initial', this);
29377     },
29378     
29379     renderProgressDialog : function()
29380     {
29381         var _this = this;
29382         
29383         this.progressDialog = new Roo.bootstrap.Modal({
29384             cls : 'roo-document-manager-progress-dialog',
29385             allow_close : false,
29386             animate : false,
29387             title : '',
29388             buttons : [
29389                 {
29390                     name  :'cancel',
29391                     weight : 'danger',
29392                     html : 'Cancel'
29393                 }
29394             ], 
29395             listeners : { 
29396                 btnclick : function() {
29397                     _this.uploadCancel();
29398                     this.hide();
29399                 }
29400             }
29401         });
29402          
29403         this.progressDialog.render(Roo.get(document.body));
29404          
29405         this.progress = new Roo.bootstrap.Progress({
29406             cls : 'roo-document-manager-progress',
29407             active : true,
29408             striped : true
29409         });
29410         
29411         this.progress.render(this.progressDialog.getChildContainer());
29412         
29413         this.progressBar = new Roo.bootstrap.ProgressBar({
29414             cls : 'roo-document-manager-progress-bar',
29415             aria_valuenow : 0,
29416             aria_valuemin : 0,
29417             aria_valuemax : 12,
29418             panel : 'success'
29419         });
29420         
29421         this.progressBar.render(this.progress.getChildContainer());
29422     },
29423     
29424     onUploaderClick : function(e)
29425     {
29426         e.preventDefault();
29427      
29428         if(this.fireEvent('beforeselectfile', this) != false){
29429             this.selectorEl.dom.click();
29430         }
29431         
29432     },
29433     
29434     onFileSelected : function(e)
29435     {
29436         e.preventDefault();
29437         
29438         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29439             return;
29440         }
29441         
29442         Roo.each(this.selectorEl.dom.files, function(file){
29443             if(this.fireEvent('inspect', this, file) != false){
29444                 this.files.push(file);
29445             }
29446         }, this);
29447         
29448         this.queue();
29449         
29450     },
29451     
29452     queue : function()
29453     {
29454         this.selectorEl.dom.value = '';
29455         
29456         if(!this.files || !this.files.length){
29457             return;
29458         }
29459         
29460         if(this.boxes > 0 && this.files.length > this.boxes){
29461             this.files = this.files.slice(0, this.boxes);
29462         }
29463         
29464         this.uploader.show();
29465         
29466         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29467             this.uploader.hide();
29468         }
29469         
29470         var _this = this;
29471         
29472         var files = [];
29473         
29474         var docs = [];
29475         
29476         Roo.each(this.files, function(file){
29477             
29478             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29479                 var f = this.renderPreview(file);
29480                 files.push(f);
29481                 return;
29482             }
29483             
29484             if(file.type.indexOf('image') != -1){
29485                 this.delegates.push(
29486                     (function(){
29487                         _this.process(file);
29488                     }).createDelegate(this)
29489                 );
29490         
29491                 return;
29492             }
29493             
29494             docs.push(
29495                 (function(){
29496                     _this.process(file);
29497                 }).createDelegate(this)
29498             );
29499             
29500         }, this);
29501         
29502         this.files = files;
29503         
29504         this.delegates = this.delegates.concat(docs);
29505         
29506         if(!this.delegates.length){
29507             this.refresh();
29508             return;
29509         }
29510         
29511         this.progressBar.aria_valuemax = this.delegates.length;
29512         
29513         this.arrange();
29514         
29515         return;
29516     },
29517     
29518     arrange : function()
29519     {
29520         if(!this.delegates.length){
29521             this.progressDialog.hide();
29522             this.refresh();
29523             return;
29524         }
29525         
29526         var delegate = this.delegates.shift();
29527         
29528         this.progressDialog.show();
29529         
29530         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29531         
29532         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29533         
29534         delegate();
29535     },
29536     
29537     refresh : function()
29538     {
29539         this.uploader.show();
29540         
29541         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29542             this.uploader.hide();
29543         }
29544         
29545         Roo.isTouch ? this.closable(false) : this.closable(true);
29546         
29547         this.fireEvent('refresh', this);
29548     },
29549     
29550     onRemove : function(e, el, o)
29551     {
29552         e.preventDefault();
29553         
29554         this.fireEvent('remove', this, o);
29555         
29556     },
29557     
29558     remove : function(o)
29559     {
29560         var files = [];
29561         
29562         Roo.each(this.files, function(file){
29563             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29564                 files.push(file);
29565                 return;
29566             }
29567
29568             o.target.remove();
29569
29570         }, this);
29571         
29572         this.files = files;
29573         
29574         this.refresh();
29575     },
29576     
29577     clear : function()
29578     {
29579         Roo.each(this.files, function(file){
29580             if(!file.target){
29581                 return;
29582             }
29583             
29584             file.target.remove();
29585
29586         }, this);
29587         
29588         this.files = [];
29589         
29590         this.refresh();
29591     },
29592     
29593     onClick : function(e, el, o)
29594     {
29595         e.preventDefault();
29596         
29597         this.fireEvent('click', this, o);
29598         
29599     },
29600     
29601     closable : function(closable)
29602     {
29603         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29604             
29605             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29606             
29607             if(closable){
29608                 el.show();
29609                 return;
29610             }
29611             
29612             el.hide();
29613             
29614         }, this);
29615     },
29616     
29617     xhrOnLoad : function(xhr)
29618     {
29619         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29620             el.remove();
29621         }, this);
29622         
29623         if (xhr.readyState !== 4) {
29624             this.arrange();
29625             this.fireEvent('exception', this, xhr);
29626             return;
29627         }
29628
29629         var response = Roo.decode(xhr.responseText);
29630         
29631         if(!response.success){
29632             this.arrange();
29633             this.fireEvent('exception', this, xhr);
29634             return;
29635         }
29636         
29637         var file = this.renderPreview(response.data);
29638         
29639         this.files.push(file);
29640         
29641         this.arrange();
29642         
29643         this.fireEvent('afterupload', this, xhr);
29644         
29645     },
29646     
29647     xhrOnError : function(xhr)
29648     {
29649         Roo.log('xhr on error');
29650         
29651         var response = Roo.decode(xhr.responseText);
29652           
29653         Roo.log(response);
29654         
29655         this.arrange();
29656     },
29657     
29658     process : function(file)
29659     {
29660         if(this.fireEvent('process', this, file) !== false){
29661             if(this.editable && file.type.indexOf('image') != -1){
29662                 this.fireEvent('edit', this, file);
29663                 return;
29664             }
29665
29666             this.uploadStart(file, false);
29667
29668             return;
29669         }
29670         
29671     },
29672     
29673     uploadStart : function(file, crop)
29674     {
29675         this.xhr = new XMLHttpRequest();
29676         
29677         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29678             this.arrange();
29679             return;
29680         }
29681         
29682         file.xhr = this.xhr;
29683             
29684         this.managerEl.createChild({
29685             tag : 'div',
29686             cls : 'roo-document-manager-loading',
29687             cn : [
29688                 {
29689                     tag : 'div',
29690                     tooltip : file.name,
29691                     cls : 'roo-document-manager-thumb',
29692                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29693                 }
29694             ]
29695
29696         });
29697
29698         this.xhr.open(this.method, this.url, true);
29699         
29700         var headers = {
29701             "Accept": "application/json",
29702             "Cache-Control": "no-cache",
29703             "X-Requested-With": "XMLHttpRequest"
29704         };
29705         
29706         for (var headerName in headers) {
29707             var headerValue = headers[headerName];
29708             if (headerValue) {
29709                 this.xhr.setRequestHeader(headerName, headerValue);
29710             }
29711         }
29712         
29713         var _this = this;
29714         
29715         this.xhr.onload = function()
29716         {
29717             _this.xhrOnLoad(_this.xhr);
29718         }
29719         
29720         this.xhr.onerror = function()
29721         {
29722             _this.xhrOnError(_this.xhr);
29723         }
29724         
29725         var formData = new FormData();
29726
29727         formData.append('returnHTML', 'NO');
29728         
29729         if(crop){
29730             formData.append('crop', crop);
29731         }
29732         
29733         formData.append(this.paramName, file, file.name);
29734         
29735         var options = {
29736             file : file, 
29737             manually : false
29738         };
29739         
29740         if(this.fireEvent('prepare', this, formData, options) != false){
29741             
29742             if(options.manually){
29743                 return;
29744             }
29745             
29746             this.xhr.send(formData);
29747             return;
29748         };
29749         
29750         this.uploadCancel();
29751     },
29752     
29753     uploadCancel : function()
29754     {
29755         if (this.xhr) {
29756             this.xhr.abort();
29757         }
29758         
29759         this.delegates = [];
29760         
29761         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29762             el.remove();
29763         }, this);
29764         
29765         this.arrange();
29766     },
29767     
29768     renderPreview : function(file)
29769     {
29770         if(typeof(file.target) != 'undefined' && file.target){
29771             return file;
29772         }
29773         
29774         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29775         
29776         var previewEl = this.managerEl.createChild({
29777             tag : 'div',
29778             cls : 'roo-document-manager-preview',
29779             cn : [
29780                 {
29781                     tag : 'div',
29782                     tooltip : file[this.toolTipName],
29783                     cls : 'roo-document-manager-thumb',
29784                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29785                 },
29786                 {
29787                     tag : 'button',
29788                     cls : 'close',
29789                     html : '<i class="fa fa-times-circle"></i>'
29790                 }
29791             ]
29792         });
29793
29794         var close = previewEl.select('button.close', true).first();
29795
29796         close.on('click', this.onRemove, this, file);
29797
29798         file.target = previewEl;
29799
29800         var image = previewEl.select('img', true).first();
29801         
29802         var _this = this;
29803         
29804         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29805         
29806         image.on('click', this.onClick, this, file);
29807         
29808         this.fireEvent('previewrendered', this, file);
29809         
29810         return file;
29811         
29812     },
29813     
29814     onPreviewLoad : function(file, image)
29815     {
29816         if(typeof(file.target) == 'undefined' || !file.target){
29817             return;
29818         }
29819         
29820         var width = image.dom.naturalWidth || image.dom.width;
29821         var height = image.dom.naturalHeight || image.dom.height;
29822         
29823         if(!this.previewResize) {
29824             return;
29825         }
29826         
29827         if(width > height){
29828             file.target.addClass('wide');
29829             return;
29830         }
29831         
29832         file.target.addClass('tall');
29833         return;
29834         
29835     },
29836     
29837     uploadFromSource : function(file, crop)
29838     {
29839         this.xhr = new XMLHttpRequest();
29840         
29841         this.managerEl.createChild({
29842             tag : 'div',
29843             cls : 'roo-document-manager-loading',
29844             cn : [
29845                 {
29846                     tag : 'div',
29847                     tooltip : file.name,
29848                     cls : 'roo-document-manager-thumb',
29849                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29850                 }
29851             ]
29852
29853         });
29854
29855         this.xhr.open(this.method, this.url, true);
29856         
29857         var headers = {
29858             "Accept": "application/json",
29859             "Cache-Control": "no-cache",
29860             "X-Requested-With": "XMLHttpRequest"
29861         };
29862         
29863         for (var headerName in headers) {
29864             var headerValue = headers[headerName];
29865             if (headerValue) {
29866                 this.xhr.setRequestHeader(headerName, headerValue);
29867             }
29868         }
29869         
29870         var _this = this;
29871         
29872         this.xhr.onload = function()
29873         {
29874             _this.xhrOnLoad(_this.xhr);
29875         }
29876         
29877         this.xhr.onerror = function()
29878         {
29879             _this.xhrOnError(_this.xhr);
29880         }
29881         
29882         var formData = new FormData();
29883
29884         formData.append('returnHTML', 'NO');
29885         
29886         formData.append('crop', crop);
29887         
29888         if(typeof(file.filename) != 'undefined'){
29889             formData.append('filename', file.filename);
29890         }
29891         
29892         if(typeof(file.mimetype) != 'undefined'){
29893             formData.append('mimetype', file.mimetype);
29894         }
29895         
29896         Roo.log(formData);
29897         
29898         if(this.fireEvent('prepare', this, formData) != false){
29899             this.xhr.send(formData);
29900         };
29901     }
29902 });
29903
29904 /*
29905 * Licence: LGPL
29906 */
29907
29908 /**
29909  * @class Roo.bootstrap.DocumentViewer
29910  * @extends Roo.bootstrap.Component
29911  * Bootstrap DocumentViewer class
29912  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29913  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29914  * 
29915  * @constructor
29916  * Create a new DocumentViewer
29917  * @param {Object} config The config object
29918  */
29919
29920 Roo.bootstrap.DocumentViewer = function(config){
29921     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29922     
29923     this.addEvents({
29924         /**
29925          * @event initial
29926          * Fire after initEvent
29927          * @param {Roo.bootstrap.DocumentViewer} this
29928          */
29929         "initial" : true,
29930         /**
29931          * @event click
29932          * Fire after click
29933          * @param {Roo.bootstrap.DocumentViewer} this
29934          */
29935         "click" : true,
29936         /**
29937          * @event download
29938          * Fire after download button
29939          * @param {Roo.bootstrap.DocumentViewer} this
29940          */
29941         "download" : true,
29942         /**
29943          * @event trash
29944          * Fire after trash button
29945          * @param {Roo.bootstrap.DocumentViewer} this
29946          */
29947         "trash" : true
29948         
29949     });
29950 };
29951
29952 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29953     
29954     showDownload : true,
29955     
29956     showTrash : true,
29957     
29958     getAutoCreate : function()
29959     {
29960         var cfg = {
29961             tag : 'div',
29962             cls : 'roo-document-viewer',
29963             cn : [
29964                 {
29965                     tag : 'div',
29966                     cls : 'roo-document-viewer-body',
29967                     cn : [
29968                         {
29969                             tag : 'div',
29970                             cls : 'roo-document-viewer-thumb',
29971                             cn : [
29972                                 {
29973                                     tag : 'img',
29974                                     cls : 'roo-document-viewer-image'
29975                                 }
29976                             ]
29977                         }
29978                     ]
29979                 },
29980                 {
29981                     tag : 'div',
29982                     cls : 'roo-document-viewer-footer',
29983                     cn : {
29984                         tag : 'div',
29985                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29986                         cn : [
29987                             {
29988                                 tag : 'div',
29989                                 cls : 'btn-group roo-document-viewer-download',
29990                                 cn : [
29991                                     {
29992                                         tag : 'button',
29993                                         cls : 'btn btn-default',
29994                                         html : '<i class="fa fa-download"></i>'
29995                                     }
29996                                 ]
29997                             },
29998                             {
29999                                 tag : 'div',
30000                                 cls : 'btn-group roo-document-viewer-trash',
30001                                 cn : [
30002                                     {
30003                                         tag : 'button',
30004                                         cls : 'btn btn-default',
30005                                         html : '<i class="fa fa-trash"></i>'
30006                                     }
30007                                 ]
30008                             }
30009                         ]
30010                     }
30011                 }
30012             ]
30013         };
30014         
30015         return cfg;
30016     },
30017     
30018     initEvents : function()
30019     {
30020         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
30021         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
30022         
30023         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
30024         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
30025         
30026         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
30027         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
30028         
30029         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
30030         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
30031         
30032         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
30033         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
30034         
30035         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
30036         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
30037         
30038         this.bodyEl.on('click', this.onClick, this);
30039         this.downloadBtn.on('click', this.onDownload, this);
30040         this.trashBtn.on('click', this.onTrash, this);
30041         
30042         this.downloadBtn.hide();
30043         this.trashBtn.hide();
30044         
30045         if(this.showDownload){
30046             this.downloadBtn.show();
30047         }
30048         
30049         if(this.showTrash){
30050             this.trashBtn.show();
30051         }
30052         
30053         if(!this.showDownload && !this.showTrash) {
30054             this.footerEl.hide();
30055         }
30056         
30057     },
30058     
30059     initial : function()
30060     {
30061         this.fireEvent('initial', this);
30062         
30063     },
30064     
30065     onClick : function(e)
30066     {
30067         e.preventDefault();
30068         
30069         this.fireEvent('click', this);
30070     },
30071     
30072     onDownload : function(e)
30073     {
30074         e.preventDefault();
30075         
30076         this.fireEvent('download', this);
30077     },
30078     
30079     onTrash : function(e)
30080     {
30081         e.preventDefault();
30082         
30083         this.fireEvent('trash', this);
30084     }
30085     
30086 });
30087 /*
30088  * - LGPL
30089  *
30090  * nav progress bar
30091  * 
30092  */
30093
30094 /**
30095  * @class Roo.bootstrap.NavProgressBar
30096  * @extends Roo.bootstrap.Component
30097  * Bootstrap NavProgressBar class
30098  * 
30099  * @constructor
30100  * Create a new nav progress bar
30101  * @param {Object} config The config object
30102  */
30103
30104 Roo.bootstrap.NavProgressBar = function(config){
30105     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
30106
30107     this.bullets = this.bullets || [];
30108    
30109 //    Roo.bootstrap.NavProgressBar.register(this);
30110      this.addEvents({
30111         /**
30112              * @event changed
30113              * Fires when the active item changes
30114              * @param {Roo.bootstrap.NavProgressBar} this
30115              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
30116              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
30117          */
30118         'changed': true
30119      });
30120     
30121 };
30122
30123 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
30124     
30125     bullets : [],
30126     barItems : [],
30127     
30128     getAutoCreate : function()
30129     {
30130         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
30131         
30132         cfg = {
30133             tag : 'div',
30134             cls : 'roo-navigation-bar-group',
30135             cn : [
30136                 {
30137                     tag : 'div',
30138                     cls : 'roo-navigation-top-bar'
30139                 },
30140                 {
30141                     tag : 'div',
30142                     cls : 'roo-navigation-bullets-bar',
30143                     cn : [
30144                         {
30145                             tag : 'ul',
30146                             cls : 'roo-navigation-bar'
30147                         }
30148                     ]
30149                 },
30150                 
30151                 {
30152                     tag : 'div',
30153                     cls : 'roo-navigation-bottom-bar'
30154                 }
30155             ]
30156             
30157         };
30158         
30159         return cfg;
30160         
30161     },
30162     
30163     initEvents: function() 
30164     {
30165         
30166     },
30167     
30168     onRender : function(ct, position) 
30169     {
30170         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30171         
30172         if(this.bullets.length){
30173             Roo.each(this.bullets, function(b){
30174                this.addItem(b);
30175             }, this);
30176         }
30177         
30178         this.format();
30179         
30180     },
30181     
30182     addItem : function(cfg)
30183     {
30184         var item = new Roo.bootstrap.NavProgressItem(cfg);
30185         
30186         item.parentId = this.id;
30187         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
30188         
30189         if(cfg.html){
30190             var top = new Roo.bootstrap.Element({
30191                 tag : 'div',
30192                 cls : 'roo-navigation-bar-text'
30193             });
30194             
30195             var bottom = new Roo.bootstrap.Element({
30196                 tag : 'div',
30197                 cls : 'roo-navigation-bar-text'
30198             });
30199             
30200             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
30201             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
30202             
30203             var topText = new Roo.bootstrap.Element({
30204                 tag : 'span',
30205                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
30206             });
30207             
30208             var bottomText = new Roo.bootstrap.Element({
30209                 tag : 'span',
30210                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30211             });
30212             
30213             topText.onRender(top.el, null);
30214             bottomText.onRender(bottom.el, null);
30215             
30216             item.topEl = top;
30217             item.bottomEl = bottom;
30218         }
30219         
30220         this.barItems.push(item);
30221         
30222         return item;
30223     },
30224     
30225     getActive : function()
30226     {
30227         var active = false;
30228         
30229         Roo.each(this.barItems, function(v){
30230             
30231             if (!v.isActive()) {
30232                 return;
30233             }
30234             
30235             active = v;
30236             return false;
30237             
30238         });
30239         
30240         return active;
30241     },
30242     
30243     setActiveItem : function(item)
30244     {
30245         var prev = false;
30246         
30247         Roo.each(this.barItems, function(v){
30248             if (v.rid == item.rid) {
30249                 return ;
30250             }
30251             
30252             if (v.isActive()) {
30253                 v.setActive(false);
30254                 prev = v;
30255             }
30256         });
30257
30258         item.setActive(true);
30259         
30260         this.fireEvent('changed', this, item, prev);
30261     },
30262     
30263     getBarItem: function(rid)
30264     {
30265         var ret = false;
30266         
30267         Roo.each(this.barItems, function(e) {
30268             if (e.rid != rid) {
30269                 return;
30270             }
30271             
30272             ret =  e;
30273             return false;
30274         });
30275         
30276         return ret;
30277     },
30278     
30279     indexOfItem : function(item)
30280     {
30281         var index = false;
30282         
30283         Roo.each(this.barItems, function(v, i){
30284             
30285             if (v.rid != item.rid) {
30286                 return;
30287             }
30288             
30289             index = i;
30290             return false
30291         });
30292         
30293         return index;
30294     },
30295     
30296     setActiveNext : function()
30297     {
30298         var i = this.indexOfItem(this.getActive());
30299         
30300         if (i > this.barItems.length) {
30301             return;
30302         }
30303         
30304         this.setActiveItem(this.barItems[i+1]);
30305     },
30306     
30307     setActivePrev : function()
30308     {
30309         var i = this.indexOfItem(this.getActive());
30310         
30311         if (i  < 1) {
30312             return;
30313         }
30314         
30315         this.setActiveItem(this.barItems[i-1]);
30316     },
30317     
30318     format : function()
30319     {
30320         if(!this.barItems.length){
30321             return;
30322         }
30323      
30324         var width = 100 / this.barItems.length;
30325         
30326         Roo.each(this.barItems, function(i){
30327             i.el.setStyle('width', width + '%');
30328             i.topEl.el.setStyle('width', width + '%');
30329             i.bottomEl.el.setStyle('width', width + '%');
30330         }, this);
30331         
30332     }
30333     
30334 });
30335 /*
30336  * - LGPL
30337  *
30338  * Nav Progress Item
30339  * 
30340  */
30341
30342 /**
30343  * @class Roo.bootstrap.NavProgressItem
30344  * @extends Roo.bootstrap.Component
30345  * Bootstrap NavProgressItem class
30346  * @cfg {String} rid the reference id
30347  * @cfg {Boolean} active (true|false) Is item active default false
30348  * @cfg {Boolean} disabled (true|false) Is item active default false
30349  * @cfg {String} html
30350  * @cfg {String} position (top|bottom) text position default bottom
30351  * @cfg {String} icon show icon instead of number
30352  * 
30353  * @constructor
30354  * Create a new NavProgressItem
30355  * @param {Object} config The config object
30356  */
30357 Roo.bootstrap.NavProgressItem = function(config){
30358     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30359     this.addEvents({
30360         // raw events
30361         /**
30362          * @event click
30363          * The raw click event for the entire grid.
30364          * @param {Roo.bootstrap.NavProgressItem} this
30365          * @param {Roo.EventObject} e
30366          */
30367         "click" : true
30368     });
30369    
30370 };
30371
30372 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30373     
30374     rid : '',
30375     active : false,
30376     disabled : false,
30377     html : '',
30378     position : 'bottom',
30379     icon : false,
30380     
30381     getAutoCreate : function()
30382     {
30383         var iconCls = 'roo-navigation-bar-item-icon';
30384         
30385         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30386         
30387         var cfg = {
30388             tag: 'li',
30389             cls: 'roo-navigation-bar-item',
30390             cn : [
30391                 {
30392                     tag : 'i',
30393                     cls : iconCls
30394                 }
30395             ]
30396         };
30397         
30398         if(this.active){
30399             cfg.cls += ' active';
30400         }
30401         if(this.disabled){
30402             cfg.cls += ' disabled';
30403         }
30404         
30405         return cfg;
30406     },
30407     
30408     disable : function()
30409     {
30410         this.setDisabled(true);
30411     },
30412     
30413     enable : function()
30414     {
30415         this.setDisabled(false);
30416     },
30417     
30418     initEvents: function() 
30419     {
30420         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30421         
30422         this.iconEl.on('click', this.onClick, this);
30423     },
30424     
30425     onClick : function(e)
30426     {
30427         e.preventDefault();
30428         
30429         if(this.disabled){
30430             return;
30431         }
30432         
30433         if(this.fireEvent('click', this, e) === false){
30434             return;
30435         };
30436         
30437         this.parent().setActiveItem(this);
30438     },
30439     
30440     isActive: function () 
30441     {
30442         return this.active;
30443     },
30444     
30445     setActive : function(state)
30446     {
30447         if(this.active == state){
30448             return;
30449         }
30450         
30451         this.active = state;
30452         
30453         if (state) {
30454             this.el.addClass('active');
30455             return;
30456         }
30457         
30458         this.el.removeClass('active');
30459         
30460         return;
30461     },
30462     
30463     setDisabled : function(state)
30464     {
30465         if(this.disabled == state){
30466             return;
30467         }
30468         
30469         this.disabled = state;
30470         
30471         if (state) {
30472             this.el.addClass('disabled');
30473             return;
30474         }
30475         
30476         this.el.removeClass('disabled');
30477     },
30478     
30479     tooltipEl : function()
30480     {
30481         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30482     }
30483 });
30484  
30485
30486  /*
30487  * - LGPL
30488  *
30489  * FieldLabel
30490  * 
30491  */
30492
30493 /**
30494  * @class Roo.bootstrap.FieldLabel
30495  * @extends Roo.bootstrap.Component
30496  * Bootstrap FieldLabel class
30497  * @cfg {String} html contents of the element
30498  * @cfg {String} tag tag of the element default label
30499  * @cfg {String} cls class of the element
30500  * @cfg {String} target label target 
30501  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30502  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
30503  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
30504  * @cfg {String} iconTooltip default "This field is required"
30505  * @cfg {String} indicatorpos (left|right) default left
30506  * 
30507  * @constructor
30508  * Create a new FieldLabel
30509  * @param {Object} config The config object
30510  */
30511
30512 Roo.bootstrap.FieldLabel = function(config){
30513     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30514     
30515     this.addEvents({
30516             /**
30517              * @event invalid
30518              * Fires after the field has been marked as invalid.
30519              * @param {Roo.form.FieldLabel} this
30520              * @param {String} msg The validation message
30521              */
30522             invalid : true,
30523             /**
30524              * @event valid
30525              * Fires after the field has been validated with no errors.
30526              * @param {Roo.form.FieldLabel} this
30527              */
30528             valid : true
30529         });
30530 };
30531
30532 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30533     
30534     tag: 'label',
30535     cls: '',
30536     html: '',
30537     target: '',
30538     allowBlank : true,
30539     invalidClass : 'has-warning',
30540     validClass : 'has-success',
30541     iconTooltip : 'This field is required',
30542     indicatorpos : 'left',
30543     
30544     getAutoCreate : function(){
30545         
30546         var cls = "";
30547         if (!this.allowBlank) {
30548             cls  = "visible";
30549         }
30550         
30551         var cfg = {
30552             tag : this.tag,
30553             cls : 'roo-bootstrap-field-label ' + this.cls,
30554             for : this.target,
30555             cn : [
30556                 {
30557                     tag : 'i',
30558                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30559                     tooltip : this.iconTooltip
30560                 },
30561                 {
30562                     tag : 'span',
30563                     html : this.html
30564                 }
30565             ] 
30566         };
30567         
30568         if(this.indicatorpos == 'right'){
30569             var cfg = {
30570                 tag : this.tag,
30571                 cls : 'roo-bootstrap-field-label ' + this.cls,
30572                 for : this.target,
30573                 cn : [
30574                     {
30575                         tag : 'span',
30576                         html : this.html
30577                     },
30578                     {
30579                         tag : 'i',
30580                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30581                         tooltip : this.iconTooltip
30582                     }
30583                 ] 
30584             };
30585         }
30586         
30587         return cfg;
30588     },
30589     
30590     initEvents: function() 
30591     {
30592         Roo.bootstrap.Element.superclass.initEvents.call(this);
30593         
30594         this.indicator = this.indicatorEl();
30595         
30596         if(this.indicator){
30597             this.indicator.removeClass('visible');
30598             this.indicator.addClass('invisible');
30599         }
30600         
30601         Roo.bootstrap.FieldLabel.register(this);
30602     },
30603     
30604     indicatorEl : function()
30605     {
30606         var indicator = this.el.select('i.roo-required-indicator',true).first();
30607         
30608         if(!indicator){
30609             return false;
30610         }
30611         
30612         return indicator;
30613         
30614     },
30615     
30616     /**
30617      * Mark this field as valid
30618      */
30619     markValid : function()
30620     {
30621         if(this.indicator){
30622             this.indicator.removeClass('visible');
30623             this.indicator.addClass('invisible');
30624         }
30625         if (Roo.bootstrap.version == 3) {
30626             this.el.removeClass(this.invalidClass);
30627             this.el.addClass(this.validClass);
30628         } else {
30629             this.el.removeClass('is-invalid');
30630             this.el.addClass('is-valid');
30631         }
30632         
30633         
30634         this.fireEvent('valid', this);
30635     },
30636     
30637     /**
30638      * Mark this field as invalid
30639      * @param {String} msg The validation message
30640      */
30641     markInvalid : function(msg)
30642     {
30643         if(this.indicator){
30644             this.indicator.removeClass('invisible');
30645             this.indicator.addClass('visible');
30646         }
30647           if (Roo.bootstrap.version == 3) {
30648             this.el.removeClass(this.validClass);
30649             this.el.addClass(this.invalidClass);
30650         } else {
30651             this.el.removeClass('is-valid');
30652             this.el.addClass('is-invalid');
30653         }
30654         
30655         
30656         this.fireEvent('invalid', this, msg);
30657     }
30658     
30659    
30660 });
30661
30662 Roo.apply(Roo.bootstrap.FieldLabel, {
30663     
30664     groups: {},
30665     
30666      /**
30667     * register a FieldLabel Group
30668     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30669     */
30670     register : function(label)
30671     {
30672         if(this.groups.hasOwnProperty(label.target)){
30673             return;
30674         }
30675      
30676         this.groups[label.target] = label;
30677         
30678     },
30679     /**
30680     * fetch a FieldLabel Group based on the target
30681     * @param {string} target
30682     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30683     */
30684     get: function(target) {
30685         if (typeof(this.groups[target]) == 'undefined') {
30686             return false;
30687         }
30688         
30689         return this.groups[target] ;
30690     }
30691 });
30692
30693  
30694
30695  /*
30696  * - LGPL
30697  *
30698  * page DateSplitField.
30699  * 
30700  */
30701
30702
30703 /**
30704  * @class Roo.bootstrap.DateSplitField
30705  * @extends Roo.bootstrap.Component
30706  * Bootstrap DateSplitField class
30707  * @cfg {string} fieldLabel - the label associated
30708  * @cfg {Number} labelWidth set the width of label (0-12)
30709  * @cfg {String} labelAlign (top|left)
30710  * @cfg {Boolean} dayAllowBlank (true|false) default false
30711  * @cfg {Boolean} monthAllowBlank (true|false) default false
30712  * @cfg {Boolean} yearAllowBlank (true|false) default false
30713  * @cfg {string} dayPlaceholder 
30714  * @cfg {string} monthPlaceholder
30715  * @cfg {string} yearPlaceholder
30716  * @cfg {string} dayFormat default 'd'
30717  * @cfg {string} monthFormat default 'm'
30718  * @cfg {string} yearFormat default 'Y'
30719  * @cfg {Number} labellg set the width of label (1-12)
30720  * @cfg {Number} labelmd set the width of label (1-12)
30721  * @cfg {Number} labelsm set the width of label (1-12)
30722  * @cfg {Number} labelxs set the width of label (1-12)
30723
30724  *     
30725  * @constructor
30726  * Create a new DateSplitField
30727  * @param {Object} config The config object
30728  */
30729
30730 Roo.bootstrap.DateSplitField = function(config){
30731     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30732     
30733     this.addEvents({
30734         // raw events
30735          /**
30736          * @event years
30737          * getting the data of years
30738          * @param {Roo.bootstrap.DateSplitField} this
30739          * @param {Object} years
30740          */
30741         "years" : true,
30742         /**
30743          * @event days
30744          * getting the data of days
30745          * @param {Roo.bootstrap.DateSplitField} this
30746          * @param {Object} days
30747          */
30748         "days" : true,
30749         /**
30750          * @event invalid
30751          * Fires after the field has been marked as invalid.
30752          * @param {Roo.form.Field} this
30753          * @param {String} msg The validation message
30754          */
30755         invalid : true,
30756        /**
30757          * @event valid
30758          * Fires after the field has been validated with no errors.
30759          * @param {Roo.form.Field} this
30760          */
30761         valid : true
30762     });
30763 };
30764
30765 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30766     
30767     fieldLabel : '',
30768     labelAlign : 'top',
30769     labelWidth : 3,
30770     dayAllowBlank : false,
30771     monthAllowBlank : false,
30772     yearAllowBlank : false,
30773     dayPlaceholder : '',
30774     monthPlaceholder : '',
30775     yearPlaceholder : '',
30776     dayFormat : 'd',
30777     monthFormat : 'm',
30778     yearFormat : 'Y',
30779     isFormField : true,
30780     labellg : 0,
30781     labelmd : 0,
30782     labelsm : 0,
30783     labelxs : 0,
30784     
30785     getAutoCreate : function()
30786     {
30787         var cfg = {
30788             tag : 'div',
30789             cls : 'row roo-date-split-field-group',
30790             cn : [
30791                 {
30792                     tag : 'input',
30793                     type : 'hidden',
30794                     cls : 'form-hidden-field roo-date-split-field-group-value',
30795                     name : this.name
30796                 }
30797             ]
30798         };
30799         
30800         var labelCls = 'col-md-12';
30801         var contentCls = 'col-md-4';
30802         
30803         if(this.fieldLabel){
30804             
30805             var label = {
30806                 tag : 'div',
30807                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30808                 cn : [
30809                     {
30810                         tag : 'label',
30811                         html : this.fieldLabel
30812                     }
30813                 ]
30814             };
30815             
30816             if(this.labelAlign == 'left'){
30817             
30818                 if(this.labelWidth > 12){
30819                     label.style = "width: " + this.labelWidth + 'px';
30820                 }
30821
30822                 if(this.labelWidth < 13 && this.labelmd == 0){
30823                     this.labelmd = this.labelWidth;
30824                 }
30825
30826                 if(this.labellg > 0){
30827                     labelCls = ' col-lg-' + this.labellg;
30828                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30829                 }
30830
30831                 if(this.labelmd > 0){
30832                     labelCls = ' col-md-' + this.labelmd;
30833                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30834                 }
30835
30836                 if(this.labelsm > 0){
30837                     labelCls = ' col-sm-' + this.labelsm;
30838                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30839                 }
30840
30841                 if(this.labelxs > 0){
30842                     labelCls = ' col-xs-' + this.labelxs;
30843                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30844                 }
30845             }
30846             
30847             label.cls += ' ' + labelCls;
30848             
30849             cfg.cn.push(label);
30850         }
30851         
30852         Roo.each(['day', 'month', 'year'], function(t){
30853             cfg.cn.push({
30854                 tag : 'div',
30855                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30856             });
30857         }, this);
30858         
30859         return cfg;
30860     },
30861     
30862     inputEl: function ()
30863     {
30864         return this.el.select('.roo-date-split-field-group-value', true).first();
30865     },
30866     
30867     onRender : function(ct, position) 
30868     {
30869         var _this = this;
30870         
30871         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30872         
30873         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30874         
30875         this.dayField = new Roo.bootstrap.ComboBox({
30876             allowBlank : this.dayAllowBlank,
30877             alwaysQuery : true,
30878             displayField : 'value',
30879             editable : false,
30880             fieldLabel : '',
30881             forceSelection : true,
30882             mode : 'local',
30883             placeholder : this.dayPlaceholder,
30884             selectOnFocus : true,
30885             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30886             triggerAction : 'all',
30887             typeAhead : true,
30888             valueField : 'value',
30889             store : new Roo.data.SimpleStore({
30890                 data : (function() {    
30891                     var days = [];
30892                     _this.fireEvent('days', _this, days);
30893                     return days;
30894                 })(),
30895                 fields : [ 'value' ]
30896             }),
30897             listeners : {
30898                 select : function (_self, record, index)
30899                 {
30900                     _this.setValue(_this.getValue());
30901                 }
30902             }
30903         });
30904
30905         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30906         
30907         this.monthField = new Roo.bootstrap.MonthField({
30908             after : '<i class=\"fa fa-calendar\"></i>',
30909             allowBlank : this.monthAllowBlank,
30910             placeholder : this.monthPlaceholder,
30911             readOnly : true,
30912             listeners : {
30913                 render : function (_self)
30914                 {
30915                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30916                         e.preventDefault();
30917                         _self.focus();
30918                     });
30919                 },
30920                 select : function (_self, oldvalue, newvalue)
30921                 {
30922                     _this.setValue(_this.getValue());
30923                 }
30924             }
30925         });
30926         
30927         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30928         
30929         this.yearField = new Roo.bootstrap.ComboBox({
30930             allowBlank : this.yearAllowBlank,
30931             alwaysQuery : true,
30932             displayField : 'value',
30933             editable : false,
30934             fieldLabel : '',
30935             forceSelection : true,
30936             mode : 'local',
30937             placeholder : this.yearPlaceholder,
30938             selectOnFocus : true,
30939             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30940             triggerAction : 'all',
30941             typeAhead : true,
30942             valueField : 'value',
30943             store : new Roo.data.SimpleStore({
30944                 data : (function() {
30945                     var years = [];
30946                     _this.fireEvent('years', _this, years);
30947                     return years;
30948                 })(),
30949                 fields : [ 'value' ]
30950             }),
30951             listeners : {
30952                 select : function (_self, record, index)
30953                 {
30954                     _this.setValue(_this.getValue());
30955                 }
30956             }
30957         });
30958
30959         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30960     },
30961     
30962     setValue : function(v, format)
30963     {
30964         this.inputEl.dom.value = v;
30965         
30966         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30967         
30968         var d = Date.parseDate(v, f);
30969         
30970         if(!d){
30971             this.validate();
30972             return;
30973         }
30974         
30975         this.setDay(d.format(this.dayFormat));
30976         this.setMonth(d.format(this.monthFormat));
30977         this.setYear(d.format(this.yearFormat));
30978         
30979         this.validate();
30980         
30981         return;
30982     },
30983     
30984     setDay : function(v)
30985     {
30986         this.dayField.setValue(v);
30987         this.inputEl.dom.value = this.getValue();
30988         this.validate();
30989         return;
30990     },
30991     
30992     setMonth : function(v)
30993     {
30994         this.monthField.setValue(v, true);
30995         this.inputEl.dom.value = this.getValue();
30996         this.validate();
30997         return;
30998     },
30999     
31000     setYear : function(v)
31001     {
31002         this.yearField.setValue(v);
31003         this.inputEl.dom.value = this.getValue();
31004         this.validate();
31005         return;
31006     },
31007     
31008     getDay : function()
31009     {
31010         return this.dayField.getValue();
31011     },
31012     
31013     getMonth : function()
31014     {
31015         return this.monthField.getValue();
31016     },
31017     
31018     getYear : function()
31019     {
31020         return this.yearField.getValue();
31021     },
31022     
31023     getValue : function()
31024     {
31025         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
31026         
31027         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
31028         
31029         return date;
31030     },
31031     
31032     reset : function()
31033     {
31034         this.setDay('');
31035         this.setMonth('');
31036         this.setYear('');
31037         this.inputEl.dom.value = '';
31038         this.validate();
31039         return;
31040     },
31041     
31042     validate : function()
31043     {
31044         var d = this.dayField.validate();
31045         var m = this.monthField.validate();
31046         var y = this.yearField.validate();
31047         
31048         var valid = true;
31049         
31050         if(
31051                 (!this.dayAllowBlank && !d) ||
31052                 (!this.monthAllowBlank && !m) ||
31053                 (!this.yearAllowBlank && !y)
31054         ){
31055             valid = false;
31056         }
31057         
31058         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
31059             return valid;
31060         }
31061         
31062         if(valid){
31063             this.markValid();
31064             return valid;
31065         }
31066         
31067         this.markInvalid();
31068         
31069         return valid;
31070     },
31071     
31072     markValid : function()
31073     {
31074         
31075         var label = this.el.select('label', true).first();
31076         var icon = this.el.select('i.fa-star', true).first();
31077
31078         if(label && icon){
31079             icon.remove();
31080         }
31081         
31082         this.fireEvent('valid', this);
31083     },
31084     
31085      /**
31086      * Mark this field as invalid
31087      * @param {String} msg The validation message
31088      */
31089     markInvalid : function(msg)
31090     {
31091         
31092         var label = this.el.select('label', true).first();
31093         var icon = this.el.select('i.fa-star', true).first();
31094
31095         if(label && !icon){
31096             this.el.select('.roo-date-split-field-label', true).createChild({
31097                 tag : 'i',
31098                 cls : 'text-danger fa fa-lg fa-star',
31099                 tooltip : 'This field is required',
31100                 style : 'margin-right:5px;'
31101             }, label, true);
31102         }
31103         
31104         this.fireEvent('invalid', this, msg);
31105     },
31106     
31107     clearInvalid : function()
31108     {
31109         var label = this.el.select('label', true).first();
31110         var icon = this.el.select('i.fa-star', true).first();
31111
31112         if(label && icon){
31113             icon.remove();
31114         }
31115         
31116         this.fireEvent('valid', this);
31117     },
31118     
31119     getName: function()
31120     {
31121         return this.name;
31122     }
31123     
31124 });
31125
31126  /**
31127  *
31128  * This is based on 
31129  * http://masonry.desandro.com
31130  *
31131  * The idea is to render all the bricks based on vertical width...
31132  *
31133  * The original code extends 'outlayer' - we might need to use that....
31134  * 
31135  */
31136
31137
31138 /**
31139  * @class Roo.bootstrap.LayoutMasonry
31140  * @extends Roo.bootstrap.Component
31141  * Bootstrap Layout Masonry class
31142  * 
31143  * @constructor
31144  * Create a new Element
31145  * @param {Object} config The config object
31146  */
31147
31148 Roo.bootstrap.LayoutMasonry = function(config){
31149     
31150     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
31151     
31152     this.bricks = [];
31153     
31154     Roo.bootstrap.LayoutMasonry.register(this);
31155     
31156     this.addEvents({
31157         // raw events
31158         /**
31159          * @event layout
31160          * Fire after layout the items
31161          * @param {Roo.bootstrap.LayoutMasonry} this
31162          * @param {Roo.EventObject} e
31163          */
31164         "layout" : true
31165     });
31166     
31167 };
31168
31169 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
31170     
31171     /**
31172      * @cfg {Boolean} isLayoutInstant = no animation?
31173      */   
31174     isLayoutInstant : false, // needed?
31175    
31176     /**
31177      * @cfg {Number} boxWidth  width of the columns
31178      */   
31179     boxWidth : 450,
31180     
31181       /**
31182      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
31183      */   
31184     boxHeight : 0,
31185     
31186     /**
31187      * @cfg {Number} padWidth padding below box..
31188      */   
31189     padWidth : 10, 
31190     
31191     /**
31192      * @cfg {Number} gutter gutter width..
31193      */   
31194     gutter : 10,
31195     
31196      /**
31197      * @cfg {Number} maxCols maximum number of columns
31198      */   
31199     
31200     maxCols: 0,
31201     
31202     /**
31203      * @cfg {Boolean} isAutoInitial defalut true
31204      */   
31205     isAutoInitial : true, 
31206     
31207     containerWidth: 0,
31208     
31209     /**
31210      * @cfg {Boolean} isHorizontal defalut false
31211      */   
31212     isHorizontal : false, 
31213
31214     currentSize : null,
31215     
31216     tag: 'div',
31217     
31218     cls: '',
31219     
31220     bricks: null, //CompositeElement
31221     
31222     cols : 1,
31223     
31224     _isLayoutInited : false,
31225     
31226 //    isAlternative : false, // only use for vertical layout...
31227     
31228     /**
31229      * @cfg {Number} alternativePadWidth padding below box..
31230      */   
31231     alternativePadWidth : 50,
31232     
31233     selectedBrick : [],
31234     
31235     getAutoCreate : function(){
31236         
31237         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31238         
31239         var cfg = {
31240             tag: this.tag,
31241             cls: 'blog-masonary-wrapper ' + this.cls,
31242             cn : {
31243                 cls : 'mas-boxes masonary'
31244             }
31245         };
31246         
31247         return cfg;
31248     },
31249     
31250     getChildContainer: function( )
31251     {
31252         if (this.boxesEl) {
31253             return this.boxesEl;
31254         }
31255         
31256         this.boxesEl = this.el.select('.mas-boxes').first();
31257         
31258         return this.boxesEl;
31259     },
31260     
31261     
31262     initEvents : function()
31263     {
31264         var _this = this;
31265         
31266         if(this.isAutoInitial){
31267             Roo.log('hook children rendered');
31268             this.on('childrenrendered', function() {
31269                 Roo.log('children rendered');
31270                 _this.initial();
31271             } ,this);
31272         }
31273     },
31274     
31275     initial : function()
31276     {
31277         this.selectedBrick = [];
31278         
31279         this.currentSize = this.el.getBox(true);
31280         
31281         Roo.EventManager.onWindowResize(this.resize, this); 
31282
31283         if(!this.isAutoInitial){
31284             this.layout();
31285             return;
31286         }
31287         
31288         this.layout();
31289         
31290         return;
31291         //this.layout.defer(500,this);
31292         
31293     },
31294     
31295     resize : function()
31296     {
31297         var cs = this.el.getBox(true);
31298         
31299         if (
31300                 this.currentSize.width == cs.width && 
31301                 this.currentSize.x == cs.x && 
31302                 this.currentSize.height == cs.height && 
31303                 this.currentSize.y == cs.y 
31304         ) {
31305             Roo.log("no change in with or X or Y");
31306             return;
31307         }
31308         
31309         this.currentSize = cs;
31310         
31311         this.layout();
31312         
31313     },
31314     
31315     layout : function()
31316     {   
31317         this._resetLayout();
31318         
31319         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31320         
31321         this.layoutItems( isInstant );
31322       
31323         this._isLayoutInited = true;
31324         
31325         this.fireEvent('layout', this);
31326         
31327     },
31328     
31329     _resetLayout : function()
31330     {
31331         if(this.isHorizontal){
31332             this.horizontalMeasureColumns();
31333             return;
31334         }
31335         
31336         this.verticalMeasureColumns();
31337         
31338     },
31339     
31340     verticalMeasureColumns : function()
31341     {
31342         this.getContainerWidth();
31343         
31344 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31345 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31346 //            return;
31347 //        }
31348         
31349         var boxWidth = this.boxWidth + this.padWidth;
31350         
31351         if(this.containerWidth < this.boxWidth){
31352             boxWidth = this.containerWidth
31353         }
31354         
31355         var containerWidth = this.containerWidth;
31356         
31357         var cols = Math.floor(containerWidth / boxWidth);
31358         
31359         this.cols = Math.max( cols, 1 );
31360         
31361         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31362         
31363         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31364         
31365         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31366         
31367         this.colWidth = boxWidth + avail - this.padWidth;
31368         
31369         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31370         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31371     },
31372     
31373     horizontalMeasureColumns : function()
31374     {
31375         this.getContainerWidth();
31376         
31377         var boxWidth = this.boxWidth;
31378         
31379         if(this.containerWidth < boxWidth){
31380             boxWidth = this.containerWidth;
31381         }
31382         
31383         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31384         
31385         this.el.setHeight(boxWidth);
31386         
31387     },
31388     
31389     getContainerWidth : function()
31390     {
31391         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31392     },
31393     
31394     layoutItems : function( isInstant )
31395     {
31396         Roo.log(this.bricks);
31397         
31398         var items = Roo.apply([], this.bricks);
31399         
31400         if(this.isHorizontal){
31401             this._horizontalLayoutItems( items , isInstant );
31402             return;
31403         }
31404         
31405 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31406 //            this._verticalAlternativeLayoutItems( items , isInstant );
31407 //            return;
31408 //        }
31409         
31410         this._verticalLayoutItems( items , isInstant );
31411         
31412     },
31413     
31414     _verticalLayoutItems : function ( items , isInstant)
31415     {
31416         if ( !items || !items.length ) {
31417             return;
31418         }
31419         
31420         var standard = [
31421             ['xs', 'xs', 'xs', 'tall'],
31422             ['xs', 'xs', 'tall'],
31423             ['xs', 'xs', 'sm'],
31424             ['xs', 'xs', 'xs'],
31425             ['xs', 'tall'],
31426             ['xs', 'sm'],
31427             ['xs', 'xs'],
31428             ['xs'],
31429             
31430             ['sm', 'xs', 'xs'],
31431             ['sm', 'xs'],
31432             ['sm'],
31433             
31434             ['tall', 'xs', 'xs', 'xs'],
31435             ['tall', 'xs', 'xs'],
31436             ['tall', 'xs'],
31437             ['tall']
31438             
31439         ];
31440         
31441         var queue = [];
31442         
31443         var boxes = [];
31444         
31445         var box = [];
31446         
31447         Roo.each(items, function(item, k){
31448             
31449             switch (item.size) {
31450                 // these layouts take up a full box,
31451                 case 'md' :
31452                 case 'md-left' :
31453                 case 'md-right' :
31454                 case 'wide' :
31455                     
31456                     if(box.length){
31457                         boxes.push(box);
31458                         box = [];
31459                     }
31460                     
31461                     boxes.push([item]);
31462                     
31463                     break;
31464                     
31465                 case 'xs' :
31466                 case 'sm' :
31467                 case 'tall' :
31468                     
31469                     box.push(item);
31470                     
31471                     break;
31472                 default :
31473                     break;
31474                     
31475             }
31476             
31477         }, this);
31478         
31479         if(box.length){
31480             boxes.push(box);
31481             box = [];
31482         }
31483         
31484         var filterPattern = function(box, length)
31485         {
31486             if(!box.length){
31487                 return;
31488             }
31489             
31490             var match = false;
31491             
31492             var pattern = box.slice(0, length);
31493             
31494             var format = [];
31495             
31496             Roo.each(pattern, function(i){
31497                 format.push(i.size);
31498             }, this);
31499             
31500             Roo.each(standard, function(s){
31501                 
31502                 if(String(s) != String(format)){
31503                     return;
31504                 }
31505                 
31506                 match = true;
31507                 return false;
31508                 
31509             }, this);
31510             
31511             if(!match && length == 1){
31512                 return;
31513             }
31514             
31515             if(!match){
31516                 filterPattern(box, length - 1);
31517                 return;
31518             }
31519                 
31520             queue.push(pattern);
31521
31522             box = box.slice(length, box.length);
31523
31524             filterPattern(box, 4);
31525
31526             return;
31527             
31528         }
31529         
31530         Roo.each(boxes, function(box, k){
31531             
31532             if(!box.length){
31533                 return;
31534             }
31535             
31536             if(box.length == 1){
31537                 queue.push(box);
31538                 return;
31539             }
31540             
31541             filterPattern(box, 4);
31542             
31543         }, this);
31544         
31545         this._processVerticalLayoutQueue( queue, isInstant );
31546         
31547     },
31548     
31549 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31550 //    {
31551 //        if ( !items || !items.length ) {
31552 //            return;
31553 //        }
31554 //
31555 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31556 //        
31557 //    },
31558     
31559     _horizontalLayoutItems : function ( items , isInstant)
31560     {
31561         if ( !items || !items.length || items.length < 3) {
31562             return;
31563         }
31564         
31565         items.reverse();
31566         
31567         var eItems = items.slice(0, 3);
31568         
31569         items = items.slice(3, items.length);
31570         
31571         var standard = [
31572             ['xs', 'xs', 'xs', 'wide'],
31573             ['xs', 'xs', 'wide'],
31574             ['xs', 'xs', 'sm'],
31575             ['xs', 'xs', 'xs'],
31576             ['xs', 'wide'],
31577             ['xs', 'sm'],
31578             ['xs', 'xs'],
31579             ['xs'],
31580             
31581             ['sm', 'xs', 'xs'],
31582             ['sm', 'xs'],
31583             ['sm'],
31584             
31585             ['wide', 'xs', 'xs', 'xs'],
31586             ['wide', 'xs', 'xs'],
31587             ['wide', 'xs'],
31588             ['wide'],
31589             
31590             ['wide-thin']
31591         ];
31592         
31593         var queue = [];
31594         
31595         var boxes = [];
31596         
31597         var box = [];
31598         
31599         Roo.each(items, function(item, k){
31600             
31601             switch (item.size) {
31602                 case 'md' :
31603                 case 'md-left' :
31604                 case 'md-right' :
31605                 case 'tall' :
31606                     
31607                     if(box.length){
31608                         boxes.push(box);
31609                         box = [];
31610                     }
31611                     
31612                     boxes.push([item]);
31613                     
31614                     break;
31615                     
31616                 case 'xs' :
31617                 case 'sm' :
31618                 case 'wide' :
31619                 case 'wide-thin' :
31620                     
31621                     box.push(item);
31622                     
31623                     break;
31624                 default :
31625                     break;
31626                     
31627             }
31628             
31629         }, this);
31630         
31631         if(box.length){
31632             boxes.push(box);
31633             box = [];
31634         }
31635         
31636         var filterPattern = function(box, length)
31637         {
31638             if(!box.length){
31639                 return;
31640             }
31641             
31642             var match = false;
31643             
31644             var pattern = box.slice(0, length);
31645             
31646             var format = [];
31647             
31648             Roo.each(pattern, function(i){
31649                 format.push(i.size);
31650             }, this);
31651             
31652             Roo.each(standard, function(s){
31653                 
31654                 if(String(s) != String(format)){
31655                     return;
31656                 }
31657                 
31658                 match = true;
31659                 return false;
31660                 
31661             }, this);
31662             
31663             if(!match && length == 1){
31664                 return;
31665             }
31666             
31667             if(!match){
31668                 filterPattern(box, length - 1);
31669                 return;
31670             }
31671                 
31672             queue.push(pattern);
31673
31674             box = box.slice(length, box.length);
31675
31676             filterPattern(box, 4);
31677
31678             return;
31679             
31680         }
31681         
31682         Roo.each(boxes, function(box, k){
31683             
31684             if(!box.length){
31685                 return;
31686             }
31687             
31688             if(box.length == 1){
31689                 queue.push(box);
31690                 return;
31691             }
31692             
31693             filterPattern(box, 4);
31694             
31695         }, this);
31696         
31697         
31698         var prune = [];
31699         
31700         var pos = this.el.getBox(true);
31701         
31702         var minX = pos.x;
31703         
31704         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31705         
31706         var hit_end = false;
31707         
31708         Roo.each(queue, function(box){
31709             
31710             if(hit_end){
31711                 
31712                 Roo.each(box, function(b){
31713                 
31714                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31715                     b.el.hide();
31716
31717                 }, this);
31718
31719                 return;
31720             }
31721             
31722             var mx = 0;
31723             
31724             Roo.each(box, function(b){
31725                 
31726                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31727                 b.el.show();
31728
31729                 mx = Math.max(mx, b.x);
31730                 
31731             }, this);
31732             
31733             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31734             
31735             if(maxX < minX){
31736                 
31737                 Roo.each(box, function(b){
31738                 
31739                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31740                     b.el.hide();
31741                     
31742                 }, this);
31743                 
31744                 hit_end = true;
31745                 
31746                 return;
31747             }
31748             
31749             prune.push(box);
31750             
31751         }, this);
31752         
31753         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31754     },
31755     
31756     /** Sets position of item in DOM
31757     * @param {Element} item
31758     * @param {Number} x - horizontal position
31759     * @param {Number} y - vertical position
31760     * @param {Boolean} isInstant - disables transitions
31761     */
31762     _processVerticalLayoutQueue : function( queue, isInstant )
31763     {
31764         var pos = this.el.getBox(true);
31765         var x = pos.x;
31766         var y = pos.y;
31767         var maxY = [];
31768         
31769         for (var i = 0; i < this.cols; i++){
31770             maxY[i] = pos.y;
31771         }
31772         
31773         Roo.each(queue, function(box, k){
31774             
31775             var col = k % this.cols;
31776             
31777             Roo.each(box, function(b,kk){
31778                 
31779                 b.el.position('absolute');
31780                 
31781                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31782                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31783                 
31784                 if(b.size == 'md-left' || b.size == 'md-right'){
31785                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31786                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31787                 }
31788                 
31789                 b.el.setWidth(width);
31790                 b.el.setHeight(height);
31791                 // iframe?
31792                 b.el.select('iframe',true).setSize(width,height);
31793                 
31794             }, this);
31795             
31796             for (var i = 0; i < this.cols; i++){
31797                 
31798                 if(maxY[i] < maxY[col]){
31799                     col = i;
31800                     continue;
31801                 }
31802                 
31803                 col = Math.min(col, i);
31804                 
31805             }
31806             
31807             x = pos.x + col * (this.colWidth + this.padWidth);
31808             
31809             y = maxY[col];
31810             
31811             var positions = [];
31812             
31813             switch (box.length){
31814                 case 1 :
31815                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31816                     break;
31817                 case 2 :
31818                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31819                     break;
31820                 case 3 :
31821                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31822                     break;
31823                 case 4 :
31824                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31825                     break;
31826                 default :
31827                     break;
31828             }
31829             
31830             Roo.each(box, function(b,kk){
31831                 
31832                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31833                 
31834                 var sz = b.el.getSize();
31835                 
31836                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31837                 
31838             }, this);
31839             
31840         }, this);
31841         
31842         var mY = 0;
31843         
31844         for (var i = 0; i < this.cols; i++){
31845             mY = Math.max(mY, maxY[i]);
31846         }
31847         
31848         this.el.setHeight(mY - pos.y);
31849         
31850     },
31851     
31852 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31853 //    {
31854 //        var pos = this.el.getBox(true);
31855 //        var x = pos.x;
31856 //        var y = pos.y;
31857 //        var maxX = pos.right;
31858 //        
31859 //        var maxHeight = 0;
31860 //        
31861 //        Roo.each(items, function(item, k){
31862 //            
31863 //            var c = k % 2;
31864 //            
31865 //            item.el.position('absolute');
31866 //                
31867 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31868 //
31869 //            item.el.setWidth(width);
31870 //
31871 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31872 //
31873 //            item.el.setHeight(height);
31874 //            
31875 //            if(c == 0){
31876 //                item.el.setXY([x, y], isInstant ? false : true);
31877 //            } else {
31878 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31879 //            }
31880 //            
31881 //            y = y + height + this.alternativePadWidth;
31882 //            
31883 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31884 //            
31885 //        }, this);
31886 //        
31887 //        this.el.setHeight(maxHeight);
31888 //        
31889 //    },
31890     
31891     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31892     {
31893         var pos = this.el.getBox(true);
31894         
31895         var minX = pos.x;
31896         var minY = pos.y;
31897         
31898         var maxX = pos.right;
31899         
31900         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31901         
31902         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31903         
31904         Roo.each(queue, function(box, k){
31905             
31906             Roo.each(box, function(b, kk){
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                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31912                 
31913                 if(b.size == 'md-left' || b.size == 'md-right'){
31914                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31915                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31916                 }
31917                 
31918                 b.el.setWidth(width);
31919                 b.el.setHeight(height);
31920                 
31921             }, this);
31922             
31923             if(!box.length){
31924                 return;
31925             }
31926             
31927             var positions = [];
31928             
31929             switch (box.length){
31930                 case 1 :
31931                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31932                     break;
31933                 case 2 :
31934                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31935                     break;
31936                 case 3 :
31937                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31938                     break;
31939                 case 4 :
31940                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31941                     break;
31942                 default :
31943                     break;
31944             }
31945             
31946             Roo.each(box, function(b,kk){
31947                 
31948                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31949                 
31950                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31951                 
31952             }, this);
31953             
31954         }, this);
31955         
31956     },
31957     
31958     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31959     {
31960         Roo.each(eItems, function(b,k){
31961             
31962             b.size = (k == 0) ? 'sm' : 'xs';
31963             b.x = (k == 0) ? 2 : 1;
31964             b.y = (k == 0) ? 2 : 1;
31965             
31966             b.el.position('absolute');
31967             
31968             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31969                 
31970             b.el.setWidth(width);
31971             
31972             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31973             
31974             b.el.setHeight(height);
31975             
31976         }, this);
31977
31978         var positions = [];
31979         
31980         positions.push({
31981             x : maxX - this.unitWidth * 2 - this.gutter,
31982             y : minY
31983         });
31984         
31985         positions.push({
31986             x : maxX - this.unitWidth,
31987             y : minY + (this.unitWidth + this.gutter) * 2
31988         });
31989         
31990         positions.push({
31991             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31992             y : minY
31993         });
31994         
31995         Roo.each(eItems, function(b,k){
31996             
31997             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31998
31999         }, this);
32000         
32001     },
32002     
32003     getVerticalOneBoxColPositions : function(x, y, box)
32004     {
32005         var pos = [];
32006         
32007         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
32008         
32009         if(box[0].size == 'md-left'){
32010             rand = 0;
32011         }
32012         
32013         if(box[0].size == 'md-right'){
32014             rand = 1;
32015         }
32016         
32017         pos.push({
32018             x : x + (this.unitWidth + this.gutter) * rand,
32019             y : y
32020         });
32021         
32022         return pos;
32023     },
32024     
32025     getVerticalTwoBoxColPositions : function(x, y, box)
32026     {
32027         var pos = [];
32028         
32029         if(box[0].size == 'xs'){
32030             
32031             pos.push({
32032                 x : x,
32033                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
32034             });
32035
32036             pos.push({
32037                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
32038                 y : y
32039             });
32040             
32041             return pos;
32042             
32043         }
32044         
32045         pos.push({
32046             x : x,
32047             y : y
32048         });
32049
32050         pos.push({
32051             x : x + (this.unitWidth + this.gutter) * 2,
32052             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
32053         });
32054         
32055         return pos;
32056         
32057     },
32058     
32059     getVerticalThreeBoxColPositions : function(x, y, box)
32060     {
32061         var pos = [];
32062         
32063         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32064             
32065             pos.push({
32066                 x : x,
32067                 y : y
32068             });
32069
32070             pos.push({
32071                 x : x + (this.unitWidth + this.gutter) * 1,
32072                 y : y
32073             });
32074             
32075             pos.push({
32076                 x : x + (this.unitWidth + this.gutter) * 2,
32077                 y : y
32078             });
32079             
32080             return pos;
32081             
32082         }
32083         
32084         if(box[0].size == 'xs' && box[1].size == 'xs'){
32085             
32086             pos.push({
32087                 x : x,
32088                 y : y
32089             });
32090
32091             pos.push({
32092                 x : x,
32093                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
32094             });
32095             
32096             pos.push({
32097                 x : x + (this.unitWidth + this.gutter) * 1,
32098                 y : y
32099             });
32100             
32101             return pos;
32102             
32103         }
32104         
32105         pos.push({
32106             x : x,
32107             y : y
32108         });
32109
32110         pos.push({
32111             x : x + (this.unitWidth + this.gutter) * 2,
32112             y : y
32113         });
32114
32115         pos.push({
32116             x : x + (this.unitWidth + this.gutter) * 2,
32117             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
32118         });
32119             
32120         return pos;
32121         
32122     },
32123     
32124     getVerticalFourBoxColPositions : function(x, y, box)
32125     {
32126         var pos = [];
32127         
32128         if(box[0].size == 'xs'){
32129             
32130             pos.push({
32131                 x : x,
32132                 y : y
32133             });
32134
32135             pos.push({
32136                 x : x,
32137                 y : y + (this.unitHeight + this.gutter) * 1
32138             });
32139             
32140             pos.push({
32141                 x : x,
32142                 y : y + (this.unitHeight + this.gutter) * 2
32143             });
32144             
32145             pos.push({
32146                 x : x + (this.unitWidth + this.gutter) * 1,
32147                 y : y
32148             });
32149             
32150             return pos;
32151             
32152         }
32153         
32154         pos.push({
32155             x : x,
32156             y : y
32157         });
32158
32159         pos.push({
32160             x : x + (this.unitWidth + this.gutter) * 2,
32161             y : y
32162         });
32163
32164         pos.push({
32165             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
32166             y : y + (this.unitHeight + this.gutter) * 1
32167         });
32168
32169         pos.push({
32170             x : x + (this.unitWidth + this.gutter) * 2,
32171             y : y + (this.unitWidth + this.gutter) * 2
32172         });
32173
32174         return pos;
32175         
32176     },
32177     
32178     getHorizontalOneBoxColPositions : function(maxX, minY, box)
32179     {
32180         var pos = [];
32181         
32182         if(box[0].size == 'md-left'){
32183             pos.push({
32184                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32185                 y : minY
32186             });
32187             
32188             return pos;
32189         }
32190         
32191         if(box[0].size == 'md-right'){
32192             pos.push({
32193                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32194                 y : minY + (this.unitWidth + this.gutter) * 1
32195             });
32196             
32197             return pos;
32198         }
32199         
32200         var rand = Math.floor(Math.random() * (4 - box[0].y));
32201         
32202         pos.push({
32203             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32204             y : minY + (this.unitWidth + this.gutter) * rand
32205         });
32206         
32207         return pos;
32208         
32209     },
32210     
32211     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
32212     {
32213         var pos = [];
32214         
32215         if(box[0].size == 'xs'){
32216             
32217             pos.push({
32218                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32219                 y : minY
32220             });
32221
32222             pos.push({
32223                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32224                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32225             });
32226             
32227             return pos;
32228             
32229         }
32230         
32231         pos.push({
32232             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32233             y : minY
32234         });
32235
32236         pos.push({
32237             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32238             y : minY + (this.unitWidth + this.gutter) * 2
32239         });
32240         
32241         return pos;
32242         
32243     },
32244     
32245     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32246     {
32247         var pos = [];
32248         
32249         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32250             
32251             pos.push({
32252                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32253                 y : minY
32254             });
32255
32256             pos.push({
32257                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32258                 y : minY + (this.unitWidth + this.gutter) * 1
32259             });
32260             
32261             pos.push({
32262                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32263                 y : minY + (this.unitWidth + this.gutter) * 2
32264             });
32265             
32266             return pos;
32267             
32268         }
32269         
32270         if(box[0].size == 'xs' && box[1].size == 'xs'){
32271             
32272             pos.push({
32273                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32274                 y : minY
32275             });
32276
32277             pos.push({
32278                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32279                 y : minY
32280             });
32281             
32282             pos.push({
32283                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32284                 y : minY + (this.unitWidth + this.gutter) * 1
32285             });
32286             
32287             return pos;
32288             
32289         }
32290         
32291         pos.push({
32292             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32293             y : minY
32294         });
32295
32296         pos.push({
32297             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32298             y : minY + (this.unitWidth + this.gutter) * 2
32299         });
32300
32301         pos.push({
32302             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32303             y : minY + (this.unitWidth + this.gutter) * 2
32304         });
32305             
32306         return pos;
32307         
32308     },
32309     
32310     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32311     {
32312         var pos = [];
32313         
32314         if(box[0].size == 'xs'){
32315             
32316             pos.push({
32317                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32318                 y : minY
32319             });
32320
32321             pos.push({
32322                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32323                 y : minY
32324             });
32325             
32326             pos.push({
32327                 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),
32328                 y : minY
32329             });
32330             
32331             pos.push({
32332                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32333                 y : minY + (this.unitWidth + this.gutter) * 1
32334             });
32335             
32336             return pos;
32337             
32338         }
32339         
32340         pos.push({
32341             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32342             y : minY
32343         });
32344         
32345         pos.push({
32346             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32347             y : minY + (this.unitWidth + this.gutter) * 2
32348         });
32349         
32350         pos.push({
32351             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32352             y : minY + (this.unitWidth + this.gutter) * 2
32353         });
32354         
32355         pos.push({
32356             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),
32357             y : minY + (this.unitWidth + this.gutter) * 2
32358         });
32359
32360         return pos;
32361         
32362     },
32363     
32364     /**
32365     * remove a Masonry Brick
32366     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32367     */
32368     removeBrick : function(brick_id)
32369     {
32370         if (!brick_id) {
32371             return;
32372         }
32373         
32374         for (var i = 0; i<this.bricks.length; i++) {
32375             if (this.bricks[i].id == brick_id) {
32376                 this.bricks.splice(i,1);
32377                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32378                 this.initial();
32379             }
32380         }
32381     },
32382     
32383     /**
32384     * adds a Masonry Brick
32385     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32386     */
32387     addBrick : function(cfg)
32388     {
32389         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32390         //this.register(cn);
32391         cn.parentId = this.id;
32392         cn.render(this.el);
32393         return cn;
32394     },
32395     
32396     /**
32397     * register a Masonry Brick
32398     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32399     */
32400     
32401     register : function(brick)
32402     {
32403         this.bricks.push(brick);
32404         brick.masonryId = this.id;
32405     },
32406     
32407     /**
32408     * clear all the Masonry Brick
32409     */
32410     clearAll : function()
32411     {
32412         this.bricks = [];
32413         //this.getChildContainer().dom.innerHTML = "";
32414         this.el.dom.innerHTML = '';
32415     },
32416     
32417     getSelected : function()
32418     {
32419         if (!this.selectedBrick) {
32420             return false;
32421         }
32422         
32423         return this.selectedBrick;
32424     }
32425 });
32426
32427 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32428     
32429     groups: {},
32430      /**
32431     * register a Masonry Layout
32432     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32433     */
32434     
32435     register : function(layout)
32436     {
32437         this.groups[layout.id] = layout;
32438     },
32439     /**
32440     * fetch a  Masonry Layout based on the masonry layout ID
32441     * @param {string} the masonry layout to add
32442     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32443     */
32444     
32445     get: function(layout_id) {
32446         if (typeof(this.groups[layout_id]) == 'undefined') {
32447             return false;
32448         }
32449         return this.groups[layout_id] ;
32450     }
32451     
32452     
32453     
32454 });
32455
32456  
32457
32458  /**
32459  *
32460  * This is based on 
32461  * http://masonry.desandro.com
32462  *
32463  * The idea is to render all the bricks based on vertical width...
32464  *
32465  * The original code extends 'outlayer' - we might need to use that....
32466  * 
32467  */
32468
32469
32470 /**
32471  * @class Roo.bootstrap.LayoutMasonryAuto
32472  * @extends Roo.bootstrap.Component
32473  * Bootstrap Layout Masonry class
32474  * 
32475  * @constructor
32476  * Create a new Element
32477  * @param {Object} config The config object
32478  */
32479
32480 Roo.bootstrap.LayoutMasonryAuto = function(config){
32481     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32482 };
32483
32484 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32485     
32486       /**
32487      * @cfg {Boolean} isFitWidth  - resize the width..
32488      */   
32489     isFitWidth : false,  // options..
32490     /**
32491      * @cfg {Boolean} isOriginLeft = left align?
32492      */   
32493     isOriginLeft : true,
32494     /**
32495      * @cfg {Boolean} isOriginTop = top align?
32496      */   
32497     isOriginTop : false,
32498     /**
32499      * @cfg {Boolean} isLayoutInstant = no animation?
32500      */   
32501     isLayoutInstant : false, // needed?
32502     /**
32503      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32504      */   
32505     isResizingContainer : true,
32506     /**
32507      * @cfg {Number} columnWidth  width of the columns 
32508      */   
32509     
32510     columnWidth : 0,
32511     
32512     /**
32513      * @cfg {Number} maxCols maximum number of columns
32514      */   
32515     
32516     maxCols: 0,
32517     /**
32518      * @cfg {Number} padHeight padding below box..
32519      */   
32520     
32521     padHeight : 10, 
32522     
32523     /**
32524      * @cfg {Boolean} isAutoInitial defalut true
32525      */   
32526     
32527     isAutoInitial : true, 
32528     
32529     // private?
32530     gutter : 0,
32531     
32532     containerWidth: 0,
32533     initialColumnWidth : 0,
32534     currentSize : null,
32535     
32536     colYs : null, // array.
32537     maxY : 0,
32538     padWidth: 10,
32539     
32540     
32541     tag: 'div',
32542     cls: '',
32543     bricks: null, //CompositeElement
32544     cols : 0, // array?
32545     // element : null, // wrapped now this.el
32546     _isLayoutInited : null, 
32547     
32548     
32549     getAutoCreate : function(){
32550         
32551         var cfg = {
32552             tag: this.tag,
32553             cls: 'blog-masonary-wrapper ' + this.cls,
32554             cn : {
32555                 cls : 'mas-boxes masonary'
32556             }
32557         };
32558         
32559         return cfg;
32560     },
32561     
32562     getChildContainer: function( )
32563     {
32564         if (this.boxesEl) {
32565             return this.boxesEl;
32566         }
32567         
32568         this.boxesEl = this.el.select('.mas-boxes').first();
32569         
32570         return this.boxesEl;
32571     },
32572     
32573     
32574     initEvents : function()
32575     {
32576         var _this = this;
32577         
32578         if(this.isAutoInitial){
32579             Roo.log('hook children rendered');
32580             this.on('childrenrendered', function() {
32581                 Roo.log('children rendered');
32582                 _this.initial();
32583             } ,this);
32584         }
32585         
32586     },
32587     
32588     initial : function()
32589     {
32590         this.reloadItems();
32591
32592         this.currentSize = this.el.getBox(true);
32593
32594         /// was window resize... - let's see if this works..
32595         Roo.EventManager.onWindowResize(this.resize, this); 
32596
32597         if(!this.isAutoInitial){
32598             this.layout();
32599             return;
32600         }
32601         
32602         this.layout.defer(500,this);
32603     },
32604     
32605     reloadItems: function()
32606     {
32607         this.bricks = this.el.select('.masonry-brick', true);
32608         
32609         this.bricks.each(function(b) {
32610             //Roo.log(b.getSize());
32611             if (!b.attr('originalwidth')) {
32612                 b.attr('originalwidth',  b.getSize().width);
32613             }
32614             
32615         });
32616         
32617         Roo.log(this.bricks.elements.length);
32618     },
32619     
32620     resize : function()
32621     {
32622         Roo.log('resize');
32623         var cs = this.el.getBox(true);
32624         
32625         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32626             Roo.log("no change in with or X");
32627             return;
32628         }
32629         this.currentSize = cs;
32630         this.layout();
32631     },
32632     
32633     layout : function()
32634     {
32635          Roo.log('layout');
32636         this._resetLayout();
32637         //this._manageStamps();
32638       
32639         // don't animate first layout
32640         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32641         this.layoutItems( isInstant );
32642       
32643         // flag for initalized
32644         this._isLayoutInited = true;
32645     },
32646     
32647     layoutItems : function( isInstant )
32648     {
32649         //var items = this._getItemsForLayout( this.items );
32650         // original code supports filtering layout items.. we just ignore it..
32651         
32652         this._layoutItems( this.bricks , isInstant );
32653       
32654         this._postLayout();
32655     },
32656     _layoutItems : function ( items , isInstant)
32657     {
32658        //this.fireEvent( 'layout', this, items );
32659     
32660
32661         if ( !items || !items.elements.length ) {
32662           // no items, emit event with empty array
32663             return;
32664         }
32665
32666         var queue = [];
32667         items.each(function(item) {
32668             Roo.log("layout item");
32669             Roo.log(item);
32670             // get x/y object from method
32671             var position = this._getItemLayoutPosition( item );
32672             // enqueue
32673             position.item = item;
32674             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32675             queue.push( position );
32676         }, this);
32677       
32678         this._processLayoutQueue( queue );
32679     },
32680     /** Sets position of item in DOM
32681     * @param {Element} item
32682     * @param {Number} x - horizontal position
32683     * @param {Number} y - vertical position
32684     * @param {Boolean} isInstant - disables transitions
32685     */
32686     _processLayoutQueue : function( queue )
32687     {
32688         for ( var i=0, len = queue.length; i < len; i++ ) {
32689             var obj = queue[i];
32690             obj.item.position('absolute');
32691             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32692         }
32693     },
32694       
32695     
32696     /**
32697     * Any logic you want to do after each layout,
32698     * i.e. size the container
32699     */
32700     _postLayout : function()
32701     {
32702         this.resizeContainer();
32703     },
32704     
32705     resizeContainer : function()
32706     {
32707         if ( !this.isResizingContainer ) {
32708             return;
32709         }
32710         var size = this._getContainerSize();
32711         if ( size ) {
32712             this.el.setSize(size.width,size.height);
32713             this.boxesEl.setSize(size.width,size.height);
32714         }
32715     },
32716     
32717     
32718     
32719     _resetLayout : function()
32720     {
32721         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32722         this.colWidth = this.el.getWidth();
32723         //this.gutter = this.el.getWidth(); 
32724         
32725         this.measureColumns();
32726
32727         // reset column Y
32728         var i = this.cols;
32729         this.colYs = [];
32730         while (i--) {
32731             this.colYs.push( 0 );
32732         }
32733     
32734         this.maxY = 0;
32735     },
32736
32737     measureColumns : function()
32738     {
32739         this.getContainerWidth();
32740       // if columnWidth is 0, default to outerWidth of first item
32741         if ( !this.columnWidth ) {
32742             var firstItem = this.bricks.first();
32743             Roo.log(firstItem);
32744             this.columnWidth  = this.containerWidth;
32745             if (firstItem && firstItem.attr('originalwidth') ) {
32746                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32747             }
32748             // columnWidth fall back to item of first element
32749             Roo.log("set column width?");
32750                         this.initialColumnWidth = this.columnWidth  ;
32751
32752             // if first elem has no width, default to size of container
32753             
32754         }
32755         
32756         
32757         if (this.initialColumnWidth) {
32758             this.columnWidth = this.initialColumnWidth;
32759         }
32760         
32761         
32762             
32763         // column width is fixed at the top - however if container width get's smaller we should
32764         // reduce it...
32765         
32766         // this bit calcs how man columns..
32767             
32768         var columnWidth = this.columnWidth += this.gutter;
32769       
32770         // calculate columns
32771         var containerWidth = this.containerWidth + this.gutter;
32772         
32773         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32774         // fix rounding errors, typically with gutters
32775         var excess = columnWidth - containerWidth % columnWidth;
32776         
32777         
32778         // if overshoot is less than a pixel, round up, otherwise floor it
32779         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32780         cols = Math[ mathMethod ]( cols );
32781         this.cols = Math.max( cols, 1 );
32782         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32783         
32784          // padding positioning..
32785         var totalColWidth = this.cols * this.columnWidth;
32786         var padavail = this.containerWidth - totalColWidth;
32787         // so for 2 columns - we need 3 'pads'
32788         
32789         var padNeeded = (1+this.cols) * this.padWidth;
32790         
32791         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32792         
32793         this.columnWidth += padExtra
32794         //this.padWidth = Math.floor(padavail /  ( this.cols));
32795         
32796         // adjust colum width so that padding is fixed??
32797         
32798         // we have 3 columns ... total = width * 3
32799         // we have X left over... that should be used by 
32800         
32801         //if (this.expandC) {
32802             
32803         //}
32804         
32805         
32806         
32807     },
32808     
32809     getContainerWidth : function()
32810     {
32811        /* // container is parent if fit width
32812         var container = this.isFitWidth ? this.element.parentNode : this.element;
32813         // check that this.size and size are there
32814         // IE8 triggers resize on body size change, so they might not be
32815         
32816         var size = getSize( container );  //FIXME
32817         this.containerWidth = size && size.innerWidth; //FIXME
32818         */
32819          
32820         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32821         
32822     },
32823     
32824     _getItemLayoutPosition : function( item )  // what is item?
32825     {
32826         // we resize the item to our columnWidth..
32827       
32828         item.setWidth(this.columnWidth);
32829         item.autoBoxAdjust  = false;
32830         
32831         var sz = item.getSize();
32832  
32833         // how many columns does this brick span
32834         var remainder = this.containerWidth % this.columnWidth;
32835         
32836         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32837         // round if off by 1 pixel, otherwise use ceil
32838         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32839         colSpan = Math.min( colSpan, this.cols );
32840         
32841         // normally this should be '1' as we dont' currently allow multi width columns..
32842         
32843         var colGroup = this._getColGroup( colSpan );
32844         // get the minimum Y value from the columns
32845         var minimumY = Math.min.apply( Math, colGroup );
32846         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32847         
32848         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32849          
32850         // position the brick
32851         var position = {
32852             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32853             y: this.currentSize.y + minimumY + this.padHeight
32854         };
32855         
32856         Roo.log(position);
32857         // apply setHeight to necessary columns
32858         var setHeight = minimumY + sz.height + this.padHeight;
32859         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32860         
32861         var setSpan = this.cols + 1 - colGroup.length;
32862         for ( var i = 0; i < setSpan; i++ ) {
32863           this.colYs[ shortColIndex + i ] = setHeight ;
32864         }
32865       
32866         return position;
32867     },
32868     
32869     /**
32870      * @param {Number} colSpan - number of columns the element spans
32871      * @returns {Array} colGroup
32872      */
32873     _getColGroup : function( colSpan )
32874     {
32875         if ( colSpan < 2 ) {
32876           // if brick spans only one column, use all the column Ys
32877           return this.colYs;
32878         }
32879       
32880         var colGroup = [];
32881         // how many different places could this brick fit horizontally
32882         var groupCount = this.cols + 1 - colSpan;
32883         // for each group potential horizontal position
32884         for ( var i = 0; i < groupCount; i++ ) {
32885           // make an array of colY values for that one group
32886           var groupColYs = this.colYs.slice( i, i + colSpan );
32887           // and get the max value of the array
32888           colGroup[i] = Math.max.apply( Math, groupColYs );
32889         }
32890         return colGroup;
32891     },
32892     /*
32893     _manageStamp : function( stamp )
32894     {
32895         var stampSize =  stamp.getSize();
32896         var offset = stamp.getBox();
32897         // get the columns that this stamp affects
32898         var firstX = this.isOriginLeft ? offset.x : offset.right;
32899         var lastX = firstX + stampSize.width;
32900         var firstCol = Math.floor( firstX / this.columnWidth );
32901         firstCol = Math.max( 0, firstCol );
32902         
32903         var lastCol = Math.floor( lastX / this.columnWidth );
32904         // lastCol should not go over if multiple of columnWidth #425
32905         lastCol -= lastX % this.columnWidth ? 0 : 1;
32906         lastCol = Math.min( this.cols - 1, lastCol );
32907         
32908         // set colYs to bottom of the stamp
32909         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32910             stampSize.height;
32911             
32912         for ( var i = firstCol; i <= lastCol; i++ ) {
32913           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32914         }
32915     },
32916     */
32917     
32918     _getContainerSize : function()
32919     {
32920         this.maxY = Math.max.apply( Math, this.colYs );
32921         var size = {
32922             height: this.maxY
32923         };
32924       
32925         if ( this.isFitWidth ) {
32926             size.width = this._getContainerFitWidth();
32927         }
32928       
32929         return size;
32930     },
32931     
32932     _getContainerFitWidth : function()
32933     {
32934         var unusedCols = 0;
32935         // count unused columns
32936         var i = this.cols;
32937         while ( --i ) {
32938           if ( this.colYs[i] !== 0 ) {
32939             break;
32940           }
32941           unusedCols++;
32942         }
32943         // fit container to columns that have been used
32944         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32945     },
32946     
32947     needsResizeLayout : function()
32948     {
32949         var previousWidth = this.containerWidth;
32950         this.getContainerWidth();
32951         return previousWidth !== this.containerWidth;
32952     }
32953  
32954 });
32955
32956  
32957
32958  /*
32959  * - LGPL
32960  *
32961  * element
32962  * 
32963  */
32964
32965 /**
32966  * @class Roo.bootstrap.MasonryBrick
32967  * @extends Roo.bootstrap.Component
32968  * Bootstrap MasonryBrick class
32969  * 
32970  * @constructor
32971  * Create a new MasonryBrick
32972  * @param {Object} config The config object
32973  */
32974
32975 Roo.bootstrap.MasonryBrick = function(config){
32976     
32977     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32978     
32979     Roo.bootstrap.MasonryBrick.register(this);
32980     
32981     this.addEvents({
32982         // raw events
32983         /**
32984          * @event click
32985          * When a MasonryBrick is clcik
32986          * @param {Roo.bootstrap.MasonryBrick} this
32987          * @param {Roo.EventObject} e
32988          */
32989         "click" : true
32990     });
32991 };
32992
32993 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32994     
32995     /**
32996      * @cfg {String} title
32997      */   
32998     title : '',
32999     /**
33000      * @cfg {String} html
33001      */   
33002     html : '',
33003     /**
33004      * @cfg {String} bgimage
33005      */   
33006     bgimage : '',
33007     /**
33008      * @cfg {String} videourl
33009      */   
33010     videourl : '',
33011     /**
33012      * @cfg {String} cls
33013      */   
33014     cls : '',
33015     /**
33016      * @cfg {String} href
33017      */   
33018     href : '',
33019     /**
33020      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
33021      */   
33022     size : 'xs',
33023     
33024     /**
33025      * @cfg {String} placetitle (center|bottom)
33026      */   
33027     placetitle : '',
33028     
33029     /**
33030      * @cfg {Boolean} isFitContainer defalut true
33031      */   
33032     isFitContainer : true, 
33033     
33034     /**
33035      * @cfg {Boolean} preventDefault defalut false
33036      */   
33037     preventDefault : false, 
33038     
33039     /**
33040      * @cfg {Boolean} inverse defalut false
33041      */   
33042     maskInverse : false, 
33043     
33044     getAutoCreate : function()
33045     {
33046         if(!this.isFitContainer){
33047             return this.getSplitAutoCreate();
33048         }
33049         
33050         var cls = 'masonry-brick masonry-brick-full';
33051         
33052         if(this.href.length){
33053             cls += ' masonry-brick-link';
33054         }
33055         
33056         if(this.bgimage.length){
33057             cls += ' masonry-brick-image';
33058         }
33059         
33060         if(this.maskInverse){
33061             cls += ' mask-inverse';
33062         }
33063         
33064         if(!this.html.length && !this.maskInverse && !this.videourl.length){
33065             cls += ' enable-mask';
33066         }
33067         
33068         if(this.size){
33069             cls += ' masonry-' + this.size + '-brick';
33070         }
33071         
33072         if(this.placetitle.length){
33073             
33074             switch (this.placetitle) {
33075                 case 'center' :
33076                     cls += ' masonry-center-title';
33077                     break;
33078                 case 'bottom' :
33079                     cls += ' masonry-bottom-title';
33080                     break;
33081                 default:
33082                     break;
33083             }
33084             
33085         } else {
33086             if(!this.html.length && !this.bgimage.length){
33087                 cls += ' masonry-center-title';
33088             }
33089
33090             if(!this.html.length && this.bgimage.length){
33091                 cls += ' masonry-bottom-title';
33092             }
33093         }
33094         
33095         if(this.cls){
33096             cls += ' ' + this.cls;
33097         }
33098         
33099         var cfg = {
33100             tag: (this.href.length) ? 'a' : 'div',
33101             cls: cls,
33102             cn: [
33103                 {
33104                     tag: 'div',
33105                     cls: 'masonry-brick-mask'
33106                 },
33107                 {
33108                     tag: 'div',
33109                     cls: 'masonry-brick-paragraph',
33110                     cn: []
33111                 }
33112             ]
33113         };
33114         
33115         if(this.href.length){
33116             cfg.href = this.href;
33117         }
33118         
33119         var cn = cfg.cn[1].cn;
33120         
33121         if(this.title.length){
33122             cn.push({
33123                 tag: 'h4',
33124                 cls: 'masonry-brick-title',
33125                 html: this.title
33126             });
33127         }
33128         
33129         if(this.html.length){
33130             cn.push({
33131                 tag: 'p',
33132                 cls: 'masonry-brick-text',
33133                 html: this.html
33134             });
33135         }
33136         
33137         if (!this.title.length && !this.html.length) {
33138             cfg.cn[1].cls += ' hide';
33139         }
33140         
33141         if(this.bgimage.length){
33142             cfg.cn.push({
33143                 tag: 'img',
33144                 cls: 'masonry-brick-image-view',
33145                 src: this.bgimage
33146             });
33147         }
33148         
33149         if(this.videourl.length){
33150             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33151             // youtube support only?
33152             cfg.cn.push({
33153                 tag: 'iframe',
33154                 cls: 'masonry-brick-image-view',
33155                 src: vurl,
33156                 frameborder : 0,
33157                 allowfullscreen : true
33158             });
33159         }
33160         
33161         return cfg;
33162         
33163     },
33164     
33165     getSplitAutoCreate : function()
33166     {
33167         var cls = 'masonry-brick masonry-brick-split';
33168         
33169         if(this.href.length){
33170             cls += ' masonry-brick-link';
33171         }
33172         
33173         if(this.bgimage.length){
33174             cls += ' masonry-brick-image';
33175         }
33176         
33177         if(this.size){
33178             cls += ' masonry-' + this.size + '-brick';
33179         }
33180         
33181         switch (this.placetitle) {
33182             case 'center' :
33183                 cls += ' masonry-center-title';
33184                 break;
33185             case 'bottom' :
33186                 cls += ' masonry-bottom-title';
33187                 break;
33188             default:
33189                 if(!this.bgimage.length){
33190                     cls += ' masonry-center-title';
33191                 }
33192
33193                 if(this.bgimage.length){
33194                     cls += ' masonry-bottom-title';
33195                 }
33196                 break;
33197         }
33198         
33199         if(this.cls){
33200             cls += ' ' + this.cls;
33201         }
33202         
33203         var cfg = {
33204             tag: (this.href.length) ? 'a' : 'div',
33205             cls: cls,
33206             cn: [
33207                 {
33208                     tag: 'div',
33209                     cls: 'masonry-brick-split-head',
33210                     cn: [
33211                         {
33212                             tag: 'div',
33213                             cls: 'masonry-brick-paragraph',
33214                             cn: []
33215                         }
33216                     ]
33217                 },
33218                 {
33219                     tag: 'div',
33220                     cls: 'masonry-brick-split-body',
33221                     cn: []
33222                 }
33223             ]
33224         };
33225         
33226         if(this.href.length){
33227             cfg.href = this.href;
33228         }
33229         
33230         if(this.title.length){
33231             cfg.cn[0].cn[0].cn.push({
33232                 tag: 'h4',
33233                 cls: 'masonry-brick-title',
33234                 html: this.title
33235             });
33236         }
33237         
33238         if(this.html.length){
33239             cfg.cn[1].cn.push({
33240                 tag: 'p',
33241                 cls: 'masonry-brick-text',
33242                 html: this.html
33243             });
33244         }
33245
33246         if(this.bgimage.length){
33247             cfg.cn[0].cn.push({
33248                 tag: 'img',
33249                 cls: 'masonry-brick-image-view',
33250                 src: this.bgimage
33251             });
33252         }
33253         
33254         if(this.videourl.length){
33255             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33256             // youtube support only?
33257             cfg.cn[0].cn.cn.push({
33258                 tag: 'iframe',
33259                 cls: 'masonry-brick-image-view',
33260                 src: vurl,
33261                 frameborder : 0,
33262                 allowfullscreen : true
33263             });
33264         }
33265         
33266         return cfg;
33267     },
33268     
33269     initEvents: function() 
33270     {
33271         switch (this.size) {
33272             case 'xs' :
33273                 this.x = 1;
33274                 this.y = 1;
33275                 break;
33276             case 'sm' :
33277                 this.x = 2;
33278                 this.y = 2;
33279                 break;
33280             case 'md' :
33281             case 'md-left' :
33282             case 'md-right' :
33283                 this.x = 3;
33284                 this.y = 3;
33285                 break;
33286             case 'tall' :
33287                 this.x = 2;
33288                 this.y = 3;
33289                 break;
33290             case 'wide' :
33291                 this.x = 3;
33292                 this.y = 2;
33293                 break;
33294             case 'wide-thin' :
33295                 this.x = 3;
33296                 this.y = 1;
33297                 break;
33298                         
33299             default :
33300                 break;
33301         }
33302         
33303         if(Roo.isTouch){
33304             this.el.on('touchstart', this.onTouchStart, this);
33305             this.el.on('touchmove', this.onTouchMove, this);
33306             this.el.on('touchend', this.onTouchEnd, this);
33307             this.el.on('contextmenu', this.onContextMenu, this);
33308         } else {
33309             this.el.on('mouseenter'  ,this.enter, this);
33310             this.el.on('mouseleave', this.leave, this);
33311             this.el.on('click', this.onClick, this);
33312         }
33313         
33314         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33315             this.parent().bricks.push(this);   
33316         }
33317         
33318     },
33319     
33320     onClick: function(e, el)
33321     {
33322         var time = this.endTimer - this.startTimer;
33323         // Roo.log(e.preventDefault());
33324         if(Roo.isTouch){
33325             if(time > 1000){
33326                 e.preventDefault();
33327                 return;
33328             }
33329         }
33330         
33331         if(!this.preventDefault){
33332             return;
33333         }
33334         
33335         e.preventDefault();
33336         
33337         if (this.activeClass != '') {
33338             this.selectBrick();
33339         }
33340         
33341         this.fireEvent('click', this, e);
33342     },
33343     
33344     enter: function(e, el)
33345     {
33346         e.preventDefault();
33347         
33348         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33349             return;
33350         }
33351         
33352         if(this.bgimage.length && this.html.length){
33353             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33354         }
33355     },
33356     
33357     leave: function(e, el)
33358     {
33359         e.preventDefault();
33360         
33361         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33362             return;
33363         }
33364         
33365         if(this.bgimage.length && this.html.length){
33366             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33367         }
33368     },
33369     
33370     onTouchStart: function(e, el)
33371     {
33372 //        e.preventDefault();
33373         
33374         this.touchmoved = false;
33375         
33376         if(!this.isFitContainer){
33377             return;
33378         }
33379         
33380         if(!this.bgimage.length || !this.html.length){
33381             return;
33382         }
33383         
33384         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33385         
33386         this.timer = new Date().getTime();
33387         
33388     },
33389     
33390     onTouchMove: function(e, el)
33391     {
33392         this.touchmoved = true;
33393     },
33394     
33395     onContextMenu : function(e,el)
33396     {
33397         e.preventDefault();
33398         e.stopPropagation();
33399         return false;
33400     },
33401     
33402     onTouchEnd: function(e, el)
33403     {
33404 //        e.preventDefault();
33405         
33406         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33407         
33408             this.leave(e,el);
33409             
33410             return;
33411         }
33412         
33413         if(!this.bgimage.length || !this.html.length){
33414             
33415             if(this.href.length){
33416                 window.location.href = this.href;
33417             }
33418             
33419             return;
33420         }
33421         
33422         if(!this.isFitContainer){
33423             return;
33424         }
33425         
33426         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33427         
33428         window.location.href = this.href;
33429     },
33430     
33431     //selection on single brick only
33432     selectBrick : function() {
33433         
33434         if (!this.parentId) {
33435             return;
33436         }
33437         
33438         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33439         var index = m.selectedBrick.indexOf(this.id);
33440         
33441         if ( index > -1) {
33442             m.selectedBrick.splice(index,1);
33443             this.el.removeClass(this.activeClass);
33444             return;
33445         }
33446         
33447         for(var i = 0; i < m.selectedBrick.length; i++) {
33448             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33449             b.el.removeClass(b.activeClass);
33450         }
33451         
33452         m.selectedBrick = [];
33453         
33454         m.selectedBrick.push(this.id);
33455         this.el.addClass(this.activeClass);
33456         return;
33457     },
33458     
33459     isSelected : function(){
33460         return this.el.hasClass(this.activeClass);
33461         
33462     }
33463 });
33464
33465 Roo.apply(Roo.bootstrap.MasonryBrick, {
33466     
33467     //groups: {},
33468     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33469      /**
33470     * register a Masonry Brick
33471     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33472     */
33473     
33474     register : function(brick)
33475     {
33476         //this.groups[brick.id] = brick;
33477         this.groups.add(brick.id, brick);
33478     },
33479     /**
33480     * fetch a  masonry brick based on the masonry brick ID
33481     * @param {string} the masonry brick to add
33482     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33483     */
33484     
33485     get: function(brick_id) 
33486     {
33487         // if (typeof(this.groups[brick_id]) == 'undefined') {
33488         //     return false;
33489         // }
33490         // return this.groups[brick_id] ;
33491         
33492         if(this.groups.key(brick_id)) {
33493             return this.groups.key(brick_id);
33494         }
33495         
33496         return false;
33497     }
33498     
33499     
33500     
33501 });
33502
33503  /*
33504  * - LGPL
33505  *
33506  * element
33507  * 
33508  */
33509
33510 /**
33511  * @class Roo.bootstrap.Brick
33512  * @extends Roo.bootstrap.Component
33513  * Bootstrap Brick class
33514  * 
33515  * @constructor
33516  * Create a new Brick
33517  * @param {Object} config The config object
33518  */
33519
33520 Roo.bootstrap.Brick = function(config){
33521     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33522     
33523     this.addEvents({
33524         // raw events
33525         /**
33526          * @event click
33527          * When a Brick is click
33528          * @param {Roo.bootstrap.Brick} this
33529          * @param {Roo.EventObject} e
33530          */
33531         "click" : true
33532     });
33533 };
33534
33535 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33536     
33537     /**
33538      * @cfg {String} title
33539      */   
33540     title : '',
33541     /**
33542      * @cfg {String} html
33543      */   
33544     html : '',
33545     /**
33546      * @cfg {String} bgimage
33547      */   
33548     bgimage : '',
33549     /**
33550      * @cfg {String} cls
33551      */   
33552     cls : '',
33553     /**
33554      * @cfg {String} href
33555      */   
33556     href : '',
33557     /**
33558      * @cfg {String} video
33559      */   
33560     video : '',
33561     /**
33562      * @cfg {Boolean} square
33563      */   
33564     square : true,
33565     
33566     getAutoCreate : function()
33567     {
33568         var cls = 'roo-brick';
33569         
33570         if(this.href.length){
33571             cls += ' roo-brick-link';
33572         }
33573         
33574         if(this.bgimage.length){
33575             cls += ' roo-brick-image';
33576         }
33577         
33578         if(!this.html.length && !this.bgimage.length){
33579             cls += ' roo-brick-center-title';
33580         }
33581         
33582         if(!this.html.length && this.bgimage.length){
33583             cls += ' roo-brick-bottom-title';
33584         }
33585         
33586         if(this.cls){
33587             cls += ' ' + this.cls;
33588         }
33589         
33590         var cfg = {
33591             tag: (this.href.length) ? 'a' : 'div',
33592             cls: cls,
33593             cn: [
33594                 {
33595                     tag: 'div',
33596                     cls: 'roo-brick-paragraph',
33597                     cn: []
33598                 }
33599             ]
33600         };
33601         
33602         if(this.href.length){
33603             cfg.href = this.href;
33604         }
33605         
33606         var cn = cfg.cn[0].cn;
33607         
33608         if(this.title.length){
33609             cn.push({
33610                 tag: 'h4',
33611                 cls: 'roo-brick-title',
33612                 html: this.title
33613             });
33614         }
33615         
33616         if(this.html.length){
33617             cn.push({
33618                 tag: 'p',
33619                 cls: 'roo-brick-text',
33620                 html: this.html
33621             });
33622         } else {
33623             cn.cls += ' hide';
33624         }
33625         
33626         if(this.bgimage.length){
33627             cfg.cn.push({
33628                 tag: 'img',
33629                 cls: 'roo-brick-image-view',
33630                 src: this.bgimage
33631             });
33632         }
33633         
33634         return cfg;
33635     },
33636     
33637     initEvents: function() 
33638     {
33639         if(this.title.length || this.html.length){
33640             this.el.on('mouseenter'  ,this.enter, this);
33641             this.el.on('mouseleave', this.leave, this);
33642         }
33643         
33644         Roo.EventManager.onWindowResize(this.resize, this); 
33645         
33646         if(this.bgimage.length){
33647             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33648             this.imageEl.on('load', this.onImageLoad, this);
33649             return;
33650         }
33651         
33652         this.resize();
33653     },
33654     
33655     onImageLoad : function()
33656     {
33657         this.resize();
33658     },
33659     
33660     resize : function()
33661     {
33662         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33663         
33664         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33665         
33666         if(this.bgimage.length){
33667             var image = this.el.select('.roo-brick-image-view', true).first();
33668             
33669             image.setWidth(paragraph.getWidth());
33670             
33671             if(this.square){
33672                 image.setHeight(paragraph.getWidth());
33673             }
33674             
33675             this.el.setHeight(image.getHeight());
33676             paragraph.setHeight(image.getHeight());
33677             
33678         }
33679         
33680     },
33681     
33682     enter: function(e, el)
33683     {
33684         e.preventDefault();
33685         
33686         if(this.bgimage.length){
33687             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33688             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33689         }
33690     },
33691     
33692     leave: function(e, el)
33693     {
33694         e.preventDefault();
33695         
33696         if(this.bgimage.length){
33697             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33698             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33699         }
33700     }
33701     
33702 });
33703
33704  
33705
33706  /*
33707  * - LGPL
33708  *
33709  * Number field 
33710  */
33711
33712 /**
33713  * @class Roo.bootstrap.NumberField
33714  * @extends Roo.bootstrap.Input
33715  * Bootstrap NumberField class
33716  * 
33717  * 
33718  * 
33719  * 
33720  * @constructor
33721  * Create a new NumberField
33722  * @param {Object} config The config object
33723  */
33724
33725 Roo.bootstrap.NumberField = function(config){
33726     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33727 };
33728
33729 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33730     
33731     /**
33732      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33733      */
33734     allowDecimals : true,
33735     /**
33736      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33737      */
33738     decimalSeparator : ".",
33739     /**
33740      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33741      */
33742     decimalPrecision : 2,
33743     /**
33744      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33745      */
33746     allowNegative : true,
33747     
33748     /**
33749      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33750      */
33751     allowZero: true,
33752     /**
33753      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33754      */
33755     minValue : Number.NEGATIVE_INFINITY,
33756     /**
33757      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33758      */
33759     maxValue : Number.MAX_VALUE,
33760     /**
33761      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33762      */
33763     minText : "The minimum value for this field is {0}",
33764     /**
33765      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33766      */
33767     maxText : "The maximum value for this field is {0}",
33768     /**
33769      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33770      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33771      */
33772     nanText : "{0} is not a valid number",
33773     /**
33774      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33775      */
33776     thousandsDelimiter : false,
33777     /**
33778      * @cfg {String} valueAlign alignment of value
33779      */
33780     valueAlign : "left",
33781
33782     getAutoCreate : function()
33783     {
33784         var hiddenInput = {
33785             tag: 'input',
33786             type: 'hidden',
33787             id: Roo.id(),
33788             cls: 'hidden-number-input'
33789         };
33790         
33791         if (this.name) {
33792             hiddenInput.name = this.name;
33793         }
33794         
33795         this.name = '';
33796         
33797         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33798         
33799         this.name = hiddenInput.name;
33800         
33801         if(cfg.cn.length > 0) {
33802             cfg.cn.push(hiddenInput);
33803         }
33804         
33805         return cfg;
33806     },
33807
33808     // private
33809     initEvents : function()
33810     {   
33811         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33812         
33813         var allowed = "0123456789";
33814         
33815         if(this.allowDecimals){
33816             allowed += this.decimalSeparator;
33817         }
33818         
33819         if(this.allowNegative){
33820             allowed += "-";
33821         }
33822         
33823         if(this.thousandsDelimiter) {
33824             allowed += ",";
33825         }
33826         
33827         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33828         
33829         var keyPress = function(e){
33830             
33831             var k = e.getKey();
33832             
33833             var c = e.getCharCode();
33834             
33835             if(
33836                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33837                     allowed.indexOf(String.fromCharCode(c)) === -1
33838             ){
33839                 e.stopEvent();
33840                 return;
33841             }
33842             
33843             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33844                 return;
33845             }
33846             
33847             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33848                 e.stopEvent();
33849             }
33850         };
33851         
33852         this.el.on("keypress", keyPress, this);
33853     },
33854     
33855     validateValue : function(value)
33856     {
33857         
33858         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33859             return false;
33860         }
33861         
33862         var num = this.parseValue(value);
33863         
33864         if(isNaN(num)){
33865             this.markInvalid(String.format(this.nanText, value));
33866             return false;
33867         }
33868         
33869         if(num < this.minValue){
33870             this.markInvalid(String.format(this.minText, this.minValue));
33871             return false;
33872         }
33873         
33874         if(num > this.maxValue){
33875             this.markInvalid(String.format(this.maxText, this.maxValue));
33876             return false;
33877         }
33878         
33879         return true;
33880     },
33881
33882     getValue : function()
33883     {
33884         var v = this.hiddenEl().getValue();
33885         
33886         return this.fixPrecision(this.parseValue(v));
33887     },
33888
33889     parseValue : function(value)
33890     {
33891         if(this.thousandsDelimiter) {
33892             value += "";
33893             r = new RegExp(",", "g");
33894             value = value.replace(r, "");
33895         }
33896         
33897         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33898         return isNaN(value) ? '' : value;
33899     },
33900
33901     fixPrecision : function(value)
33902     {
33903         if(this.thousandsDelimiter) {
33904             value += "";
33905             r = new RegExp(",", "g");
33906             value = value.replace(r, "");
33907         }
33908         
33909         var nan = isNaN(value);
33910         
33911         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33912             return nan ? '' : value;
33913         }
33914         return parseFloat(value).toFixed(this.decimalPrecision);
33915     },
33916
33917     setValue : function(v)
33918     {
33919         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33920         
33921         this.value = v;
33922         
33923         if(this.rendered){
33924             
33925             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33926             
33927             this.inputEl().dom.value = (v == '') ? '' :
33928                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33929             
33930             if(!this.allowZero && v === '0') {
33931                 this.hiddenEl().dom.value = '';
33932                 this.inputEl().dom.value = '';
33933             }
33934             
33935             this.validate();
33936         }
33937     },
33938
33939     decimalPrecisionFcn : function(v)
33940     {
33941         return Math.floor(v);
33942     },
33943
33944     beforeBlur : function()
33945     {
33946         var v = this.parseValue(this.getRawValue());
33947         
33948         if(v || v === 0 || v === ''){
33949             this.setValue(v);
33950         }
33951     },
33952     
33953     hiddenEl : function()
33954     {
33955         return this.el.select('input.hidden-number-input',true).first();
33956     }
33957     
33958 });
33959
33960  
33961
33962 /*
33963 * Licence: LGPL
33964 */
33965
33966 /**
33967  * @class Roo.bootstrap.DocumentSlider
33968  * @extends Roo.bootstrap.Component
33969  * Bootstrap DocumentSlider class
33970  * 
33971  * @constructor
33972  * Create a new DocumentViewer
33973  * @param {Object} config The config object
33974  */
33975
33976 Roo.bootstrap.DocumentSlider = function(config){
33977     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33978     
33979     this.files = [];
33980     
33981     this.addEvents({
33982         /**
33983          * @event initial
33984          * Fire after initEvent
33985          * @param {Roo.bootstrap.DocumentSlider} this
33986          */
33987         "initial" : true,
33988         /**
33989          * @event update
33990          * Fire after update
33991          * @param {Roo.bootstrap.DocumentSlider} this
33992          */
33993         "update" : true,
33994         /**
33995          * @event click
33996          * Fire after click
33997          * @param {Roo.bootstrap.DocumentSlider} this
33998          */
33999         "click" : true
34000     });
34001 };
34002
34003 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
34004     
34005     files : false,
34006     
34007     indicator : 0,
34008     
34009     getAutoCreate : function()
34010     {
34011         var cfg = {
34012             tag : 'div',
34013             cls : 'roo-document-slider',
34014             cn : [
34015                 {
34016                     tag : 'div',
34017                     cls : 'roo-document-slider-header',
34018                     cn : [
34019                         {
34020                             tag : 'div',
34021                             cls : 'roo-document-slider-header-title'
34022                         }
34023                     ]
34024                 },
34025                 {
34026                     tag : 'div',
34027                     cls : 'roo-document-slider-body',
34028                     cn : [
34029                         {
34030                             tag : 'div',
34031                             cls : 'roo-document-slider-prev',
34032                             cn : [
34033                                 {
34034                                     tag : 'i',
34035                                     cls : 'fa fa-chevron-left'
34036                                 }
34037                             ]
34038                         },
34039                         {
34040                             tag : 'div',
34041                             cls : 'roo-document-slider-thumb',
34042                             cn : [
34043                                 {
34044                                     tag : 'img',
34045                                     cls : 'roo-document-slider-image'
34046                                 }
34047                             ]
34048                         },
34049                         {
34050                             tag : 'div',
34051                             cls : 'roo-document-slider-next',
34052                             cn : [
34053                                 {
34054                                     tag : 'i',
34055                                     cls : 'fa fa-chevron-right'
34056                                 }
34057                             ]
34058                         }
34059                     ]
34060                 }
34061             ]
34062         };
34063         
34064         return cfg;
34065     },
34066     
34067     initEvents : function()
34068     {
34069         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
34070         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
34071         
34072         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
34073         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
34074         
34075         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
34076         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
34077         
34078         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
34079         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
34080         
34081         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
34082         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
34083         
34084         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
34085         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34086         
34087         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
34088         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34089         
34090         this.thumbEl.on('click', this.onClick, this);
34091         
34092         this.prevIndicator.on('click', this.prev, this);
34093         
34094         this.nextIndicator.on('click', this.next, this);
34095         
34096     },
34097     
34098     initial : function()
34099     {
34100         if(this.files.length){
34101             this.indicator = 1;
34102             this.update()
34103         }
34104         
34105         this.fireEvent('initial', this);
34106     },
34107     
34108     update : function()
34109     {
34110         this.imageEl.attr('src', this.files[this.indicator - 1]);
34111         
34112         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
34113         
34114         this.prevIndicator.show();
34115         
34116         if(this.indicator == 1){
34117             this.prevIndicator.hide();
34118         }
34119         
34120         this.nextIndicator.show();
34121         
34122         if(this.indicator == this.files.length){
34123             this.nextIndicator.hide();
34124         }
34125         
34126         this.thumbEl.scrollTo('top');
34127         
34128         this.fireEvent('update', this);
34129     },
34130     
34131     onClick : function(e)
34132     {
34133         e.preventDefault();
34134         
34135         this.fireEvent('click', this);
34136     },
34137     
34138     prev : function(e)
34139     {
34140         e.preventDefault();
34141         
34142         this.indicator = Math.max(1, this.indicator - 1);
34143         
34144         this.update();
34145     },
34146     
34147     next : function(e)
34148     {
34149         e.preventDefault();
34150         
34151         this.indicator = Math.min(this.files.length, this.indicator + 1);
34152         
34153         this.update();
34154     }
34155 });
34156 /*
34157  * - LGPL
34158  *
34159  * RadioSet
34160  *
34161  *
34162  */
34163
34164 /**
34165  * @class Roo.bootstrap.RadioSet
34166  * @extends Roo.bootstrap.Input
34167  * Bootstrap RadioSet class
34168  * @cfg {String} indicatorpos (left|right) default left
34169  * @cfg {Boolean} inline (true|false) inline the element (default true)
34170  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
34171  * @constructor
34172  * Create a new RadioSet
34173  * @param {Object} config The config object
34174  */
34175
34176 Roo.bootstrap.RadioSet = function(config){
34177     
34178     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
34179     
34180     this.radioes = [];
34181     
34182     Roo.bootstrap.RadioSet.register(this);
34183     
34184     this.addEvents({
34185         /**
34186         * @event check
34187         * Fires when the element is checked or unchecked.
34188         * @param {Roo.bootstrap.RadioSet} this This radio
34189         * @param {Roo.bootstrap.Radio} item The checked item
34190         */
34191        check : true,
34192        /**
34193         * @event click
34194         * Fires when the element is click.
34195         * @param {Roo.bootstrap.RadioSet} this This radio set
34196         * @param {Roo.bootstrap.Radio} item The checked item
34197         * @param {Roo.EventObject} e The event object
34198         */
34199        click : true
34200     });
34201     
34202 };
34203
34204 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
34205
34206     radioes : false,
34207     
34208     inline : true,
34209     
34210     weight : '',
34211     
34212     indicatorpos : 'left',
34213     
34214     getAutoCreate : function()
34215     {
34216         var label = {
34217             tag : 'label',
34218             cls : 'roo-radio-set-label',
34219             cn : [
34220                 {
34221                     tag : 'span',
34222                     html : this.fieldLabel
34223                 }
34224             ]
34225         };
34226         if (Roo.bootstrap.version == 3) {
34227             
34228             
34229             if(this.indicatorpos == 'left'){
34230                 label.cn.unshift({
34231                     tag : 'i',
34232                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34233                     tooltip : 'This field is required'
34234                 });
34235             } else {
34236                 label.cn.push({
34237                     tag : 'i',
34238                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34239                     tooltip : 'This field is required'
34240                 });
34241             }
34242         }
34243         var items = {
34244             tag : 'div',
34245             cls : 'roo-radio-set-items'
34246         };
34247         
34248         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34249         
34250         if (align === 'left' && this.fieldLabel.length) {
34251             
34252             items = {
34253                 cls : "roo-radio-set-right", 
34254                 cn: [
34255                     items
34256                 ]
34257             };
34258             
34259             if(this.labelWidth > 12){
34260                 label.style = "width: " + this.labelWidth + 'px';
34261             }
34262             
34263             if(this.labelWidth < 13 && this.labelmd == 0){
34264                 this.labelmd = this.labelWidth;
34265             }
34266             
34267             if(this.labellg > 0){
34268                 label.cls += ' col-lg-' + this.labellg;
34269                 items.cls += ' col-lg-' + (12 - this.labellg);
34270             }
34271             
34272             if(this.labelmd > 0){
34273                 label.cls += ' col-md-' + this.labelmd;
34274                 items.cls += ' col-md-' + (12 - this.labelmd);
34275             }
34276             
34277             if(this.labelsm > 0){
34278                 label.cls += ' col-sm-' + this.labelsm;
34279                 items.cls += ' col-sm-' + (12 - this.labelsm);
34280             }
34281             
34282             if(this.labelxs > 0){
34283                 label.cls += ' col-xs-' + this.labelxs;
34284                 items.cls += ' col-xs-' + (12 - this.labelxs);
34285             }
34286         }
34287         
34288         var cfg = {
34289             tag : 'div',
34290             cls : 'roo-radio-set',
34291             cn : [
34292                 {
34293                     tag : 'input',
34294                     cls : 'roo-radio-set-input',
34295                     type : 'hidden',
34296                     name : this.name,
34297                     value : this.value ? this.value :  ''
34298                 },
34299                 label,
34300                 items
34301             ]
34302         };
34303         
34304         if(this.weight.length){
34305             cfg.cls += ' roo-radio-' + this.weight;
34306         }
34307         
34308         if(this.inline) {
34309             cfg.cls += ' roo-radio-set-inline';
34310         }
34311         
34312         var settings=this;
34313         ['xs','sm','md','lg'].map(function(size){
34314             if (settings[size]) {
34315                 cfg.cls += ' col-' + size + '-' + settings[size];
34316             }
34317         });
34318         
34319         return cfg;
34320         
34321     },
34322
34323     initEvents : function()
34324     {
34325         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34326         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34327         
34328         if(!this.fieldLabel.length){
34329             this.labelEl.hide();
34330         }
34331         
34332         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34333         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34334         
34335         this.indicator = this.indicatorEl();
34336         
34337         if(this.indicator){
34338             this.indicator.addClass('invisible');
34339         }
34340         
34341         this.originalValue = this.getValue();
34342         
34343     },
34344     
34345     inputEl: function ()
34346     {
34347         return this.el.select('.roo-radio-set-input', true).first();
34348     },
34349     
34350     getChildContainer : function()
34351     {
34352         return this.itemsEl;
34353     },
34354     
34355     register : function(item)
34356     {
34357         this.radioes.push(item);
34358         
34359     },
34360     
34361     validate : function()
34362     {   
34363         if(this.getVisibilityEl().hasClass('hidden')){
34364             return true;
34365         }
34366         
34367         var valid = false;
34368         
34369         Roo.each(this.radioes, function(i){
34370             if(!i.checked){
34371                 return;
34372             }
34373             
34374             valid = true;
34375             return false;
34376         });
34377         
34378         if(this.allowBlank) {
34379             return true;
34380         }
34381         
34382         if(this.disabled || valid){
34383             this.markValid();
34384             return true;
34385         }
34386         
34387         this.markInvalid();
34388         return false;
34389         
34390     },
34391     
34392     markValid : function()
34393     {
34394         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34395             this.indicatorEl().removeClass('visible');
34396             this.indicatorEl().addClass('invisible');
34397         }
34398         
34399         
34400         if (Roo.bootstrap.version == 3) {
34401             this.el.removeClass([this.invalidClass, this.validClass]);
34402             this.el.addClass(this.validClass);
34403         } else {
34404             this.el.removeClass(['is-invalid','is-valid']);
34405             this.el.addClass(['is-valid']);
34406         }
34407         this.fireEvent('valid', this);
34408     },
34409     
34410     markInvalid : function(msg)
34411     {
34412         if(this.allowBlank || this.disabled){
34413             return;
34414         }
34415         
34416         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34417             this.indicatorEl().removeClass('invisible');
34418             this.indicatorEl().addClass('visible');
34419         }
34420         if (Roo.bootstrap.version == 3) {
34421             this.el.removeClass([this.invalidClass, this.validClass]);
34422             this.el.addClass(this.invalidClass);
34423         } else {
34424             this.el.removeClass(['is-invalid','is-valid']);
34425             this.el.addClass(['is-invalid']);
34426         }
34427         
34428         this.fireEvent('invalid', this, msg);
34429         
34430     },
34431     
34432     setValue : function(v, suppressEvent)
34433     {   
34434         if(this.value === v){
34435             return;
34436         }
34437         
34438         this.value = v;
34439         
34440         if(this.rendered){
34441             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34442         }
34443         
34444         Roo.each(this.radioes, function(i){
34445             i.checked = false;
34446             i.el.removeClass('checked');
34447         });
34448         
34449         Roo.each(this.radioes, function(i){
34450             
34451             if(i.value === v || i.value.toString() === v.toString()){
34452                 i.checked = true;
34453                 i.el.addClass('checked');
34454                 
34455                 if(suppressEvent !== true){
34456                     this.fireEvent('check', this, i);
34457                 }
34458                 
34459                 return false;
34460             }
34461             
34462         }, this);
34463         
34464         this.validate();
34465     },
34466     
34467     clearInvalid : function(){
34468         
34469         if(!this.el || this.preventMark){
34470             return;
34471         }
34472         
34473         this.el.removeClass([this.invalidClass]);
34474         
34475         this.fireEvent('valid', this);
34476     }
34477     
34478 });
34479
34480 Roo.apply(Roo.bootstrap.RadioSet, {
34481     
34482     groups: {},
34483     
34484     register : function(set)
34485     {
34486         this.groups[set.name] = set;
34487     },
34488     
34489     get: function(name) 
34490     {
34491         if (typeof(this.groups[name]) == 'undefined') {
34492             return false;
34493         }
34494         
34495         return this.groups[name] ;
34496     }
34497     
34498 });
34499 /*
34500  * Based on:
34501  * Ext JS Library 1.1.1
34502  * Copyright(c) 2006-2007, Ext JS, LLC.
34503  *
34504  * Originally Released Under LGPL - original licence link has changed is not relivant.
34505  *
34506  * Fork - LGPL
34507  * <script type="text/javascript">
34508  */
34509
34510
34511 /**
34512  * @class Roo.bootstrap.SplitBar
34513  * @extends Roo.util.Observable
34514  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34515  * <br><br>
34516  * Usage:
34517  * <pre><code>
34518 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34519                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34520 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34521 split.minSize = 100;
34522 split.maxSize = 600;
34523 split.animate = true;
34524 split.on('moved', splitterMoved);
34525 </code></pre>
34526  * @constructor
34527  * Create a new SplitBar
34528  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34529  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34530  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34531  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34532                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34533                         position of the SplitBar).
34534  */
34535 Roo.bootstrap.SplitBar = function(cfg){
34536     
34537     /** @private */
34538     
34539     //{
34540     //  dragElement : elm
34541     //  resizingElement: el,
34542         // optional..
34543     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34544     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34545         // existingProxy ???
34546     //}
34547     
34548     this.el = Roo.get(cfg.dragElement, true);
34549     this.el.dom.unselectable = "on";
34550     /** @private */
34551     this.resizingEl = Roo.get(cfg.resizingElement, true);
34552
34553     /**
34554      * @private
34555      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34556      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34557      * @type Number
34558      */
34559     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34560     
34561     /**
34562      * The minimum size of the resizing element. (Defaults to 0)
34563      * @type Number
34564      */
34565     this.minSize = 0;
34566     
34567     /**
34568      * The maximum size of the resizing element. (Defaults to 2000)
34569      * @type Number
34570      */
34571     this.maxSize = 2000;
34572     
34573     /**
34574      * Whether to animate the transition to the new size
34575      * @type Boolean
34576      */
34577     this.animate = false;
34578     
34579     /**
34580      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34581      * @type Boolean
34582      */
34583     this.useShim = false;
34584     
34585     /** @private */
34586     this.shim = null;
34587     
34588     if(!cfg.existingProxy){
34589         /** @private */
34590         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34591     }else{
34592         this.proxy = Roo.get(cfg.existingProxy).dom;
34593     }
34594     /** @private */
34595     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34596     
34597     /** @private */
34598     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34599     
34600     /** @private */
34601     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34602     
34603     /** @private */
34604     this.dragSpecs = {};
34605     
34606     /**
34607      * @private The adapter to use to positon and resize elements
34608      */
34609     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34610     this.adapter.init(this);
34611     
34612     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34613         /** @private */
34614         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34615         this.el.addClass("roo-splitbar-h");
34616     }else{
34617         /** @private */
34618         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34619         this.el.addClass("roo-splitbar-v");
34620     }
34621     
34622     this.addEvents({
34623         /**
34624          * @event resize
34625          * Fires when the splitter is moved (alias for {@link #event-moved})
34626          * @param {Roo.bootstrap.SplitBar} this
34627          * @param {Number} newSize the new width or height
34628          */
34629         "resize" : true,
34630         /**
34631          * @event moved
34632          * Fires when the splitter is moved
34633          * @param {Roo.bootstrap.SplitBar} this
34634          * @param {Number} newSize the new width or height
34635          */
34636         "moved" : true,
34637         /**
34638          * @event beforeresize
34639          * Fires before the splitter is dragged
34640          * @param {Roo.bootstrap.SplitBar} this
34641          */
34642         "beforeresize" : true,
34643
34644         "beforeapply" : true
34645     });
34646
34647     Roo.util.Observable.call(this);
34648 };
34649
34650 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34651     onStartProxyDrag : function(x, y){
34652         this.fireEvent("beforeresize", this);
34653         if(!this.overlay){
34654             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34655             o.unselectable();
34656             o.enableDisplayMode("block");
34657             // all splitbars share the same overlay
34658             Roo.bootstrap.SplitBar.prototype.overlay = o;
34659         }
34660         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34661         this.overlay.show();
34662         Roo.get(this.proxy).setDisplayed("block");
34663         var size = this.adapter.getElementSize(this);
34664         this.activeMinSize = this.getMinimumSize();;
34665         this.activeMaxSize = this.getMaximumSize();;
34666         var c1 = size - this.activeMinSize;
34667         var c2 = Math.max(this.activeMaxSize - size, 0);
34668         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34669             this.dd.resetConstraints();
34670             this.dd.setXConstraint(
34671                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34672                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34673             );
34674             this.dd.setYConstraint(0, 0);
34675         }else{
34676             this.dd.resetConstraints();
34677             this.dd.setXConstraint(0, 0);
34678             this.dd.setYConstraint(
34679                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34680                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34681             );
34682          }
34683         this.dragSpecs.startSize = size;
34684         this.dragSpecs.startPoint = [x, y];
34685         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34686     },
34687     
34688     /** 
34689      * @private Called after the drag operation by the DDProxy
34690      */
34691     onEndProxyDrag : function(e){
34692         Roo.get(this.proxy).setDisplayed(false);
34693         var endPoint = Roo.lib.Event.getXY(e);
34694         if(this.overlay){
34695             this.overlay.hide();
34696         }
34697         var newSize;
34698         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34699             newSize = this.dragSpecs.startSize + 
34700                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34701                     endPoint[0] - this.dragSpecs.startPoint[0] :
34702                     this.dragSpecs.startPoint[0] - endPoint[0]
34703                 );
34704         }else{
34705             newSize = this.dragSpecs.startSize + 
34706                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34707                     endPoint[1] - this.dragSpecs.startPoint[1] :
34708                     this.dragSpecs.startPoint[1] - endPoint[1]
34709                 );
34710         }
34711         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34712         if(newSize != this.dragSpecs.startSize){
34713             if(this.fireEvent('beforeapply', this, newSize) !== false){
34714                 this.adapter.setElementSize(this, newSize);
34715                 this.fireEvent("moved", this, newSize);
34716                 this.fireEvent("resize", this, newSize);
34717             }
34718         }
34719     },
34720     
34721     /**
34722      * Get the adapter this SplitBar uses
34723      * @return The adapter object
34724      */
34725     getAdapter : function(){
34726         return this.adapter;
34727     },
34728     
34729     /**
34730      * Set the adapter this SplitBar uses
34731      * @param {Object} adapter A SplitBar adapter object
34732      */
34733     setAdapter : function(adapter){
34734         this.adapter = adapter;
34735         this.adapter.init(this);
34736     },
34737     
34738     /**
34739      * Gets the minimum size for the resizing element
34740      * @return {Number} The minimum size
34741      */
34742     getMinimumSize : function(){
34743         return this.minSize;
34744     },
34745     
34746     /**
34747      * Sets the minimum size for the resizing element
34748      * @param {Number} minSize The minimum size
34749      */
34750     setMinimumSize : function(minSize){
34751         this.minSize = minSize;
34752     },
34753     
34754     /**
34755      * Gets the maximum size for the resizing element
34756      * @return {Number} The maximum size
34757      */
34758     getMaximumSize : function(){
34759         return this.maxSize;
34760     },
34761     
34762     /**
34763      * Sets the maximum size for the resizing element
34764      * @param {Number} maxSize The maximum size
34765      */
34766     setMaximumSize : function(maxSize){
34767         this.maxSize = maxSize;
34768     },
34769     
34770     /**
34771      * Sets the initialize size for the resizing element
34772      * @param {Number} size The initial size
34773      */
34774     setCurrentSize : function(size){
34775         var oldAnimate = this.animate;
34776         this.animate = false;
34777         this.adapter.setElementSize(this, size);
34778         this.animate = oldAnimate;
34779     },
34780     
34781     /**
34782      * Destroy this splitbar. 
34783      * @param {Boolean} removeEl True to remove the element
34784      */
34785     destroy : function(removeEl){
34786         if(this.shim){
34787             this.shim.remove();
34788         }
34789         this.dd.unreg();
34790         this.proxy.parentNode.removeChild(this.proxy);
34791         if(removeEl){
34792             this.el.remove();
34793         }
34794     }
34795 });
34796
34797 /**
34798  * @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.
34799  */
34800 Roo.bootstrap.SplitBar.createProxy = function(dir){
34801     var proxy = new Roo.Element(document.createElement("div"));
34802     proxy.unselectable();
34803     var cls = 'roo-splitbar-proxy';
34804     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34805     document.body.appendChild(proxy.dom);
34806     return proxy.dom;
34807 };
34808
34809 /** 
34810  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34811  * Default Adapter. It assumes the splitter and resizing element are not positioned
34812  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34813  */
34814 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34815 };
34816
34817 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34818     // do nothing for now
34819     init : function(s){
34820     
34821     },
34822     /**
34823      * Called before drag operations to get the current size of the resizing element. 
34824      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34825      */
34826      getElementSize : function(s){
34827         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34828             return s.resizingEl.getWidth();
34829         }else{
34830             return s.resizingEl.getHeight();
34831         }
34832     },
34833     
34834     /**
34835      * Called after drag operations to set the size of the resizing element.
34836      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34837      * @param {Number} newSize The new size to set
34838      * @param {Function} onComplete A function to be invoked when resizing is complete
34839      */
34840     setElementSize : function(s, newSize, onComplete){
34841         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34842             if(!s.animate){
34843                 s.resizingEl.setWidth(newSize);
34844                 if(onComplete){
34845                     onComplete(s, newSize);
34846                 }
34847             }else{
34848                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34849             }
34850         }else{
34851             
34852             if(!s.animate){
34853                 s.resizingEl.setHeight(newSize);
34854                 if(onComplete){
34855                     onComplete(s, newSize);
34856                 }
34857             }else{
34858                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34859             }
34860         }
34861     }
34862 };
34863
34864 /** 
34865  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34866  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34867  * Adapter that  moves the splitter element to align with the resized sizing element. 
34868  * Used with an absolute positioned SplitBar.
34869  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34870  * document.body, make sure you assign an id to the body element.
34871  */
34872 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34873     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34874     this.container = Roo.get(container);
34875 };
34876
34877 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34878     init : function(s){
34879         this.basic.init(s);
34880     },
34881     
34882     getElementSize : function(s){
34883         return this.basic.getElementSize(s);
34884     },
34885     
34886     setElementSize : function(s, newSize, onComplete){
34887         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34888     },
34889     
34890     moveSplitter : function(s){
34891         var yes = Roo.bootstrap.SplitBar;
34892         switch(s.placement){
34893             case yes.LEFT:
34894                 s.el.setX(s.resizingEl.getRight());
34895                 break;
34896             case yes.RIGHT:
34897                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34898                 break;
34899             case yes.TOP:
34900                 s.el.setY(s.resizingEl.getBottom());
34901                 break;
34902             case yes.BOTTOM:
34903                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34904                 break;
34905         }
34906     }
34907 };
34908
34909 /**
34910  * Orientation constant - Create a vertical SplitBar
34911  * @static
34912  * @type Number
34913  */
34914 Roo.bootstrap.SplitBar.VERTICAL = 1;
34915
34916 /**
34917  * Orientation constant - Create a horizontal SplitBar
34918  * @static
34919  * @type Number
34920  */
34921 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34922
34923 /**
34924  * Placement constant - The resizing element is to the left of the splitter element
34925  * @static
34926  * @type Number
34927  */
34928 Roo.bootstrap.SplitBar.LEFT = 1;
34929
34930 /**
34931  * Placement constant - The resizing element is to the right of the splitter element
34932  * @static
34933  * @type Number
34934  */
34935 Roo.bootstrap.SplitBar.RIGHT = 2;
34936
34937 /**
34938  * Placement constant - The resizing element is positioned above the splitter element
34939  * @static
34940  * @type Number
34941  */
34942 Roo.bootstrap.SplitBar.TOP = 3;
34943
34944 /**
34945  * Placement constant - The resizing element is positioned under splitter element
34946  * @static
34947  * @type Number
34948  */
34949 Roo.bootstrap.SplitBar.BOTTOM = 4;
34950 Roo.namespace("Roo.bootstrap.layout");/*
34951  * Based on:
34952  * Ext JS Library 1.1.1
34953  * Copyright(c) 2006-2007, Ext JS, LLC.
34954  *
34955  * Originally Released Under LGPL - original licence link has changed is not relivant.
34956  *
34957  * Fork - LGPL
34958  * <script type="text/javascript">
34959  */
34960
34961 /**
34962  * @class Roo.bootstrap.layout.Manager
34963  * @extends Roo.bootstrap.Component
34964  * Base class for layout managers.
34965  */
34966 Roo.bootstrap.layout.Manager = function(config)
34967 {
34968     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34969
34970
34971
34972
34973
34974     /** false to disable window resize monitoring @type Boolean */
34975     this.monitorWindowResize = true;
34976     this.regions = {};
34977     this.addEvents({
34978         /**
34979          * @event layout
34980          * Fires when a layout is performed.
34981          * @param {Roo.LayoutManager} this
34982          */
34983         "layout" : true,
34984         /**
34985          * @event regionresized
34986          * Fires when the user resizes a region.
34987          * @param {Roo.LayoutRegion} region The resized region
34988          * @param {Number} newSize The new size (width for east/west, height for north/south)
34989          */
34990         "regionresized" : true,
34991         /**
34992          * @event regioncollapsed
34993          * Fires when a region is collapsed.
34994          * @param {Roo.LayoutRegion} region The collapsed region
34995          */
34996         "regioncollapsed" : true,
34997         /**
34998          * @event regionexpanded
34999          * Fires when a region is expanded.
35000          * @param {Roo.LayoutRegion} region The expanded region
35001          */
35002         "regionexpanded" : true
35003     });
35004     this.updating = false;
35005
35006     if (config.el) {
35007         this.el = Roo.get(config.el);
35008         this.initEvents();
35009     }
35010
35011 };
35012
35013 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
35014
35015
35016     regions : null,
35017
35018     monitorWindowResize : true,
35019
35020
35021     updating : false,
35022
35023
35024     onRender : function(ct, position)
35025     {
35026         if(!this.el){
35027             this.el = Roo.get(ct);
35028             this.initEvents();
35029         }
35030         //this.fireEvent('render',this);
35031     },
35032
35033
35034     initEvents: function()
35035     {
35036
35037
35038         // ie scrollbar fix
35039         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
35040             document.body.scroll = "no";
35041         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
35042             this.el.position('relative');
35043         }
35044         this.id = this.el.id;
35045         this.el.addClass("roo-layout-container");
35046         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
35047         if(this.el.dom != document.body ) {
35048             this.el.on('resize', this.layout,this);
35049             this.el.on('show', this.layout,this);
35050         }
35051
35052     },
35053
35054     /**
35055      * Returns true if this layout is currently being updated
35056      * @return {Boolean}
35057      */
35058     isUpdating : function(){
35059         return this.updating;
35060     },
35061
35062     /**
35063      * Suspend the LayoutManager from doing auto-layouts while
35064      * making multiple add or remove calls
35065      */
35066     beginUpdate : function(){
35067         this.updating = true;
35068     },
35069
35070     /**
35071      * Restore auto-layouts and optionally disable the manager from performing a layout
35072      * @param {Boolean} noLayout true to disable a layout update
35073      */
35074     endUpdate : function(noLayout){
35075         this.updating = false;
35076         if(!noLayout){
35077             this.layout();
35078         }
35079     },
35080
35081     layout: function(){
35082         // abstract...
35083     },
35084
35085     onRegionResized : function(region, newSize){
35086         this.fireEvent("regionresized", region, newSize);
35087         this.layout();
35088     },
35089
35090     onRegionCollapsed : function(region){
35091         this.fireEvent("regioncollapsed", region);
35092     },
35093
35094     onRegionExpanded : function(region){
35095         this.fireEvent("regionexpanded", region);
35096     },
35097
35098     /**
35099      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
35100      * performs box-model adjustments.
35101      * @return {Object} The size as an object {width: (the width), height: (the height)}
35102      */
35103     getViewSize : function()
35104     {
35105         var size;
35106         if(this.el.dom != document.body){
35107             size = this.el.getSize();
35108         }else{
35109             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
35110         }
35111         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
35112         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35113         return size;
35114     },
35115
35116     /**
35117      * Returns the Element this layout is bound to.
35118      * @return {Roo.Element}
35119      */
35120     getEl : function(){
35121         return this.el;
35122     },
35123
35124     /**
35125      * Returns the specified region.
35126      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
35127      * @return {Roo.LayoutRegion}
35128      */
35129     getRegion : function(target){
35130         return this.regions[target.toLowerCase()];
35131     },
35132
35133     onWindowResize : function(){
35134         if(this.monitorWindowResize){
35135             this.layout();
35136         }
35137     }
35138 });
35139 /*
35140  * Based on:
35141  * Ext JS Library 1.1.1
35142  * Copyright(c) 2006-2007, Ext JS, LLC.
35143  *
35144  * Originally Released Under LGPL - original licence link has changed is not relivant.
35145  *
35146  * Fork - LGPL
35147  * <script type="text/javascript">
35148  */
35149 /**
35150  * @class Roo.bootstrap.layout.Border
35151  * @extends Roo.bootstrap.layout.Manager
35152  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
35153  * please see: examples/bootstrap/nested.html<br><br>
35154  
35155 <b>The container the layout is rendered into can be either the body element or any other element.
35156 If it is not the body element, the container needs to either be an absolute positioned element,
35157 or you will need to add "position:relative" to the css of the container.  You will also need to specify
35158 the container size if it is not the body element.</b>
35159
35160 * @constructor
35161 * Create a new Border
35162 * @param {Object} config Configuration options
35163  */
35164 Roo.bootstrap.layout.Border = function(config){
35165     config = config || {};
35166     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
35167     
35168     
35169     
35170     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35171         if(config[region]){
35172             config[region].region = region;
35173             this.addRegion(config[region]);
35174         }
35175     },this);
35176     
35177 };
35178
35179 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
35180
35181 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
35182     
35183     parent : false, // this might point to a 'nest' or a ???
35184     
35185     /**
35186      * Creates and adds a new region if it doesn't already exist.
35187      * @param {String} target The target region key (north, south, east, west or center).
35188      * @param {Object} config The regions config object
35189      * @return {BorderLayoutRegion} The new region
35190      */
35191     addRegion : function(config)
35192     {
35193         if(!this.regions[config.region]){
35194             var r = this.factory(config);
35195             this.bindRegion(r);
35196         }
35197         return this.regions[config.region];
35198     },
35199
35200     // private (kinda)
35201     bindRegion : function(r){
35202         this.regions[r.config.region] = r;
35203         
35204         r.on("visibilitychange",    this.layout, this);
35205         r.on("paneladded",          this.layout, this);
35206         r.on("panelremoved",        this.layout, this);
35207         r.on("invalidated",         this.layout, this);
35208         r.on("resized",             this.onRegionResized, this);
35209         r.on("collapsed",           this.onRegionCollapsed, this);
35210         r.on("expanded",            this.onRegionExpanded, this);
35211     },
35212
35213     /**
35214      * Performs a layout update.
35215      */
35216     layout : function()
35217     {
35218         if(this.updating) {
35219             return;
35220         }
35221         
35222         // render all the rebions if they have not been done alreayd?
35223         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35224             if(this.regions[region] && !this.regions[region].bodyEl){
35225                 this.regions[region].onRender(this.el)
35226             }
35227         },this);
35228         
35229         var size = this.getViewSize();
35230         var w = size.width;
35231         var h = size.height;
35232         var centerW = w;
35233         var centerH = h;
35234         var centerY = 0;
35235         var centerX = 0;
35236         //var x = 0, y = 0;
35237
35238         var rs = this.regions;
35239         var north = rs["north"];
35240         var south = rs["south"]; 
35241         var west = rs["west"];
35242         var east = rs["east"];
35243         var center = rs["center"];
35244         //if(this.hideOnLayout){ // not supported anymore
35245             //c.el.setStyle("display", "none");
35246         //}
35247         if(north && north.isVisible()){
35248             var b = north.getBox();
35249             var m = north.getMargins();
35250             b.width = w - (m.left+m.right);
35251             b.x = m.left;
35252             b.y = m.top;
35253             centerY = b.height + b.y + m.bottom;
35254             centerH -= centerY;
35255             north.updateBox(this.safeBox(b));
35256         }
35257         if(south && south.isVisible()){
35258             var b = south.getBox();
35259             var m = south.getMargins();
35260             b.width = w - (m.left+m.right);
35261             b.x = m.left;
35262             var totalHeight = (b.height + m.top + m.bottom);
35263             b.y = h - totalHeight + m.top;
35264             centerH -= totalHeight;
35265             south.updateBox(this.safeBox(b));
35266         }
35267         if(west && west.isVisible()){
35268             var b = west.getBox();
35269             var m = west.getMargins();
35270             b.height = centerH - (m.top+m.bottom);
35271             b.x = m.left;
35272             b.y = centerY + m.top;
35273             var totalWidth = (b.width + m.left + m.right);
35274             centerX += totalWidth;
35275             centerW -= totalWidth;
35276             west.updateBox(this.safeBox(b));
35277         }
35278         if(east && east.isVisible()){
35279             var b = east.getBox();
35280             var m = east.getMargins();
35281             b.height = centerH - (m.top+m.bottom);
35282             var totalWidth = (b.width + m.left + m.right);
35283             b.x = w - totalWidth + m.left;
35284             b.y = centerY + m.top;
35285             centerW -= totalWidth;
35286             east.updateBox(this.safeBox(b));
35287         }
35288         if(center){
35289             var m = center.getMargins();
35290             var centerBox = {
35291                 x: centerX + m.left,
35292                 y: centerY + m.top,
35293                 width: centerW - (m.left+m.right),
35294                 height: centerH - (m.top+m.bottom)
35295             };
35296             //if(this.hideOnLayout){
35297                 //center.el.setStyle("display", "block");
35298             //}
35299             center.updateBox(this.safeBox(centerBox));
35300         }
35301         this.el.repaint();
35302         this.fireEvent("layout", this);
35303     },
35304
35305     // private
35306     safeBox : function(box){
35307         box.width = Math.max(0, box.width);
35308         box.height = Math.max(0, box.height);
35309         return box;
35310     },
35311
35312     /**
35313      * Adds a ContentPanel (or subclass) to this layout.
35314      * @param {String} target The target region key (north, south, east, west or center).
35315      * @param {Roo.ContentPanel} panel The panel to add
35316      * @return {Roo.ContentPanel} The added panel
35317      */
35318     add : function(target, panel){
35319          
35320         target = target.toLowerCase();
35321         return this.regions[target].add(panel);
35322     },
35323
35324     /**
35325      * Remove a ContentPanel (or subclass) to this layout.
35326      * @param {String} target The target region key (north, south, east, west or center).
35327      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35328      * @return {Roo.ContentPanel} The removed panel
35329      */
35330     remove : function(target, panel){
35331         target = target.toLowerCase();
35332         return this.regions[target].remove(panel);
35333     },
35334
35335     /**
35336      * Searches all regions for a panel with the specified id
35337      * @param {String} panelId
35338      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35339      */
35340     findPanel : function(panelId){
35341         var rs = this.regions;
35342         for(var target in rs){
35343             if(typeof rs[target] != "function"){
35344                 var p = rs[target].getPanel(panelId);
35345                 if(p){
35346                     return p;
35347                 }
35348             }
35349         }
35350         return null;
35351     },
35352
35353     /**
35354      * Searches all regions for a panel with the specified id and activates (shows) it.
35355      * @param {String/ContentPanel} panelId The panels id or the panel itself
35356      * @return {Roo.ContentPanel} The shown panel or null
35357      */
35358     showPanel : function(panelId) {
35359       var rs = this.regions;
35360       for(var target in rs){
35361          var r = rs[target];
35362          if(typeof r != "function"){
35363             if(r.hasPanel(panelId)){
35364                return r.showPanel(panelId);
35365             }
35366          }
35367       }
35368       return null;
35369    },
35370
35371    /**
35372      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35373      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35374      */
35375    /*
35376     restoreState : function(provider){
35377         if(!provider){
35378             provider = Roo.state.Manager;
35379         }
35380         var sm = new Roo.LayoutStateManager();
35381         sm.init(this, provider);
35382     },
35383 */
35384  
35385  
35386     /**
35387      * Adds a xtype elements to the layout.
35388      * <pre><code>
35389
35390 layout.addxtype({
35391        xtype : 'ContentPanel',
35392        region: 'west',
35393        items: [ .... ]
35394    }
35395 );
35396
35397 layout.addxtype({
35398         xtype : 'NestedLayoutPanel',
35399         region: 'west',
35400         layout: {
35401            center: { },
35402            west: { }   
35403         },
35404         items : [ ... list of content panels or nested layout panels.. ]
35405    }
35406 );
35407 </code></pre>
35408      * @param {Object} cfg Xtype definition of item to add.
35409      */
35410     addxtype : function(cfg)
35411     {
35412         // basically accepts a pannel...
35413         // can accept a layout region..!?!?
35414         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35415         
35416         
35417         // theory?  children can only be panels??
35418         
35419         //if (!cfg.xtype.match(/Panel$/)) {
35420         //    return false;
35421         //}
35422         var ret = false;
35423         
35424         if (typeof(cfg.region) == 'undefined') {
35425             Roo.log("Failed to add Panel, region was not set");
35426             Roo.log(cfg);
35427             return false;
35428         }
35429         var region = cfg.region;
35430         delete cfg.region;
35431         
35432           
35433         var xitems = [];
35434         if (cfg.items) {
35435             xitems = cfg.items;
35436             delete cfg.items;
35437         }
35438         var nb = false;
35439         
35440         if ( region == 'center') {
35441             Roo.log("Center: " + cfg.title);
35442         }
35443         
35444         
35445         switch(cfg.xtype) 
35446         {
35447             case 'Content':  // ContentPanel (el, cfg)
35448             case 'Scroll':  // ContentPanel (el, cfg)
35449             case 'View': 
35450                 cfg.autoCreate = cfg.autoCreate || true;
35451                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35452                 //} else {
35453                 //    var el = this.el.createChild();
35454                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35455                 //}
35456                 
35457                 this.add(region, ret);
35458                 break;
35459             
35460             /*
35461             case 'TreePanel': // our new panel!
35462                 cfg.el = this.el.createChild();
35463                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35464                 this.add(region, ret);
35465                 break;
35466             */
35467             
35468             case 'Nest': 
35469                 // create a new Layout (which is  a Border Layout...
35470                 
35471                 var clayout = cfg.layout;
35472                 clayout.el  = this.el.createChild();
35473                 clayout.items   = clayout.items  || [];
35474                 
35475                 delete cfg.layout;
35476                 
35477                 // replace this exitems with the clayout ones..
35478                 xitems = clayout.items;
35479                  
35480                 // force background off if it's in center...
35481                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35482                     cfg.background = false;
35483                 }
35484                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35485                 
35486                 
35487                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35488                 //console.log('adding nested layout panel '  + cfg.toSource());
35489                 this.add(region, ret);
35490                 nb = {}; /// find first...
35491                 break;
35492             
35493             case 'Grid':
35494                 
35495                 // needs grid and region
35496                 
35497                 //var el = this.getRegion(region).el.createChild();
35498                 /*
35499                  *var el = this.el.createChild();
35500                 // create the grid first...
35501                 cfg.grid.container = el;
35502                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35503                 */
35504                 
35505                 if (region == 'center' && this.active ) {
35506                     cfg.background = false;
35507                 }
35508                 
35509                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35510                 
35511                 this.add(region, ret);
35512                 /*
35513                 if (cfg.background) {
35514                     // render grid on panel activation (if panel background)
35515                     ret.on('activate', function(gp) {
35516                         if (!gp.grid.rendered) {
35517                     //        gp.grid.render(el);
35518                         }
35519                     });
35520                 } else {
35521                   //  cfg.grid.render(el);
35522                 }
35523                 */
35524                 break;
35525            
35526            
35527             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35528                 // it was the old xcomponent building that caused this before.
35529                 // espeically if border is the top element in the tree.
35530                 ret = this;
35531                 break; 
35532                 
35533                     
35534                 
35535                 
35536                 
35537             default:
35538                 /*
35539                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35540                     
35541                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35542                     this.add(region, ret);
35543                 } else {
35544                 */
35545                     Roo.log(cfg);
35546                     throw "Can not add '" + cfg.xtype + "' to Border";
35547                     return null;
35548              
35549                                 
35550              
35551         }
35552         this.beginUpdate();
35553         // add children..
35554         var region = '';
35555         var abn = {};
35556         Roo.each(xitems, function(i)  {
35557             region = nb && i.region ? i.region : false;
35558             
35559             var add = ret.addxtype(i);
35560            
35561             if (region) {
35562                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35563                 if (!i.background) {
35564                     abn[region] = nb[region] ;
35565                 }
35566             }
35567             
35568         });
35569         this.endUpdate();
35570
35571         // make the last non-background panel active..
35572         //if (nb) { Roo.log(abn); }
35573         if (nb) {
35574             
35575             for(var r in abn) {
35576                 region = this.getRegion(r);
35577                 if (region) {
35578                     // tried using nb[r], but it does not work..
35579                      
35580                     region.showPanel(abn[r]);
35581                    
35582                 }
35583             }
35584         }
35585         return ret;
35586         
35587     },
35588     
35589     
35590 // private
35591     factory : function(cfg)
35592     {
35593         
35594         var validRegions = Roo.bootstrap.layout.Border.regions;
35595
35596         var target = cfg.region;
35597         cfg.mgr = this;
35598         
35599         var r = Roo.bootstrap.layout;
35600         Roo.log(target);
35601         switch(target){
35602             case "north":
35603                 return new r.North(cfg);
35604             case "south":
35605                 return new r.South(cfg);
35606             case "east":
35607                 return new r.East(cfg);
35608             case "west":
35609                 return new r.West(cfg);
35610             case "center":
35611                 return new r.Center(cfg);
35612         }
35613         throw 'Layout region "'+target+'" not supported.';
35614     }
35615     
35616     
35617 });
35618  /*
35619  * Based on:
35620  * Ext JS Library 1.1.1
35621  * Copyright(c) 2006-2007, Ext JS, LLC.
35622  *
35623  * Originally Released Under LGPL - original licence link has changed is not relivant.
35624  *
35625  * Fork - LGPL
35626  * <script type="text/javascript">
35627  */
35628  
35629 /**
35630  * @class Roo.bootstrap.layout.Basic
35631  * @extends Roo.util.Observable
35632  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35633  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35634  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35635  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35636  * @cfg {string}   region  the region that it inhabits..
35637  * @cfg {bool}   skipConfig skip config?
35638  * 
35639
35640  */
35641 Roo.bootstrap.layout.Basic = function(config){
35642     
35643     this.mgr = config.mgr;
35644     
35645     this.position = config.region;
35646     
35647     var skipConfig = config.skipConfig;
35648     
35649     this.events = {
35650         /**
35651          * @scope Roo.BasicLayoutRegion
35652          */
35653         
35654         /**
35655          * @event beforeremove
35656          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35657          * @param {Roo.LayoutRegion} this
35658          * @param {Roo.ContentPanel} panel The panel
35659          * @param {Object} e The cancel event object
35660          */
35661         "beforeremove" : true,
35662         /**
35663          * @event invalidated
35664          * Fires when the layout for this region is changed.
35665          * @param {Roo.LayoutRegion} this
35666          */
35667         "invalidated" : true,
35668         /**
35669          * @event visibilitychange
35670          * Fires when this region is shown or hidden 
35671          * @param {Roo.LayoutRegion} this
35672          * @param {Boolean} visibility true or false
35673          */
35674         "visibilitychange" : true,
35675         /**
35676          * @event paneladded
35677          * Fires when a panel is added. 
35678          * @param {Roo.LayoutRegion} this
35679          * @param {Roo.ContentPanel} panel The panel
35680          */
35681         "paneladded" : true,
35682         /**
35683          * @event panelremoved
35684          * Fires when a panel is removed. 
35685          * @param {Roo.LayoutRegion} this
35686          * @param {Roo.ContentPanel} panel The panel
35687          */
35688         "panelremoved" : true,
35689         /**
35690          * @event beforecollapse
35691          * Fires when this region before collapse.
35692          * @param {Roo.LayoutRegion} this
35693          */
35694         "beforecollapse" : true,
35695         /**
35696          * @event collapsed
35697          * Fires when this region is collapsed.
35698          * @param {Roo.LayoutRegion} this
35699          */
35700         "collapsed" : true,
35701         /**
35702          * @event expanded
35703          * Fires when this region is expanded.
35704          * @param {Roo.LayoutRegion} this
35705          */
35706         "expanded" : true,
35707         /**
35708          * @event slideshow
35709          * Fires when this region is slid into view.
35710          * @param {Roo.LayoutRegion} this
35711          */
35712         "slideshow" : true,
35713         /**
35714          * @event slidehide
35715          * Fires when this region slides out of view. 
35716          * @param {Roo.LayoutRegion} this
35717          */
35718         "slidehide" : true,
35719         /**
35720          * @event panelactivated
35721          * Fires when a panel is activated. 
35722          * @param {Roo.LayoutRegion} this
35723          * @param {Roo.ContentPanel} panel The activated panel
35724          */
35725         "panelactivated" : true,
35726         /**
35727          * @event resized
35728          * Fires when the user resizes this region. 
35729          * @param {Roo.LayoutRegion} this
35730          * @param {Number} newSize The new size (width for east/west, height for north/south)
35731          */
35732         "resized" : true
35733     };
35734     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35735     this.panels = new Roo.util.MixedCollection();
35736     this.panels.getKey = this.getPanelId.createDelegate(this);
35737     this.box = null;
35738     this.activePanel = null;
35739     // ensure listeners are added...
35740     
35741     if (config.listeners || config.events) {
35742         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35743             listeners : config.listeners || {},
35744             events : config.events || {}
35745         });
35746     }
35747     
35748     if(skipConfig !== true){
35749         this.applyConfig(config);
35750     }
35751 };
35752
35753 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35754 {
35755     getPanelId : function(p){
35756         return p.getId();
35757     },
35758     
35759     applyConfig : function(config){
35760         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35761         this.config = config;
35762         
35763     },
35764     
35765     /**
35766      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35767      * the width, for horizontal (north, south) the height.
35768      * @param {Number} newSize The new width or height
35769      */
35770     resizeTo : function(newSize){
35771         var el = this.el ? this.el :
35772                  (this.activePanel ? this.activePanel.getEl() : null);
35773         if(el){
35774             switch(this.position){
35775                 case "east":
35776                 case "west":
35777                     el.setWidth(newSize);
35778                     this.fireEvent("resized", this, newSize);
35779                 break;
35780                 case "north":
35781                 case "south":
35782                     el.setHeight(newSize);
35783                     this.fireEvent("resized", this, newSize);
35784                 break;                
35785             }
35786         }
35787     },
35788     
35789     getBox : function(){
35790         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35791     },
35792     
35793     getMargins : function(){
35794         return this.margins;
35795     },
35796     
35797     updateBox : function(box){
35798         this.box = box;
35799         var el = this.activePanel.getEl();
35800         el.dom.style.left = box.x + "px";
35801         el.dom.style.top = box.y + "px";
35802         this.activePanel.setSize(box.width, box.height);
35803     },
35804     
35805     /**
35806      * Returns the container element for this region.
35807      * @return {Roo.Element}
35808      */
35809     getEl : function(){
35810         return this.activePanel;
35811     },
35812     
35813     /**
35814      * Returns true if this region is currently visible.
35815      * @return {Boolean}
35816      */
35817     isVisible : function(){
35818         return this.activePanel ? true : false;
35819     },
35820     
35821     setActivePanel : function(panel){
35822         panel = this.getPanel(panel);
35823         if(this.activePanel && this.activePanel != panel){
35824             this.activePanel.setActiveState(false);
35825             this.activePanel.getEl().setLeftTop(-10000,-10000);
35826         }
35827         this.activePanel = panel;
35828         panel.setActiveState(true);
35829         if(this.box){
35830             panel.setSize(this.box.width, this.box.height);
35831         }
35832         this.fireEvent("panelactivated", this, panel);
35833         this.fireEvent("invalidated");
35834     },
35835     
35836     /**
35837      * Show the specified panel.
35838      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35839      * @return {Roo.ContentPanel} The shown panel or null
35840      */
35841     showPanel : function(panel){
35842         panel = this.getPanel(panel);
35843         if(panel){
35844             this.setActivePanel(panel);
35845         }
35846         return panel;
35847     },
35848     
35849     /**
35850      * Get the active panel for this region.
35851      * @return {Roo.ContentPanel} The active panel or null
35852      */
35853     getActivePanel : function(){
35854         return this.activePanel;
35855     },
35856     
35857     /**
35858      * Add the passed ContentPanel(s)
35859      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35860      * @return {Roo.ContentPanel} The panel added (if only one was added)
35861      */
35862     add : function(panel){
35863         if(arguments.length > 1){
35864             for(var i = 0, len = arguments.length; i < len; i++) {
35865                 this.add(arguments[i]);
35866             }
35867             return null;
35868         }
35869         if(this.hasPanel(panel)){
35870             this.showPanel(panel);
35871             return panel;
35872         }
35873         var el = panel.getEl();
35874         if(el.dom.parentNode != this.mgr.el.dom){
35875             this.mgr.el.dom.appendChild(el.dom);
35876         }
35877         if(panel.setRegion){
35878             panel.setRegion(this);
35879         }
35880         this.panels.add(panel);
35881         el.setStyle("position", "absolute");
35882         if(!panel.background){
35883             this.setActivePanel(panel);
35884             if(this.config.initialSize && this.panels.getCount()==1){
35885                 this.resizeTo(this.config.initialSize);
35886             }
35887         }
35888         this.fireEvent("paneladded", this, panel);
35889         return panel;
35890     },
35891     
35892     /**
35893      * Returns true if the panel is in this region.
35894      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35895      * @return {Boolean}
35896      */
35897     hasPanel : function(panel){
35898         if(typeof panel == "object"){ // must be panel obj
35899             panel = panel.getId();
35900         }
35901         return this.getPanel(panel) ? true : false;
35902     },
35903     
35904     /**
35905      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35906      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35907      * @param {Boolean} preservePanel Overrides the config preservePanel option
35908      * @return {Roo.ContentPanel} The panel that was removed
35909      */
35910     remove : function(panel, preservePanel){
35911         panel = this.getPanel(panel);
35912         if(!panel){
35913             return null;
35914         }
35915         var e = {};
35916         this.fireEvent("beforeremove", this, panel, e);
35917         if(e.cancel === true){
35918             return null;
35919         }
35920         var panelId = panel.getId();
35921         this.panels.removeKey(panelId);
35922         return panel;
35923     },
35924     
35925     /**
35926      * Returns the panel specified or null if it's not in this region.
35927      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35928      * @return {Roo.ContentPanel}
35929      */
35930     getPanel : function(id){
35931         if(typeof id == "object"){ // must be panel obj
35932             return id;
35933         }
35934         return this.panels.get(id);
35935     },
35936     
35937     /**
35938      * Returns this regions position (north/south/east/west/center).
35939      * @return {String} 
35940      */
35941     getPosition: function(){
35942         return this.position;    
35943     }
35944 });/*
35945  * Based on:
35946  * Ext JS Library 1.1.1
35947  * Copyright(c) 2006-2007, Ext JS, LLC.
35948  *
35949  * Originally Released Under LGPL - original licence link has changed is not relivant.
35950  *
35951  * Fork - LGPL
35952  * <script type="text/javascript">
35953  */
35954  
35955 /**
35956  * @class Roo.bootstrap.layout.Region
35957  * @extends Roo.bootstrap.layout.Basic
35958  * This class represents a region in a layout manager.
35959  
35960  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35961  * @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})
35962  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35963  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35964  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35965  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35966  * @cfg {String}    title           The title for the region (overrides panel titles)
35967  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35968  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35969  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35970  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35971  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35972  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35973  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35974  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35975  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35976  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35977
35978  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35979  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35980  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35981  * @cfg {Number}    width           For East/West panels
35982  * @cfg {Number}    height          For North/South panels
35983  * @cfg {Boolean}   split           To show the splitter
35984  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35985  * 
35986  * @cfg {string}   cls             Extra CSS classes to add to region
35987  * 
35988  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35989  * @cfg {string}   region  the region that it inhabits..
35990  *
35991
35992  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35993  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35994
35995  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35996  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35997  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35998  */
35999 Roo.bootstrap.layout.Region = function(config)
36000 {
36001     this.applyConfig(config);
36002
36003     var mgr = config.mgr;
36004     var pos = config.region;
36005     config.skipConfig = true;
36006     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
36007     
36008     if (mgr.el) {
36009         this.onRender(mgr.el);   
36010     }
36011      
36012     this.visible = true;
36013     this.collapsed = false;
36014     this.unrendered_panels = [];
36015 };
36016
36017 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
36018
36019     position: '', // set by wrapper (eg. north/south etc..)
36020     unrendered_panels : null,  // unrendered panels.
36021     
36022     tabPosition : false,
36023     
36024     mgr: false, // points to 'Border'
36025     
36026     
36027     createBody : function(){
36028         /** This region's body element 
36029         * @type Roo.Element */
36030         this.bodyEl = this.el.createChild({
36031                 tag: "div",
36032                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
36033         });
36034     },
36035
36036     onRender: function(ctr, pos)
36037     {
36038         var dh = Roo.DomHelper;
36039         /** This region's container element 
36040         * @type Roo.Element */
36041         this.el = dh.append(ctr.dom, {
36042                 tag: "div",
36043                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
36044             }, true);
36045         /** This region's title element 
36046         * @type Roo.Element */
36047     
36048         this.titleEl = dh.append(this.el.dom,  {
36049                 tag: "div",
36050                 unselectable: "on",
36051                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
36052                 children:[
36053                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
36054                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
36055                 ]
36056             }, true);
36057         
36058         this.titleEl.enableDisplayMode();
36059         /** This region's title text element 
36060         * @type HTMLElement */
36061         this.titleTextEl = this.titleEl.dom.firstChild;
36062         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
36063         /*
36064         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
36065         this.closeBtn.enableDisplayMode();
36066         this.closeBtn.on("click", this.closeClicked, this);
36067         this.closeBtn.hide();
36068     */
36069         this.createBody(this.config);
36070         if(this.config.hideWhenEmpty){
36071             this.hide();
36072             this.on("paneladded", this.validateVisibility, this);
36073             this.on("panelremoved", this.validateVisibility, this);
36074         }
36075         if(this.autoScroll){
36076             this.bodyEl.setStyle("overflow", "auto");
36077         }else{
36078             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
36079         }
36080         //if(c.titlebar !== false){
36081             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
36082                 this.titleEl.hide();
36083             }else{
36084                 this.titleEl.show();
36085                 if(this.config.title){
36086                     this.titleTextEl.innerHTML = this.config.title;
36087                 }
36088             }
36089         //}
36090         if(this.config.collapsed){
36091             this.collapse(true);
36092         }
36093         if(this.config.hidden){
36094             this.hide();
36095         }
36096         
36097         if (this.unrendered_panels && this.unrendered_panels.length) {
36098             for (var i =0;i< this.unrendered_panels.length; i++) {
36099                 this.add(this.unrendered_panels[i]);
36100             }
36101             this.unrendered_panels = null;
36102             
36103         }
36104         
36105     },
36106     
36107     applyConfig : function(c)
36108     {
36109         /*
36110          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
36111             var dh = Roo.DomHelper;
36112             if(c.titlebar !== false){
36113                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
36114                 this.collapseBtn.on("click", this.collapse, this);
36115                 this.collapseBtn.enableDisplayMode();
36116                 /*
36117                 if(c.showPin === true || this.showPin){
36118                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
36119                     this.stickBtn.enableDisplayMode();
36120                     this.stickBtn.on("click", this.expand, this);
36121                     this.stickBtn.hide();
36122                 }
36123                 
36124             }
36125             */
36126             /** This region's collapsed element
36127             * @type Roo.Element */
36128             /*
36129              *
36130             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
36131                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
36132             ]}, true);
36133             
36134             if(c.floatable !== false){
36135                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
36136                this.collapsedEl.on("click", this.collapseClick, this);
36137             }
36138
36139             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
36140                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
36141                    id: "message", unselectable: "on", style:{"float":"left"}});
36142                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
36143              }
36144             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
36145             this.expandBtn.on("click", this.expand, this);
36146             
36147         }
36148         
36149         if(this.collapseBtn){
36150             this.collapseBtn.setVisible(c.collapsible == true);
36151         }
36152         
36153         this.cmargins = c.cmargins || this.cmargins ||
36154                          (this.position == "west" || this.position == "east" ?
36155                              {top: 0, left: 2, right:2, bottom: 0} :
36156                              {top: 2, left: 0, right:0, bottom: 2});
36157         */
36158         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36159         
36160         
36161         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
36162         
36163         this.autoScroll = c.autoScroll || false;
36164         
36165         
36166        
36167         
36168         this.duration = c.duration || .30;
36169         this.slideDuration = c.slideDuration || .45;
36170         this.config = c;
36171        
36172     },
36173     /**
36174      * Returns true if this region is currently visible.
36175      * @return {Boolean}
36176      */
36177     isVisible : function(){
36178         return this.visible;
36179     },
36180
36181     /**
36182      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
36183      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
36184      */
36185     //setCollapsedTitle : function(title){
36186     //    title = title || "&#160;";
36187      //   if(this.collapsedTitleTextEl){
36188       //      this.collapsedTitleTextEl.innerHTML = title;
36189        // }
36190     //},
36191
36192     getBox : function(){
36193         var b;
36194       //  if(!this.collapsed){
36195             b = this.el.getBox(false, true);
36196        // }else{
36197           //  b = this.collapsedEl.getBox(false, true);
36198         //}
36199         return b;
36200     },
36201
36202     getMargins : function(){
36203         return this.margins;
36204         //return this.collapsed ? this.cmargins : this.margins;
36205     },
36206 /*
36207     highlight : function(){
36208         this.el.addClass("x-layout-panel-dragover");
36209     },
36210
36211     unhighlight : function(){
36212         this.el.removeClass("x-layout-panel-dragover");
36213     },
36214 */
36215     updateBox : function(box)
36216     {
36217         if (!this.bodyEl) {
36218             return; // not rendered yet..
36219         }
36220         
36221         this.box = box;
36222         if(!this.collapsed){
36223             this.el.dom.style.left = box.x + "px";
36224             this.el.dom.style.top = box.y + "px";
36225             this.updateBody(box.width, box.height);
36226         }else{
36227             this.collapsedEl.dom.style.left = box.x + "px";
36228             this.collapsedEl.dom.style.top = box.y + "px";
36229             this.collapsedEl.setSize(box.width, box.height);
36230         }
36231         if(this.tabs){
36232             this.tabs.autoSizeTabs();
36233         }
36234     },
36235
36236     updateBody : function(w, h)
36237     {
36238         if(w !== null){
36239             this.el.setWidth(w);
36240             w -= this.el.getBorderWidth("rl");
36241             if(this.config.adjustments){
36242                 w += this.config.adjustments[0];
36243             }
36244         }
36245         if(h !== null && h > 0){
36246             this.el.setHeight(h);
36247             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36248             h -= this.el.getBorderWidth("tb");
36249             if(this.config.adjustments){
36250                 h += this.config.adjustments[1];
36251             }
36252             this.bodyEl.setHeight(h);
36253             if(this.tabs){
36254                 h = this.tabs.syncHeight(h);
36255             }
36256         }
36257         if(this.panelSize){
36258             w = w !== null ? w : this.panelSize.width;
36259             h = h !== null ? h : this.panelSize.height;
36260         }
36261         if(this.activePanel){
36262             var el = this.activePanel.getEl();
36263             w = w !== null ? w : el.getWidth();
36264             h = h !== null ? h : el.getHeight();
36265             this.panelSize = {width: w, height: h};
36266             this.activePanel.setSize(w, h);
36267         }
36268         if(Roo.isIE && this.tabs){
36269             this.tabs.el.repaint();
36270         }
36271     },
36272
36273     /**
36274      * Returns the container element for this region.
36275      * @return {Roo.Element}
36276      */
36277     getEl : function(){
36278         return this.el;
36279     },
36280
36281     /**
36282      * Hides this region.
36283      */
36284     hide : function(){
36285         //if(!this.collapsed){
36286             this.el.dom.style.left = "-2000px";
36287             this.el.hide();
36288         //}else{
36289          //   this.collapsedEl.dom.style.left = "-2000px";
36290          //   this.collapsedEl.hide();
36291        // }
36292         this.visible = false;
36293         this.fireEvent("visibilitychange", this, false);
36294     },
36295
36296     /**
36297      * Shows this region if it was previously hidden.
36298      */
36299     show : function(){
36300         //if(!this.collapsed){
36301             this.el.show();
36302         //}else{
36303         //    this.collapsedEl.show();
36304        // }
36305         this.visible = true;
36306         this.fireEvent("visibilitychange", this, true);
36307     },
36308 /*
36309     closeClicked : function(){
36310         if(this.activePanel){
36311             this.remove(this.activePanel);
36312         }
36313     },
36314
36315     collapseClick : function(e){
36316         if(this.isSlid){
36317            e.stopPropagation();
36318            this.slideIn();
36319         }else{
36320            e.stopPropagation();
36321            this.slideOut();
36322         }
36323     },
36324 */
36325     /**
36326      * Collapses this region.
36327      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36328      */
36329     /*
36330     collapse : function(skipAnim, skipCheck = false){
36331         if(this.collapsed) {
36332             return;
36333         }
36334         
36335         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36336             
36337             this.collapsed = true;
36338             if(this.split){
36339                 this.split.el.hide();
36340             }
36341             if(this.config.animate && skipAnim !== true){
36342                 this.fireEvent("invalidated", this);
36343                 this.animateCollapse();
36344             }else{
36345                 this.el.setLocation(-20000,-20000);
36346                 this.el.hide();
36347                 this.collapsedEl.show();
36348                 this.fireEvent("collapsed", this);
36349                 this.fireEvent("invalidated", this);
36350             }
36351         }
36352         
36353     },
36354 */
36355     animateCollapse : function(){
36356         // overridden
36357     },
36358
36359     /**
36360      * Expands this region if it was previously collapsed.
36361      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36362      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36363      */
36364     /*
36365     expand : function(e, skipAnim){
36366         if(e) {
36367             e.stopPropagation();
36368         }
36369         if(!this.collapsed || this.el.hasActiveFx()) {
36370             return;
36371         }
36372         if(this.isSlid){
36373             this.afterSlideIn();
36374             skipAnim = true;
36375         }
36376         this.collapsed = false;
36377         if(this.config.animate && skipAnim !== true){
36378             this.animateExpand();
36379         }else{
36380             this.el.show();
36381             if(this.split){
36382                 this.split.el.show();
36383             }
36384             this.collapsedEl.setLocation(-2000,-2000);
36385             this.collapsedEl.hide();
36386             this.fireEvent("invalidated", this);
36387             this.fireEvent("expanded", this);
36388         }
36389     },
36390 */
36391     animateExpand : function(){
36392         // overridden
36393     },
36394
36395     initTabs : function()
36396     {
36397         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36398         
36399         var ts = new Roo.bootstrap.panel.Tabs({
36400             el: this.bodyEl.dom,
36401             region : this,
36402             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
36403             disableTooltips: this.config.disableTabTips,
36404             toolbar : this.config.toolbar
36405         });
36406         
36407         if(this.config.hideTabs){
36408             ts.stripWrap.setDisplayed(false);
36409         }
36410         this.tabs = ts;
36411         ts.resizeTabs = this.config.resizeTabs === true;
36412         ts.minTabWidth = this.config.minTabWidth || 40;
36413         ts.maxTabWidth = this.config.maxTabWidth || 250;
36414         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36415         ts.monitorResize = false;
36416         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36417         ts.bodyEl.addClass('roo-layout-tabs-body');
36418         this.panels.each(this.initPanelAsTab, this);
36419     },
36420
36421     initPanelAsTab : function(panel){
36422         var ti = this.tabs.addTab(
36423             panel.getEl().id,
36424             panel.getTitle(),
36425             null,
36426             this.config.closeOnTab && panel.isClosable(),
36427             panel.tpl
36428         );
36429         if(panel.tabTip !== undefined){
36430             ti.setTooltip(panel.tabTip);
36431         }
36432         ti.on("activate", function(){
36433               this.setActivePanel(panel);
36434         }, this);
36435         
36436         if(this.config.closeOnTab){
36437             ti.on("beforeclose", function(t, e){
36438                 e.cancel = true;
36439                 this.remove(panel);
36440             }, this);
36441         }
36442         
36443         panel.tabItem = ti;
36444         
36445         return ti;
36446     },
36447
36448     updatePanelTitle : function(panel, title)
36449     {
36450         if(this.activePanel == panel){
36451             this.updateTitle(title);
36452         }
36453         if(this.tabs){
36454             var ti = this.tabs.getTab(panel.getEl().id);
36455             ti.setText(title);
36456             if(panel.tabTip !== undefined){
36457                 ti.setTooltip(panel.tabTip);
36458             }
36459         }
36460     },
36461
36462     updateTitle : function(title){
36463         if(this.titleTextEl && !this.config.title){
36464             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36465         }
36466     },
36467
36468     setActivePanel : function(panel)
36469     {
36470         panel = this.getPanel(panel);
36471         if(this.activePanel && this.activePanel != panel){
36472             if(this.activePanel.setActiveState(false) === false){
36473                 return;
36474             }
36475         }
36476         this.activePanel = panel;
36477         panel.setActiveState(true);
36478         if(this.panelSize){
36479             panel.setSize(this.panelSize.width, this.panelSize.height);
36480         }
36481         if(this.closeBtn){
36482             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36483         }
36484         this.updateTitle(panel.getTitle());
36485         if(this.tabs){
36486             this.fireEvent("invalidated", this);
36487         }
36488         this.fireEvent("panelactivated", this, panel);
36489     },
36490
36491     /**
36492      * Shows the specified panel.
36493      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36494      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36495      */
36496     showPanel : function(panel)
36497     {
36498         panel = this.getPanel(panel);
36499         if(panel){
36500             if(this.tabs){
36501                 var tab = this.tabs.getTab(panel.getEl().id);
36502                 if(tab.isHidden()){
36503                     this.tabs.unhideTab(tab.id);
36504                 }
36505                 tab.activate();
36506             }else{
36507                 this.setActivePanel(panel);
36508             }
36509         }
36510         return panel;
36511     },
36512
36513     /**
36514      * Get the active panel for this region.
36515      * @return {Roo.ContentPanel} The active panel or null
36516      */
36517     getActivePanel : function(){
36518         return this.activePanel;
36519     },
36520
36521     validateVisibility : function(){
36522         if(this.panels.getCount() < 1){
36523             this.updateTitle("&#160;");
36524             this.closeBtn.hide();
36525             this.hide();
36526         }else{
36527             if(!this.isVisible()){
36528                 this.show();
36529             }
36530         }
36531     },
36532
36533     /**
36534      * Adds the passed ContentPanel(s) to this region.
36535      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36536      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36537      */
36538     add : function(panel)
36539     {
36540         if(arguments.length > 1){
36541             for(var i = 0, len = arguments.length; i < len; i++) {
36542                 this.add(arguments[i]);
36543             }
36544             return null;
36545         }
36546         
36547         // if we have not been rendered yet, then we can not really do much of this..
36548         if (!this.bodyEl) {
36549             this.unrendered_panels.push(panel);
36550             return panel;
36551         }
36552         
36553         
36554         
36555         
36556         if(this.hasPanel(panel)){
36557             this.showPanel(panel);
36558             return panel;
36559         }
36560         panel.setRegion(this);
36561         this.panels.add(panel);
36562        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36563             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36564             // and hide them... ???
36565             this.bodyEl.dom.appendChild(panel.getEl().dom);
36566             if(panel.background !== true){
36567                 this.setActivePanel(panel);
36568             }
36569             this.fireEvent("paneladded", this, panel);
36570             return panel;
36571         }
36572         */
36573         if(!this.tabs){
36574             this.initTabs();
36575         }else{
36576             this.initPanelAsTab(panel);
36577         }
36578         
36579         
36580         if(panel.background !== true){
36581             this.tabs.activate(panel.getEl().id);
36582         }
36583         this.fireEvent("paneladded", this, panel);
36584         return panel;
36585     },
36586
36587     /**
36588      * Hides the tab for the specified panel.
36589      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36590      */
36591     hidePanel : function(panel){
36592         if(this.tabs && (panel = this.getPanel(panel))){
36593             this.tabs.hideTab(panel.getEl().id);
36594         }
36595     },
36596
36597     /**
36598      * Unhides the tab for a previously hidden panel.
36599      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36600      */
36601     unhidePanel : function(panel){
36602         if(this.tabs && (panel = this.getPanel(panel))){
36603             this.tabs.unhideTab(panel.getEl().id);
36604         }
36605     },
36606
36607     clearPanels : function(){
36608         while(this.panels.getCount() > 0){
36609              this.remove(this.panels.first());
36610         }
36611     },
36612
36613     /**
36614      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36615      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36616      * @param {Boolean} preservePanel Overrides the config preservePanel option
36617      * @return {Roo.ContentPanel} The panel that was removed
36618      */
36619     remove : function(panel, preservePanel)
36620     {
36621         panel = this.getPanel(panel);
36622         if(!panel){
36623             return null;
36624         }
36625         var e = {};
36626         this.fireEvent("beforeremove", this, panel, e);
36627         if(e.cancel === true){
36628             return null;
36629         }
36630         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36631         var panelId = panel.getId();
36632         this.panels.removeKey(panelId);
36633         if(preservePanel){
36634             document.body.appendChild(panel.getEl().dom);
36635         }
36636         if(this.tabs){
36637             this.tabs.removeTab(panel.getEl().id);
36638         }else if (!preservePanel){
36639             this.bodyEl.dom.removeChild(panel.getEl().dom);
36640         }
36641         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36642             var p = this.panels.first();
36643             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36644             tempEl.appendChild(p.getEl().dom);
36645             this.bodyEl.update("");
36646             this.bodyEl.dom.appendChild(p.getEl().dom);
36647             tempEl = null;
36648             this.updateTitle(p.getTitle());
36649             this.tabs = null;
36650             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36651             this.setActivePanel(p);
36652         }
36653         panel.setRegion(null);
36654         if(this.activePanel == panel){
36655             this.activePanel = null;
36656         }
36657         if(this.config.autoDestroy !== false && preservePanel !== true){
36658             try{panel.destroy();}catch(e){}
36659         }
36660         this.fireEvent("panelremoved", this, panel);
36661         return panel;
36662     },
36663
36664     /**
36665      * Returns the TabPanel component used by this region
36666      * @return {Roo.TabPanel}
36667      */
36668     getTabs : function(){
36669         return this.tabs;
36670     },
36671
36672     createTool : function(parentEl, className){
36673         var btn = Roo.DomHelper.append(parentEl, {
36674             tag: "div",
36675             cls: "x-layout-tools-button",
36676             children: [ {
36677                 tag: "div",
36678                 cls: "roo-layout-tools-button-inner " + className,
36679                 html: "&#160;"
36680             }]
36681         }, true);
36682         btn.addClassOnOver("roo-layout-tools-button-over");
36683         return btn;
36684     }
36685 });/*
36686  * Based on:
36687  * Ext JS Library 1.1.1
36688  * Copyright(c) 2006-2007, Ext JS, LLC.
36689  *
36690  * Originally Released Under LGPL - original licence link has changed is not relivant.
36691  *
36692  * Fork - LGPL
36693  * <script type="text/javascript">
36694  */
36695  
36696
36697
36698 /**
36699  * @class Roo.SplitLayoutRegion
36700  * @extends Roo.LayoutRegion
36701  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36702  */
36703 Roo.bootstrap.layout.Split = function(config){
36704     this.cursor = config.cursor;
36705     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36706 };
36707
36708 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36709 {
36710     splitTip : "Drag to resize.",
36711     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36712     useSplitTips : false,
36713
36714     applyConfig : function(config){
36715         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36716     },
36717     
36718     onRender : function(ctr,pos) {
36719         
36720         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36721         if(!this.config.split){
36722             return;
36723         }
36724         if(!this.split){
36725             
36726             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36727                             tag: "div",
36728                             id: this.el.id + "-split",
36729                             cls: "roo-layout-split roo-layout-split-"+this.position,
36730                             html: "&#160;"
36731             });
36732             /** The SplitBar for this region 
36733             * @type Roo.SplitBar */
36734             // does not exist yet...
36735             Roo.log([this.position, this.orientation]);
36736             
36737             this.split = new Roo.bootstrap.SplitBar({
36738                 dragElement : splitEl,
36739                 resizingElement: this.el,
36740                 orientation : this.orientation
36741             });
36742             
36743             this.split.on("moved", this.onSplitMove, this);
36744             this.split.useShim = this.config.useShim === true;
36745             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36746             if(this.useSplitTips){
36747                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36748             }
36749             //if(config.collapsible){
36750             //    this.split.el.on("dblclick", this.collapse,  this);
36751             //}
36752         }
36753         if(typeof this.config.minSize != "undefined"){
36754             this.split.minSize = this.config.minSize;
36755         }
36756         if(typeof this.config.maxSize != "undefined"){
36757             this.split.maxSize = this.config.maxSize;
36758         }
36759         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36760             this.hideSplitter();
36761         }
36762         
36763     },
36764
36765     getHMaxSize : function(){
36766          var cmax = this.config.maxSize || 10000;
36767          var center = this.mgr.getRegion("center");
36768          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36769     },
36770
36771     getVMaxSize : function(){
36772          var cmax = this.config.maxSize || 10000;
36773          var center = this.mgr.getRegion("center");
36774          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36775     },
36776
36777     onSplitMove : function(split, newSize){
36778         this.fireEvent("resized", this, newSize);
36779     },
36780     
36781     /** 
36782      * Returns the {@link Roo.SplitBar} for this region.
36783      * @return {Roo.SplitBar}
36784      */
36785     getSplitBar : function(){
36786         return this.split;
36787     },
36788     
36789     hide : function(){
36790         this.hideSplitter();
36791         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36792     },
36793
36794     hideSplitter : function(){
36795         if(this.split){
36796             this.split.el.setLocation(-2000,-2000);
36797             this.split.el.hide();
36798         }
36799     },
36800
36801     show : function(){
36802         if(this.split){
36803             this.split.el.show();
36804         }
36805         Roo.bootstrap.layout.Split.superclass.show.call(this);
36806     },
36807     
36808     beforeSlide: function(){
36809         if(Roo.isGecko){// firefox overflow auto bug workaround
36810             this.bodyEl.clip();
36811             if(this.tabs) {
36812                 this.tabs.bodyEl.clip();
36813             }
36814             if(this.activePanel){
36815                 this.activePanel.getEl().clip();
36816                 
36817                 if(this.activePanel.beforeSlide){
36818                     this.activePanel.beforeSlide();
36819                 }
36820             }
36821         }
36822     },
36823     
36824     afterSlide : function(){
36825         if(Roo.isGecko){// firefox overflow auto bug workaround
36826             this.bodyEl.unclip();
36827             if(this.tabs) {
36828                 this.tabs.bodyEl.unclip();
36829             }
36830             if(this.activePanel){
36831                 this.activePanel.getEl().unclip();
36832                 if(this.activePanel.afterSlide){
36833                     this.activePanel.afterSlide();
36834                 }
36835             }
36836         }
36837     },
36838
36839     initAutoHide : function(){
36840         if(this.autoHide !== false){
36841             if(!this.autoHideHd){
36842                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36843                 this.autoHideHd = {
36844                     "mouseout": function(e){
36845                         if(!e.within(this.el, true)){
36846                             st.delay(500);
36847                         }
36848                     },
36849                     "mouseover" : function(e){
36850                         st.cancel();
36851                     },
36852                     scope : this
36853                 };
36854             }
36855             this.el.on(this.autoHideHd);
36856         }
36857     },
36858
36859     clearAutoHide : function(){
36860         if(this.autoHide !== false){
36861             this.el.un("mouseout", this.autoHideHd.mouseout);
36862             this.el.un("mouseover", this.autoHideHd.mouseover);
36863         }
36864     },
36865
36866     clearMonitor : function(){
36867         Roo.get(document).un("click", this.slideInIf, this);
36868     },
36869
36870     // these names are backwards but not changed for compat
36871     slideOut : function(){
36872         if(this.isSlid || this.el.hasActiveFx()){
36873             return;
36874         }
36875         this.isSlid = true;
36876         if(this.collapseBtn){
36877             this.collapseBtn.hide();
36878         }
36879         this.closeBtnState = this.closeBtn.getStyle('display');
36880         this.closeBtn.hide();
36881         if(this.stickBtn){
36882             this.stickBtn.show();
36883         }
36884         this.el.show();
36885         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36886         this.beforeSlide();
36887         this.el.setStyle("z-index", 10001);
36888         this.el.slideIn(this.getSlideAnchor(), {
36889             callback: function(){
36890                 this.afterSlide();
36891                 this.initAutoHide();
36892                 Roo.get(document).on("click", this.slideInIf, this);
36893                 this.fireEvent("slideshow", this);
36894             },
36895             scope: this,
36896             block: true
36897         });
36898     },
36899
36900     afterSlideIn : function(){
36901         this.clearAutoHide();
36902         this.isSlid = false;
36903         this.clearMonitor();
36904         this.el.setStyle("z-index", "");
36905         if(this.collapseBtn){
36906             this.collapseBtn.show();
36907         }
36908         this.closeBtn.setStyle('display', this.closeBtnState);
36909         if(this.stickBtn){
36910             this.stickBtn.hide();
36911         }
36912         this.fireEvent("slidehide", this);
36913     },
36914
36915     slideIn : function(cb){
36916         if(!this.isSlid || this.el.hasActiveFx()){
36917             Roo.callback(cb);
36918             return;
36919         }
36920         this.isSlid = false;
36921         this.beforeSlide();
36922         this.el.slideOut(this.getSlideAnchor(), {
36923             callback: function(){
36924                 this.el.setLeftTop(-10000, -10000);
36925                 this.afterSlide();
36926                 this.afterSlideIn();
36927                 Roo.callback(cb);
36928             },
36929             scope: this,
36930             block: true
36931         });
36932     },
36933     
36934     slideInIf : function(e){
36935         if(!e.within(this.el)){
36936             this.slideIn();
36937         }
36938     },
36939
36940     animateCollapse : function(){
36941         this.beforeSlide();
36942         this.el.setStyle("z-index", 20000);
36943         var anchor = this.getSlideAnchor();
36944         this.el.slideOut(anchor, {
36945             callback : function(){
36946                 this.el.setStyle("z-index", "");
36947                 this.collapsedEl.slideIn(anchor, {duration:.3});
36948                 this.afterSlide();
36949                 this.el.setLocation(-10000,-10000);
36950                 this.el.hide();
36951                 this.fireEvent("collapsed", this);
36952             },
36953             scope: this,
36954             block: true
36955         });
36956     },
36957
36958     animateExpand : function(){
36959         this.beforeSlide();
36960         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36961         this.el.setStyle("z-index", 20000);
36962         this.collapsedEl.hide({
36963             duration:.1
36964         });
36965         this.el.slideIn(this.getSlideAnchor(), {
36966             callback : function(){
36967                 this.el.setStyle("z-index", "");
36968                 this.afterSlide();
36969                 if(this.split){
36970                     this.split.el.show();
36971                 }
36972                 this.fireEvent("invalidated", this);
36973                 this.fireEvent("expanded", this);
36974             },
36975             scope: this,
36976             block: true
36977         });
36978     },
36979
36980     anchors : {
36981         "west" : "left",
36982         "east" : "right",
36983         "north" : "top",
36984         "south" : "bottom"
36985     },
36986
36987     sanchors : {
36988         "west" : "l",
36989         "east" : "r",
36990         "north" : "t",
36991         "south" : "b"
36992     },
36993
36994     canchors : {
36995         "west" : "tl-tr",
36996         "east" : "tr-tl",
36997         "north" : "tl-bl",
36998         "south" : "bl-tl"
36999     },
37000
37001     getAnchor : function(){
37002         return this.anchors[this.position];
37003     },
37004
37005     getCollapseAnchor : function(){
37006         return this.canchors[this.position];
37007     },
37008
37009     getSlideAnchor : function(){
37010         return this.sanchors[this.position];
37011     },
37012
37013     getAlignAdj : function(){
37014         var cm = this.cmargins;
37015         switch(this.position){
37016             case "west":
37017                 return [0, 0];
37018             break;
37019             case "east":
37020                 return [0, 0];
37021             break;
37022             case "north":
37023                 return [0, 0];
37024             break;
37025             case "south":
37026                 return [0, 0];
37027             break;
37028         }
37029     },
37030
37031     getExpandAdj : function(){
37032         var c = this.collapsedEl, cm = this.cmargins;
37033         switch(this.position){
37034             case "west":
37035                 return [-(cm.right+c.getWidth()+cm.left), 0];
37036             break;
37037             case "east":
37038                 return [cm.right+c.getWidth()+cm.left, 0];
37039             break;
37040             case "north":
37041                 return [0, -(cm.top+cm.bottom+c.getHeight())];
37042             break;
37043             case "south":
37044                 return [0, cm.top+cm.bottom+c.getHeight()];
37045             break;
37046         }
37047     }
37048 });/*
37049  * Based on:
37050  * Ext JS Library 1.1.1
37051  * Copyright(c) 2006-2007, Ext JS, LLC.
37052  *
37053  * Originally Released Under LGPL - original licence link has changed is not relivant.
37054  *
37055  * Fork - LGPL
37056  * <script type="text/javascript">
37057  */
37058 /*
37059  * These classes are private internal classes
37060  */
37061 Roo.bootstrap.layout.Center = function(config){
37062     config.region = "center";
37063     Roo.bootstrap.layout.Region.call(this, config);
37064     this.visible = true;
37065     this.minWidth = config.minWidth || 20;
37066     this.minHeight = config.minHeight || 20;
37067 };
37068
37069 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
37070     hide : function(){
37071         // center panel can't be hidden
37072     },
37073     
37074     show : function(){
37075         // center panel can't be hidden
37076     },
37077     
37078     getMinWidth: function(){
37079         return this.minWidth;
37080     },
37081     
37082     getMinHeight: function(){
37083         return this.minHeight;
37084     }
37085 });
37086
37087
37088
37089
37090  
37091
37092
37093
37094
37095
37096
37097 Roo.bootstrap.layout.North = function(config)
37098 {
37099     config.region = 'north';
37100     config.cursor = 'n-resize';
37101     
37102     Roo.bootstrap.layout.Split.call(this, config);
37103     
37104     
37105     if(this.split){
37106         this.split.placement = Roo.bootstrap.SplitBar.TOP;
37107         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37108         this.split.el.addClass("roo-layout-split-v");
37109     }
37110     var size = config.initialSize || config.height;
37111     if(typeof size != "undefined"){
37112         this.el.setHeight(size);
37113     }
37114 };
37115 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
37116 {
37117     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37118     
37119     
37120     
37121     getBox : function(){
37122         if(this.collapsed){
37123             return this.collapsedEl.getBox();
37124         }
37125         var box = this.el.getBox();
37126         if(this.split){
37127             box.height += this.split.el.getHeight();
37128         }
37129         return box;
37130     },
37131     
37132     updateBox : function(box){
37133         if(this.split && !this.collapsed){
37134             box.height -= this.split.el.getHeight();
37135             this.split.el.setLeft(box.x);
37136             this.split.el.setTop(box.y+box.height);
37137             this.split.el.setWidth(box.width);
37138         }
37139         if(this.collapsed){
37140             this.updateBody(box.width, null);
37141         }
37142         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37143     }
37144 });
37145
37146
37147
37148
37149
37150 Roo.bootstrap.layout.South = function(config){
37151     config.region = 'south';
37152     config.cursor = 's-resize';
37153     Roo.bootstrap.layout.Split.call(this, config);
37154     if(this.split){
37155         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
37156         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37157         this.split.el.addClass("roo-layout-split-v");
37158     }
37159     var size = config.initialSize || config.height;
37160     if(typeof size != "undefined"){
37161         this.el.setHeight(size);
37162     }
37163 };
37164
37165 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
37166     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37167     getBox : function(){
37168         if(this.collapsed){
37169             return this.collapsedEl.getBox();
37170         }
37171         var box = this.el.getBox();
37172         if(this.split){
37173             var sh = this.split.el.getHeight();
37174             box.height += sh;
37175             box.y -= sh;
37176         }
37177         return box;
37178     },
37179     
37180     updateBox : function(box){
37181         if(this.split && !this.collapsed){
37182             var sh = this.split.el.getHeight();
37183             box.height -= sh;
37184             box.y += sh;
37185             this.split.el.setLeft(box.x);
37186             this.split.el.setTop(box.y-sh);
37187             this.split.el.setWidth(box.width);
37188         }
37189         if(this.collapsed){
37190             this.updateBody(box.width, null);
37191         }
37192         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37193     }
37194 });
37195
37196 Roo.bootstrap.layout.East = function(config){
37197     config.region = "east";
37198     config.cursor = "e-resize";
37199     Roo.bootstrap.layout.Split.call(this, config);
37200     if(this.split){
37201         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
37202         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37203         this.split.el.addClass("roo-layout-split-h");
37204     }
37205     var size = config.initialSize || config.width;
37206     if(typeof size != "undefined"){
37207         this.el.setWidth(size);
37208     }
37209 };
37210 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
37211     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37212     getBox : function(){
37213         if(this.collapsed){
37214             return this.collapsedEl.getBox();
37215         }
37216         var box = this.el.getBox();
37217         if(this.split){
37218             var sw = this.split.el.getWidth();
37219             box.width += sw;
37220             box.x -= sw;
37221         }
37222         return box;
37223     },
37224
37225     updateBox : function(box){
37226         if(this.split && !this.collapsed){
37227             var sw = this.split.el.getWidth();
37228             box.width -= sw;
37229             this.split.el.setLeft(box.x);
37230             this.split.el.setTop(box.y);
37231             this.split.el.setHeight(box.height);
37232             box.x += sw;
37233         }
37234         if(this.collapsed){
37235             this.updateBody(null, box.height);
37236         }
37237         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37238     }
37239 });
37240
37241 Roo.bootstrap.layout.West = function(config){
37242     config.region = "west";
37243     config.cursor = "w-resize";
37244     
37245     Roo.bootstrap.layout.Split.call(this, config);
37246     if(this.split){
37247         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37248         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37249         this.split.el.addClass("roo-layout-split-h");
37250     }
37251     
37252 };
37253 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37254     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37255     
37256     onRender: function(ctr, pos)
37257     {
37258         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37259         var size = this.config.initialSize || this.config.width;
37260         if(typeof size != "undefined"){
37261             this.el.setWidth(size);
37262         }
37263     },
37264     
37265     getBox : function(){
37266         if(this.collapsed){
37267             return this.collapsedEl.getBox();
37268         }
37269         var box = this.el.getBox();
37270         if(this.split){
37271             box.width += this.split.el.getWidth();
37272         }
37273         return box;
37274     },
37275     
37276     updateBox : function(box){
37277         if(this.split && !this.collapsed){
37278             var sw = this.split.el.getWidth();
37279             box.width -= sw;
37280             this.split.el.setLeft(box.x+box.width);
37281             this.split.el.setTop(box.y);
37282             this.split.el.setHeight(box.height);
37283         }
37284         if(this.collapsed){
37285             this.updateBody(null, box.height);
37286         }
37287         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37288     }
37289 });Roo.namespace("Roo.bootstrap.panel");/*
37290  * Based on:
37291  * Ext JS Library 1.1.1
37292  * Copyright(c) 2006-2007, Ext JS, LLC.
37293  *
37294  * Originally Released Under LGPL - original licence link has changed is not relivant.
37295  *
37296  * Fork - LGPL
37297  * <script type="text/javascript">
37298  */
37299 /**
37300  * @class Roo.ContentPanel
37301  * @extends Roo.util.Observable
37302  * A basic ContentPanel element.
37303  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
37304  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
37305  * @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
37306  * @cfg {Boolean}   closable      True if the panel can be closed/removed
37307  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
37308  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37309  * @cfg {Toolbar}   toolbar       A toolbar for this panel
37310  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
37311  * @cfg {String} title          The title for this panel
37312  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37313  * @cfg {String} url            Calls {@link #setUrl} with this value
37314  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37315  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37316  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37317  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37318  * @cfg {Boolean} badges render the badges
37319
37320  * @constructor
37321  * Create a new ContentPanel.
37322  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37323  * @param {String/Object} config A string to set only the title or a config object
37324  * @param {String} content (optional) Set the HTML content for this panel
37325  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37326  */
37327 Roo.bootstrap.panel.Content = function( config){
37328     
37329     this.tpl = config.tpl || false;
37330     
37331     var el = config.el;
37332     var content = config.content;
37333
37334     if(config.autoCreate){ // xtype is available if this is called from factory
37335         el = Roo.id();
37336     }
37337     this.el = Roo.get(el);
37338     if(!this.el && config && config.autoCreate){
37339         if(typeof config.autoCreate == "object"){
37340             if(!config.autoCreate.id){
37341                 config.autoCreate.id = config.id||el;
37342             }
37343             this.el = Roo.DomHelper.append(document.body,
37344                         config.autoCreate, true);
37345         }else{
37346             var elcfg =  {   tag: "div",
37347                             cls: "roo-layout-inactive-content",
37348                             id: config.id||el
37349                             };
37350             if (config.html) {
37351                 elcfg.html = config.html;
37352                 
37353             }
37354                         
37355             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37356         }
37357     } 
37358     this.closable = false;
37359     this.loaded = false;
37360     this.active = false;
37361    
37362       
37363     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37364         
37365         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37366         
37367         this.wrapEl = this.el; //this.el.wrap();
37368         var ti = [];
37369         if (config.toolbar.items) {
37370             ti = config.toolbar.items ;
37371             delete config.toolbar.items ;
37372         }
37373         
37374         var nitems = [];
37375         this.toolbar.render(this.wrapEl, 'before');
37376         for(var i =0;i < ti.length;i++) {
37377           //  Roo.log(['add child', items[i]]);
37378             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37379         }
37380         this.toolbar.items = nitems;
37381         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37382         delete config.toolbar;
37383         
37384     }
37385     /*
37386     // xtype created footer. - not sure if will work as we normally have to render first..
37387     if (this.footer && !this.footer.el && this.footer.xtype) {
37388         if (!this.wrapEl) {
37389             this.wrapEl = this.el.wrap();
37390         }
37391     
37392         this.footer.container = this.wrapEl.createChild();
37393          
37394         this.footer = Roo.factory(this.footer, Roo);
37395         
37396     }
37397     */
37398     
37399      if(typeof config == "string"){
37400         this.title = config;
37401     }else{
37402         Roo.apply(this, config);
37403     }
37404     
37405     if(this.resizeEl){
37406         this.resizeEl = Roo.get(this.resizeEl, true);
37407     }else{
37408         this.resizeEl = this.el;
37409     }
37410     // handle view.xtype
37411     
37412  
37413     
37414     
37415     this.addEvents({
37416         /**
37417          * @event activate
37418          * Fires when this panel is activated. 
37419          * @param {Roo.ContentPanel} this
37420          */
37421         "activate" : true,
37422         /**
37423          * @event deactivate
37424          * Fires when this panel is activated. 
37425          * @param {Roo.ContentPanel} this
37426          */
37427         "deactivate" : true,
37428
37429         /**
37430          * @event resize
37431          * Fires when this panel is resized if fitToFrame is true.
37432          * @param {Roo.ContentPanel} this
37433          * @param {Number} width The width after any component adjustments
37434          * @param {Number} height The height after any component adjustments
37435          */
37436         "resize" : true,
37437         
37438          /**
37439          * @event render
37440          * Fires when this tab is created
37441          * @param {Roo.ContentPanel} this
37442          */
37443         "render" : true
37444         
37445         
37446         
37447     });
37448     
37449
37450     
37451     
37452     if(this.autoScroll){
37453         this.resizeEl.setStyle("overflow", "auto");
37454     } else {
37455         // fix randome scrolling
37456         //this.el.on('scroll', function() {
37457         //    Roo.log('fix random scolling');
37458         //    this.scrollTo('top',0); 
37459         //});
37460     }
37461     content = content || this.content;
37462     if(content){
37463         this.setContent(content);
37464     }
37465     if(config && config.url){
37466         this.setUrl(this.url, this.params, this.loadOnce);
37467     }
37468     
37469     
37470     
37471     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37472     
37473     if (this.view && typeof(this.view.xtype) != 'undefined') {
37474         this.view.el = this.el.appendChild(document.createElement("div"));
37475         this.view = Roo.factory(this.view); 
37476         this.view.render  &&  this.view.render(false, '');  
37477     }
37478     
37479     
37480     this.fireEvent('render', this);
37481 };
37482
37483 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37484     
37485     tabTip : '',
37486     
37487     setRegion : function(region){
37488         this.region = region;
37489         this.setActiveClass(region && !this.background);
37490     },
37491     
37492     
37493     setActiveClass: function(state)
37494     {
37495         if(state){
37496            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37497            this.el.setStyle('position','relative');
37498         }else{
37499            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37500            this.el.setStyle('position', 'absolute');
37501         } 
37502     },
37503     
37504     /**
37505      * Returns the toolbar for this Panel if one was configured. 
37506      * @return {Roo.Toolbar} 
37507      */
37508     getToolbar : function(){
37509         return this.toolbar;
37510     },
37511     
37512     setActiveState : function(active)
37513     {
37514         this.active = active;
37515         this.setActiveClass(active);
37516         if(!active){
37517             if(this.fireEvent("deactivate", this) === false){
37518                 return false;
37519             }
37520             return true;
37521         }
37522         this.fireEvent("activate", this);
37523         return true;
37524     },
37525     /**
37526      * Updates this panel's element
37527      * @param {String} content The new content
37528      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37529     */
37530     setContent : function(content, loadScripts){
37531         this.el.update(content, loadScripts);
37532     },
37533
37534     ignoreResize : function(w, h){
37535         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37536             return true;
37537         }else{
37538             this.lastSize = {width: w, height: h};
37539             return false;
37540         }
37541     },
37542     /**
37543      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37544      * @return {Roo.UpdateManager} The UpdateManager
37545      */
37546     getUpdateManager : function(){
37547         return this.el.getUpdateManager();
37548     },
37549      /**
37550      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37551      * @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:
37552 <pre><code>
37553 panel.load({
37554     url: "your-url.php",
37555     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37556     callback: yourFunction,
37557     scope: yourObject, //(optional scope)
37558     discardUrl: false,
37559     nocache: false,
37560     text: "Loading...",
37561     timeout: 30,
37562     scripts: false
37563 });
37564 </code></pre>
37565      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37566      * 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.
37567      * @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}
37568      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37569      * @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.
37570      * @return {Roo.ContentPanel} this
37571      */
37572     load : function(){
37573         var um = this.el.getUpdateManager();
37574         um.update.apply(um, arguments);
37575         return this;
37576     },
37577
37578
37579     /**
37580      * 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.
37581      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37582      * @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)
37583      * @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)
37584      * @return {Roo.UpdateManager} The UpdateManager
37585      */
37586     setUrl : function(url, params, loadOnce){
37587         if(this.refreshDelegate){
37588             this.removeListener("activate", this.refreshDelegate);
37589         }
37590         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37591         this.on("activate", this.refreshDelegate);
37592         return this.el.getUpdateManager();
37593     },
37594     
37595     _handleRefresh : function(url, params, loadOnce){
37596         if(!loadOnce || !this.loaded){
37597             var updater = this.el.getUpdateManager();
37598             updater.update(url, params, this._setLoaded.createDelegate(this));
37599         }
37600     },
37601     
37602     _setLoaded : function(){
37603         this.loaded = true;
37604     }, 
37605     
37606     /**
37607      * Returns this panel's id
37608      * @return {String} 
37609      */
37610     getId : function(){
37611         return this.el.id;
37612     },
37613     
37614     /** 
37615      * Returns this panel's element - used by regiosn to add.
37616      * @return {Roo.Element} 
37617      */
37618     getEl : function(){
37619         return this.wrapEl || this.el;
37620     },
37621     
37622    
37623     
37624     adjustForComponents : function(width, height)
37625     {
37626         //Roo.log('adjustForComponents ');
37627         if(this.resizeEl != this.el){
37628             width -= this.el.getFrameWidth('lr');
37629             height -= this.el.getFrameWidth('tb');
37630         }
37631         if(this.toolbar){
37632             var te = this.toolbar.getEl();
37633             te.setWidth(width);
37634             height -= te.getHeight();
37635         }
37636         if(this.footer){
37637             var te = this.footer.getEl();
37638             te.setWidth(width);
37639             height -= te.getHeight();
37640         }
37641         
37642         
37643         if(this.adjustments){
37644             width += this.adjustments[0];
37645             height += this.adjustments[1];
37646         }
37647         return {"width": width, "height": height};
37648     },
37649     
37650     setSize : function(width, height){
37651         if(this.fitToFrame && !this.ignoreResize(width, height)){
37652             if(this.fitContainer && this.resizeEl != this.el){
37653                 this.el.setSize(width, height);
37654             }
37655             var size = this.adjustForComponents(width, height);
37656             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37657             this.fireEvent('resize', this, size.width, size.height);
37658         }
37659     },
37660     
37661     /**
37662      * Returns this panel's title
37663      * @return {String} 
37664      */
37665     getTitle : function(){
37666         
37667         if (typeof(this.title) != 'object') {
37668             return this.title;
37669         }
37670         
37671         var t = '';
37672         for (var k in this.title) {
37673             if (!this.title.hasOwnProperty(k)) {
37674                 continue;
37675             }
37676             
37677             if (k.indexOf('-') >= 0) {
37678                 var s = k.split('-');
37679                 for (var i = 0; i<s.length; i++) {
37680                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37681                 }
37682             } else {
37683                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37684             }
37685         }
37686         return t;
37687     },
37688     
37689     /**
37690      * Set this panel's title
37691      * @param {String} title
37692      */
37693     setTitle : function(title){
37694         this.title = title;
37695         if(this.region){
37696             this.region.updatePanelTitle(this, title);
37697         }
37698     },
37699     
37700     /**
37701      * Returns true is this panel was configured to be closable
37702      * @return {Boolean} 
37703      */
37704     isClosable : function(){
37705         return this.closable;
37706     },
37707     
37708     beforeSlide : function(){
37709         this.el.clip();
37710         this.resizeEl.clip();
37711     },
37712     
37713     afterSlide : function(){
37714         this.el.unclip();
37715         this.resizeEl.unclip();
37716     },
37717     
37718     /**
37719      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37720      *   Will fail silently if the {@link #setUrl} method has not been called.
37721      *   This does not activate the panel, just updates its content.
37722      */
37723     refresh : function(){
37724         if(this.refreshDelegate){
37725            this.loaded = false;
37726            this.refreshDelegate();
37727         }
37728     },
37729     
37730     /**
37731      * Destroys this panel
37732      */
37733     destroy : function(){
37734         this.el.removeAllListeners();
37735         var tempEl = document.createElement("span");
37736         tempEl.appendChild(this.el.dom);
37737         tempEl.innerHTML = "";
37738         this.el.remove();
37739         this.el = null;
37740     },
37741     
37742     /**
37743      * form - if the content panel contains a form - this is a reference to it.
37744      * @type {Roo.form.Form}
37745      */
37746     form : false,
37747     /**
37748      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37749      *    This contains a reference to it.
37750      * @type {Roo.View}
37751      */
37752     view : false,
37753     
37754       /**
37755      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37756      * <pre><code>
37757
37758 layout.addxtype({
37759        xtype : 'Form',
37760        items: [ .... ]
37761    }
37762 );
37763
37764 </code></pre>
37765      * @param {Object} cfg Xtype definition of item to add.
37766      */
37767     
37768     
37769     getChildContainer: function () {
37770         return this.getEl();
37771     }
37772     
37773     
37774     /*
37775         var  ret = new Roo.factory(cfg);
37776         return ret;
37777         
37778         
37779         // add form..
37780         if (cfg.xtype.match(/^Form$/)) {
37781             
37782             var el;
37783             //if (this.footer) {
37784             //    el = this.footer.container.insertSibling(false, 'before');
37785             //} else {
37786                 el = this.el.createChild();
37787             //}
37788
37789             this.form = new  Roo.form.Form(cfg);
37790             
37791             
37792             if ( this.form.allItems.length) {
37793                 this.form.render(el.dom);
37794             }
37795             return this.form;
37796         }
37797         // should only have one of theses..
37798         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37799             // views.. should not be just added - used named prop 'view''
37800             
37801             cfg.el = this.el.appendChild(document.createElement("div"));
37802             // factory?
37803             
37804             var ret = new Roo.factory(cfg);
37805              
37806              ret.render && ret.render(false, ''); // render blank..
37807             this.view = ret;
37808             return ret;
37809         }
37810         return false;
37811     }
37812     \*/
37813 });
37814  
37815 /**
37816  * @class Roo.bootstrap.panel.Grid
37817  * @extends Roo.bootstrap.panel.Content
37818  * @constructor
37819  * Create a new GridPanel.
37820  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37821  * @param {Object} config A the config object
37822   
37823  */
37824
37825
37826
37827 Roo.bootstrap.panel.Grid = function(config)
37828 {
37829     
37830       
37831     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37832         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37833
37834     config.el = this.wrapper;
37835     //this.el = this.wrapper;
37836     
37837       if (config.container) {
37838         // ctor'ed from a Border/panel.grid
37839         
37840         
37841         this.wrapper.setStyle("overflow", "hidden");
37842         this.wrapper.addClass('roo-grid-container');
37843
37844     }
37845     
37846     
37847     if(config.toolbar){
37848         var tool_el = this.wrapper.createChild();    
37849         this.toolbar = Roo.factory(config.toolbar);
37850         var ti = [];
37851         if (config.toolbar.items) {
37852             ti = config.toolbar.items ;
37853             delete config.toolbar.items ;
37854         }
37855         
37856         var nitems = [];
37857         this.toolbar.render(tool_el);
37858         for(var i =0;i < ti.length;i++) {
37859           //  Roo.log(['add child', items[i]]);
37860             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37861         }
37862         this.toolbar.items = nitems;
37863         
37864         delete config.toolbar;
37865     }
37866     
37867     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37868     config.grid.scrollBody = true;;
37869     config.grid.monitorWindowResize = false; // turn off autosizing
37870     config.grid.autoHeight = false;
37871     config.grid.autoWidth = false;
37872     
37873     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37874     
37875     if (config.background) {
37876         // render grid on panel activation (if panel background)
37877         this.on('activate', function(gp) {
37878             if (!gp.grid.rendered) {
37879                 gp.grid.render(this.wrapper);
37880                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37881             }
37882         });
37883             
37884     } else {
37885         this.grid.render(this.wrapper);
37886         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37887
37888     }
37889     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37890     // ??? needed ??? config.el = this.wrapper;
37891     
37892     
37893     
37894   
37895     // xtype created footer. - not sure if will work as we normally have to render first..
37896     if (this.footer && !this.footer.el && this.footer.xtype) {
37897         
37898         var ctr = this.grid.getView().getFooterPanel(true);
37899         this.footer.dataSource = this.grid.dataSource;
37900         this.footer = Roo.factory(this.footer, Roo);
37901         this.footer.render(ctr);
37902         
37903     }
37904     
37905     
37906     
37907     
37908      
37909 };
37910
37911 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37912     getId : function(){
37913         return this.grid.id;
37914     },
37915     
37916     /**
37917      * Returns the grid for this panel
37918      * @return {Roo.bootstrap.Table} 
37919      */
37920     getGrid : function(){
37921         return this.grid;    
37922     },
37923     
37924     setSize : function(width, height){
37925         if(!this.ignoreResize(width, height)){
37926             var grid = this.grid;
37927             var size = this.adjustForComponents(width, height);
37928             var gridel = grid.getGridEl();
37929             gridel.setSize(size.width, size.height);
37930             /*
37931             var thd = grid.getGridEl().select('thead',true).first();
37932             var tbd = grid.getGridEl().select('tbody', true).first();
37933             if (tbd) {
37934                 tbd.setSize(width, height - thd.getHeight());
37935             }
37936             */
37937             grid.autoSize();
37938         }
37939     },
37940      
37941     
37942     
37943     beforeSlide : function(){
37944         this.grid.getView().scroller.clip();
37945     },
37946     
37947     afterSlide : function(){
37948         this.grid.getView().scroller.unclip();
37949     },
37950     
37951     destroy : function(){
37952         this.grid.destroy();
37953         delete this.grid;
37954         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37955     }
37956 });
37957
37958 /**
37959  * @class Roo.bootstrap.panel.Nest
37960  * @extends Roo.bootstrap.panel.Content
37961  * @constructor
37962  * Create a new Panel, that can contain a layout.Border.
37963  * 
37964  * 
37965  * @param {Roo.BorderLayout} layout The layout for this panel
37966  * @param {String/Object} config A string to set only the title or a config object
37967  */
37968 Roo.bootstrap.panel.Nest = function(config)
37969 {
37970     // construct with only one argument..
37971     /* FIXME - implement nicer consturctors
37972     if (layout.layout) {
37973         config = layout;
37974         layout = config.layout;
37975         delete config.layout;
37976     }
37977     if (layout.xtype && !layout.getEl) {
37978         // then layout needs constructing..
37979         layout = Roo.factory(layout, Roo);
37980     }
37981     */
37982     
37983     config.el =  config.layout.getEl();
37984     
37985     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37986     
37987     config.layout.monitorWindowResize = false; // turn off autosizing
37988     this.layout = config.layout;
37989     this.layout.getEl().addClass("roo-layout-nested-layout");
37990     this.layout.parent = this;
37991     
37992     
37993     
37994     
37995 };
37996
37997 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37998
37999     setSize : function(width, height){
38000         if(!this.ignoreResize(width, height)){
38001             var size = this.adjustForComponents(width, height);
38002             var el = this.layout.getEl();
38003             if (size.height < 1) {
38004                 el.setWidth(size.width);   
38005             } else {
38006                 el.setSize(size.width, size.height);
38007             }
38008             var touch = el.dom.offsetWidth;
38009             this.layout.layout();
38010             // ie requires a double layout on the first pass
38011             if(Roo.isIE && !this.initialized){
38012                 this.initialized = true;
38013                 this.layout.layout();
38014             }
38015         }
38016     },
38017     
38018     // activate all subpanels if not currently active..
38019     
38020     setActiveState : function(active){
38021         this.active = active;
38022         this.setActiveClass(active);
38023         
38024         if(!active){
38025             this.fireEvent("deactivate", this);
38026             return;
38027         }
38028         
38029         this.fireEvent("activate", this);
38030         // not sure if this should happen before or after..
38031         if (!this.layout) {
38032             return; // should not happen..
38033         }
38034         var reg = false;
38035         for (var r in this.layout.regions) {
38036             reg = this.layout.getRegion(r);
38037             if (reg.getActivePanel()) {
38038                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
38039                 reg.setActivePanel(reg.getActivePanel());
38040                 continue;
38041             }
38042             if (!reg.panels.length) {
38043                 continue;
38044             }
38045             reg.showPanel(reg.getPanel(0));
38046         }
38047         
38048         
38049         
38050         
38051     },
38052     
38053     /**
38054      * Returns the nested BorderLayout for this panel
38055      * @return {Roo.BorderLayout} 
38056      */
38057     getLayout : function(){
38058         return this.layout;
38059     },
38060     
38061      /**
38062      * Adds a xtype elements to the layout of the nested panel
38063      * <pre><code>
38064
38065 panel.addxtype({
38066        xtype : 'ContentPanel',
38067        region: 'west',
38068        items: [ .... ]
38069    }
38070 );
38071
38072 panel.addxtype({
38073         xtype : 'NestedLayoutPanel',
38074         region: 'west',
38075         layout: {
38076            center: { },
38077            west: { }   
38078         },
38079         items : [ ... list of content panels or nested layout panels.. ]
38080    }
38081 );
38082 </code></pre>
38083      * @param {Object} cfg Xtype definition of item to add.
38084      */
38085     addxtype : function(cfg) {
38086         return this.layout.addxtype(cfg);
38087     
38088     }
38089 });/*
38090  * Based on:
38091  * Ext JS Library 1.1.1
38092  * Copyright(c) 2006-2007, Ext JS, LLC.
38093  *
38094  * Originally Released Under LGPL - original licence link has changed is not relivant.
38095  *
38096  * Fork - LGPL
38097  * <script type="text/javascript">
38098  */
38099 /**
38100  * @class Roo.TabPanel
38101  * @extends Roo.util.Observable
38102  * A lightweight tab container.
38103  * <br><br>
38104  * Usage:
38105  * <pre><code>
38106 // basic tabs 1, built from existing content
38107 var tabs = new Roo.TabPanel("tabs1");
38108 tabs.addTab("script", "View Script");
38109 tabs.addTab("markup", "View Markup");
38110 tabs.activate("script");
38111
38112 // more advanced tabs, built from javascript
38113 var jtabs = new Roo.TabPanel("jtabs");
38114 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
38115
38116 // set up the UpdateManager
38117 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
38118 var updater = tab2.getUpdateManager();
38119 updater.setDefaultUrl("ajax1.htm");
38120 tab2.on('activate', updater.refresh, updater, true);
38121
38122 // Use setUrl for Ajax loading
38123 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
38124 tab3.setUrl("ajax2.htm", null, true);
38125
38126 // Disabled tab
38127 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
38128 tab4.disable();
38129
38130 jtabs.activate("jtabs-1");
38131  * </code></pre>
38132  * @constructor
38133  * Create a new TabPanel.
38134  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
38135  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
38136  */
38137 Roo.bootstrap.panel.Tabs = function(config){
38138     /**
38139     * The container element for this TabPanel.
38140     * @type Roo.Element
38141     */
38142     this.el = Roo.get(config.el);
38143     delete config.el;
38144     if(config){
38145         if(typeof config == "boolean"){
38146             this.tabPosition = config ? "bottom" : "top";
38147         }else{
38148             Roo.apply(this, config);
38149         }
38150     }
38151     
38152     if(this.tabPosition == "bottom"){
38153         // if tabs are at the bottom = create the body first.
38154         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38155         this.el.addClass("roo-tabs-bottom");
38156     }
38157     // next create the tabs holders
38158     
38159     if (this.tabPosition == "west"){
38160         
38161         var reg = this.region; // fake it..
38162         while (reg) {
38163             if (!reg.mgr.parent) {
38164                 break;
38165             }
38166             reg = reg.mgr.parent.region;
38167         }
38168         Roo.log("got nest?");
38169         Roo.log(reg);
38170         if (reg.mgr.getRegion('west')) {
38171             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
38172             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
38173             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38174             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38175             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38176         
38177             
38178         }
38179         
38180         
38181     } else {
38182      
38183         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
38184         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38185         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38186         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38187     }
38188     
38189     
38190     if(Roo.isIE){
38191         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
38192     }
38193     
38194     // finally - if tabs are at the top, then create the body last..
38195     if(this.tabPosition != "bottom"){
38196         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
38197          * @type Roo.Element
38198          */
38199         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38200         this.el.addClass("roo-tabs-top");
38201     }
38202     this.items = [];
38203
38204     this.bodyEl.setStyle("position", "relative");
38205
38206     this.active = null;
38207     this.activateDelegate = this.activate.createDelegate(this);
38208
38209     this.addEvents({
38210         /**
38211          * @event tabchange
38212          * Fires when the active tab changes
38213          * @param {Roo.TabPanel} this
38214          * @param {Roo.TabPanelItem} activePanel The new active tab
38215          */
38216         "tabchange": true,
38217         /**
38218          * @event beforetabchange
38219          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
38220          * @param {Roo.TabPanel} this
38221          * @param {Object} e Set cancel to true on this object to cancel the tab change
38222          * @param {Roo.TabPanelItem} tab The tab being changed to
38223          */
38224         "beforetabchange" : true
38225     });
38226
38227     Roo.EventManager.onWindowResize(this.onResize, this);
38228     this.cpad = this.el.getPadding("lr");
38229     this.hiddenCount = 0;
38230
38231
38232     // toolbar on the tabbar support...
38233     if (this.toolbar) {
38234         alert("no toolbar support yet");
38235         this.toolbar  = false;
38236         /*
38237         var tcfg = this.toolbar;
38238         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
38239         this.toolbar = new Roo.Toolbar(tcfg);
38240         if (Roo.isSafari) {
38241             var tbl = tcfg.container.child('table', true);
38242             tbl.setAttribute('width', '100%');
38243         }
38244         */
38245         
38246     }
38247    
38248
38249
38250     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
38251 };
38252
38253 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
38254     /*
38255      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
38256      */
38257     tabPosition : "top",
38258     /*
38259      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
38260      */
38261     currentTabWidth : 0,
38262     /*
38263      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
38264      */
38265     minTabWidth : 40,
38266     /*
38267      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
38268      */
38269     maxTabWidth : 250,
38270     /*
38271      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
38272      */
38273     preferredTabWidth : 175,
38274     /*
38275      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
38276      */
38277     resizeTabs : false,
38278     /*
38279      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38280      */
38281     monitorResize : true,
38282     /*
38283      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
38284      */
38285     toolbar : false,  // set by caller..
38286     
38287     region : false, /// set by caller
38288     
38289     disableTooltips : true, // not used yet...
38290
38291     /**
38292      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38293      * @param {String} id The id of the div to use <b>or create</b>
38294      * @param {String} text The text for the tab
38295      * @param {String} content (optional) Content to put in the TabPanelItem body
38296      * @param {Boolean} closable (optional) True to create a close icon on the tab
38297      * @return {Roo.TabPanelItem} The created TabPanelItem
38298      */
38299     addTab : function(id, text, content, closable, tpl)
38300     {
38301         var item = new Roo.bootstrap.panel.TabItem({
38302             panel: this,
38303             id : id,
38304             text : text,
38305             closable : closable,
38306             tpl : tpl
38307         });
38308         this.addTabItem(item);
38309         if(content){
38310             item.setContent(content);
38311         }
38312         return item;
38313     },
38314
38315     /**
38316      * Returns the {@link Roo.TabPanelItem} with the specified id/index
38317      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38318      * @return {Roo.TabPanelItem}
38319      */
38320     getTab : function(id){
38321         return this.items[id];
38322     },
38323
38324     /**
38325      * Hides the {@link Roo.TabPanelItem} with the specified id/index
38326      * @param {String/Number} id The id or index of the TabPanelItem to hide.
38327      */
38328     hideTab : function(id){
38329         var t = this.items[id];
38330         if(!t.isHidden()){
38331            t.setHidden(true);
38332            this.hiddenCount++;
38333            this.autoSizeTabs();
38334         }
38335     },
38336
38337     /**
38338      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38339      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38340      */
38341     unhideTab : function(id){
38342         var t = this.items[id];
38343         if(t.isHidden()){
38344            t.setHidden(false);
38345            this.hiddenCount--;
38346            this.autoSizeTabs();
38347         }
38348     },
38349
38350     /**
38351      * Adds an existing {@link Roo.TabPanelItem}.
38352      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38353      */
38354     addTabItem : function(item)
38355     {
38356         this.items[item.id] = item;
38357         this.items.push(item);
38358         this.autoSizeTabs();
38359       //  if(this.resizeTabs){
38360     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38361   //         this.autoSizeTabs();
38362 //        }else{
38363 //            item.autoSize();
38364        // }
38365     },
38366
38367     /**
38368      * Removes a {@link Roo.TabPanelItem}.
38369      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38370      */
38371     removeTab : function(id){
38372         var items = this.items;
38373         var tab = items[id];
38374         if(!tab) { return; }
38375         var index = items.indexOf(tab);
38376         if(this.active == tab && items.length > 1){
38377             var newTab = this.getNextAvailable(index);
38378             if(newTab) {
38379                 newTab.activate();
38380             }
38381         }
38382         this.stripEl.dom.removeChild(tab.pnode.dom);
38383         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38384             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38385         }
38386         items.splice(index, 1);
38387         delete this.items[tab.id];
38388         tab.fireEvent("close", tab);
38389         tab.purgeListeners();
38390         this.autoSizeTabs();
38391     },
38392
38393     getNextAvailable : function(start){
38394         var items = this.items;
38395         var index = start;
38396         // look for a next tab that will slide over to
38397         // replace the one being removed
38398         while(index < items.length){
38399             var item = items[++index];
38400             if(item && !item.isHidden()){
38401                 return item;
38402             }
38403         }
38404         // if one isn't found select the previous tab (on the left)
38405         index = start;
38406         while(index >= 0){
38407             var item = items[--index];
38408             if(item && !item.isHidden()){
38409                 return item;
38410             }
38411         }
38412         return null;
38413     },
38414
38415     /**
38416      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38417      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38418      */
38419     disableTab : function(id){
38420         var tab = this.items[id];
38421         if(tab && this.active != tab){
38422             tab.disable();
38423         }
38424     },
38425
38426     /**
38427      * Enables a {@link Roo.TabPanelItem} that is disabled.
38428      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38429      */
38430     enableTab : function(id){
38431         var tab = this.items[id];
38432         tab.enable();
38433     },
38434
38435     /**
38436      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38437      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38438      * @return {Roo.TabPanelItem} The TabPanelItem.
38439      */
38440     activate : function(id)
38441     {
38442         //Roo.log('activite:'  + id);
38443         
38444         var tab = this.items[id];
38445         if(!tab){
38446             return null;
38447         }
38448         if(tab == this.active || tab.disabled){
38449             return tab;
38450         }
38451         var e = {};
38452         this.fireEvent("beforetabchange", this, e, tab);
38453         if(e.cancel !== true && !tab.disabled){
38454             if(this.active){
38455                 this.active.hide();
38456             }
38457             this.active = this.items[id];
38458             this.active.show();
38459             this.fireEvent("tabchange", this, this.active);
38460         }
38461         return tab;
38462     },
38463
38464     /**
38465      * Gets the active {@link Roo.TabPanelItem}.
38466      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38467      */
38468     getActiveTab : function(){
38469         return this.active;
38470     },
38471
38472     /**
38473      * Updates the tab body element to fit the height of the container element
38474      * for overflow scrolling
38475      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38476      */
38477     syncHeight : function(targetHeight){
38478         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38479         var bm = this.bodyEl.getMargins();
38480         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38481         this.bodyEl.setHeight(newHeight);
38482         return newHeight;
38483     },
38484
38485     onResize : function(){
38486         if(this.monitorResize){
38487             this.autoSizeTabs();
38488         }
38489     },
38490
38491     /**
38492      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38493      */
38494     beginUpdate : function(){
38495         this.updating = true;
38496     },
38497
38498     /**
38499      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38500      */
38501     endUpdate : function(){
38502         this.updating = false;
38503         this.autoSizeTabs();
38504     },
38505
38506     /**
38507      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38508      */
38509     autoSizeTabs : function()
38510     {
38511         var count = this.items.length;
38512         var vcount = count - this.hiddenCount;
38513         
38514         if (vcount < 2) {
38515             this.stripEl.hide();
38516         } else {
38517             this.stripEl.show();
38518         }
38519         
38520         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38521             return;
38522         }
38523         
38524         
38525         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38526         var availWidth = Math.floor(w / vcount);
38527         var b = this.stripBody;
38528         if(b.getWidth() > w){
38529             var tabs = this.items;
38530             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38531             if(availWidth < this.minTabWidth){
38532                 /*if(!this.sleft){    // incomplete scrolling code
38533                     this.createScrollButtons();
38534                 }
38535                 this.showScroll();
38536                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38537             }
38538         }else{
38539             if(this.currentTabWidth < this.preferredTabWidth){
38540                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38541             }
38542         }
38543     },
38544
38545     /**
38546      * Returns the number of tabs in this TabPanel.
38547      * @return {Number}
38548      */
38549      getCount : function(){
38550          return this.items.length;
38551      },
38552
38553     /**
38554      * Resizes all the tabs to the passed width
38555      * @param {Number} The new width
38556      */
38557     setTabWidth : function(width){
38558         this.currentTabWidth = width;
38559         for(var i = 0, len = this.items.length; i < len; i++) {
38560                 if(!this.items[i].isHidden()) {
38561                 this.items[i].setWidth(width);
38562             }
38563         }
38564     },
38565
38566     /**
38567      * Destroys this TabPanel
38568      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38569      */
38570     destroy : function(removeEl){
38571         Roo.EventManager.removeResizeListener(this.onResize, this);
38572         for(var i = 0, len = this.items.length; i < len; i++){
38573             this.items[i].purgeListeners();
38574         }
38575         if(removeEl === true){
38576             this.el.update("");
38577             this.el.remove();
38578         }
38579     },
38580     
38581     createStrip : function(container)
38582     {
38583         var strip = document.createElement("nav");
38584         strip.className = Roo.bootstrap.version == 4 ?
38585             "navbar-light bg-light" : 
38586             "navbar navbar-default"; //"x-tabs-wrap";
38587         container.appendChild(strip);
38588         return strip;
38589     },
38590     
38591     createStripList : function(strip)
38592     {
38593         // div wrapper for retard IE
38594         // returns the "tr" element.
38595         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38596         //'<div class="x-tabs-strip-wrap">'+
38597           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38598           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38599         return strip.firstChild; //.firstChild.firstChild.firstChild;
38600     },
38601     createBody : function(container)
38602     {
38603         var body = document.createElement("div");
38604         Roo.id(body, "tab-body");
38605         //Roo.fly(body).addClass("x-tabs-body");
38606         Roo.fly(body).addClass("tab-content");
38607         container.appendChild(body);
38608         return body;
38609     },
38610     createItemBody :function(bodyEl, id){
38611         var body = Roo.getDom(id);
38612         if(!body){
38613             body = document.createElement("div");
38614             body.id = id;
38615         }
38616         //Roo.fly(body).addClass("x-tabs-item-body");
38617         Roo.fly(body).addClass("tab-pane");
38618          bodyEl.insertBefore(body, bodyEl.firstChild);
38619         return body;
38620     },
38621     /** @private */
38622     createStripElements :  function(stripEl, text, closable, tpl)
38623     {
38624         var td = document.createElement("li"); // was td..
38625         td.className = 'nav-item';
38626         
38627         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38628         
38629         
38630         stripEl.appendChild(td);
38631         /*if(closable){
38632             td.className = "x-tabs-closable";
38633             if(!this.closeTpl){
38634                 this.closeTpl = new Roo.Template(
38635                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38636                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38637                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38638                 );
38639             }
38640             var el = this.closeTpl.overwrite(td, {"text": text});
38641             var close = el.getElementsByTagName("div")[0];
38642             var inner = el.getElementsByTagName("em")[0];
38643             return {"el": el, "close": close, "inner": inner};
38644         } else {
38645         */
38646         // not sure what this is..
38647 //            if(!this.tabTpl){
38648                 //this.tabTpl = new Roo.Template(
38649                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38650                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38651                 //);
38652 //                this.tabTpl = new Roo.Template(
38653 //                   '<a href="#">' +
38654 //                   '<span unselectable="on"' +
38655 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38656 //                            ' >{text}</span></a>'
38657 //                );
38658 //                
38659 //            }
38660
38661
38662             var template = tpl || this.tabTpl || false;
38663             
38664             if(!template){
38665                 template =  new Roo.Template(
38666                         Roo.bootstrap.version == 4 ? 
38667                             (
38668                                 '<a class="nav-link" href="#" unselectable="on"' +
38669                                      (this.disableTooltips ? '' : ' title="{text}"') +
38670                                      ' >{text}</a>'
38671                             ) : (
38672                                 '<a class="nav-link" href="#">' +
38673                                 '<span unselectable="on"' +
38674                                          (this.disableTooltips ? '' : ' title="{text}"') +
38675                                     ' >{text}</span></a>'
38676                             )
38677                 );
38678             }
38679             
38680             switch (typeof(template)) {
38681                 case 'object' :
38682                     break;
38683                 case 'string' :
38684                     template = new Roo.Template(template);
38685                     break;
38686                 default :
38687                     break;
38688             }
38689             
38690             var el = template.overwrite(td, {"text": text});
38691             
38692             var inner = el.getElementsByTagName("span")[0];
38693             
38694             return {"el": el, "inner": inner};
38695             
38696     }
38697         
38698     
38699 });
38700
38701 /**
38702  * @class Roo.TabPanelItem
38703  * @extends Roo.util.Observable
38704  * Represents an individual item (tab plus body) in a TabPanel.
38705  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38706  * @param {String} id The id of this TabPanelItem
38707  * @param {String} text The text for the tab of this TabPanelItem
38708  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38709  */
38710 Roo.bootstrap.panel.TabItem = function(config){
38711     /**
38712      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38713      * @type Roo.TabPanel
38714      */
38715     this.tabPanel = config.panel;
38716     /**
38717      * The id for this TabPanelItem
38718      * @type String
38719      */
38720     this.id = config.id;
38721     /** @private */
38722     this.disabled = false;
38723     /** @private */
38724     this.text = config.text;
38725     /** @private */
38726     this.loaded = false;
38727     this.closable = config.closable;
38728
38729     /**
38730      * The body element for this TabPanelItem.
38731      * @type Roo.Element
38732      */
38733     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38734     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38735     this.bodyEl.setStyle("display", "block");
38736     this.bodyEl.setStyle("zoom", "1");
38737     //this.hideAction();
38738
38739     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38740     /** @private */
38741     this.el = Roo.get(els.el);
38742     this.inner = Roo.get(els.inner, true);
38743      this.textEl = Roo.bootstrap.version == 4 ?
38744         this.el : Roo.get(this.el.dom.firstChild, true);
38745
38746     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
38747     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
38748
38749     
38750 //    this.el.on("mousedown", this.onTabMouseDown, this);
38751     this.el.on("click", this.onTabClick, this);
38752     /** @private */
38753     if(config.closable){
38754         var c = Roo.get(els.close, true);
38755         c.dom.title = this.closeText;
38756         c.addClassOnOver("close-over");
38757         c.on("click", this.closeClick, this);
38758      }
38759
38760     this.addEvents({
38761          /**
38762          * @event activate
38763          * Fires when this tab becomes the active tab.
38764          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38765          * @param {Roo.TabPanelItem} this
38766          */
38767         "activate": true,
38768         /**
38769          * @event beforeclose
38770          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38771          * @param {Roo.TabPanelItem} this
38772          * @param {Object} e Set cancel to true on this object to cancel the close.
38773          */
38774         "beforeclose": true,
38775         /**
38776          * @event close
38777          * Fires when this tab is closed.
38778          * @param {Roo.TabPanelItem} this
38779          */
38780          "close": true,
38781         /**
38782          * @event deactivate
38783          * Fires when this tab is no longer the active tab.
38784          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38785          * @param {Roo.TabPanelItem} this
38786          */
38787          "deactivate" : true
38788     });
38789     this.hidden = false;
38790
38791     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38792 };
38793
38794 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38795            {
38796     purgeListeners : function(){
38797        Roo.util.Observable.prototype.purgeListeners.call(this);
38798        this.el.removeAllListeners();
38799     },
38800     /**
38801      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38802      */
38803     show : function(){
38804         this.status_node.addClass("active");
38805         this.showAction();
38806         if(Roo.isOpera){
38807             this.tabPanel.stripWrap.repaint();
38808         }
38809         this.fireEvent("activate", this.tabPanel, this);
38810     },
38811
38812     /**
38813      * Returns true if this tab is the active tab.
38814      * @return {Boolean}
38815      */
38816     isActive : function(){
38817         return this.tabPanel.getActiveTab() == this;
38818     },
38819
38820     /**
38821      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38822      */
38823     hide : function(){
38824         this.status_node.removeClass("active");
38825         this.hideAction();
38826         this.fireEvent("deactivate", this.tabPanel, this);
38827     },
38828
38829     hideAction : function(){
38830         this.bodyEl.hide();
38831         this.bodyEl.setStyle("position", "absolute");
38832         this.bodyEl.setLeft("-20000px");
38833         this.bodyEl.setTop("-20000px");
38834     },
38835
38836     showAction : function(){
38837         this.bodyEl.setStyle("position", "relative");
38838         this.bodyEl.setTop("");
38839         this.bodyEl.setLeft("");
38840         this.bodyEl.show();
38841     },
38842
38843     /**
38844      * Set the tooltip for the tab.
38845      * @param {String} tooltip The tab's tooltip
38846      */
38847     setTooltip : function(text){
38848         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38849             this.textEl.dom.qtip = text;
38850             this.textEl.dom.removeAttribute('title');
38851         }else{
38852             this.textEl.dom.title = text;
38853         }
38854     },
38855
38856     onTabClick : function(e){
38857         e.preventDefault();
38858         this.tabPanel.activate(this.id);
38859     },
38860
38861     onTabMouseDown : function(e){
38862         e.preventDefault();
38863         this.tabPanel.activate(this.id);
38864     },
38865 /*
38866     getWidth : function(){
38867         return this.inner.getWidth();
38868     },
38869
38870     setWidth : function(width){
38871         var iwidth = width - this.linode.getPadding("lr");
38872         this.inner.setWidth(iwidth);
38873         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38874         this.linode.setWidth(width);
38875     },
38876 */
38877     /**
38878      * Show or hide the tab
38879      * @param {Boolean} hidden True to hide or false to show.
38880      */
38881     setHidden : function(hidden){
38882         this.hidden = hidden;
38883         this.linode.setStyle("display", hidden ? "none" : "");
38884     },
38885
38886     /**
38887      * Returns true if this tab is "hidden"
38888      * @return {Boolean}
38889      */
38890     isHidden : function(){
38891         return this.hidden;
38892     },
38893
38894     /**
38895      * Returns the text for this tab
38896      * @return {String}
38897      */
38898     getText : function(){
38899         return this.text;
38900     },
38901     /*
38902     autoSize : function(){
38903         //this.el.beginMeasure();
38904         this.textEl.setWidth(1);
38905         /*
38906          *  #2804 [new] Tabs in Roojs
38907          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38908          */
38909         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38910         //this.el.endMeasure();
38911     //},
38912
38913     /**
38914      * Sets the text for the tab (Note: this also sets the tooltip text)
38915      * @param {String} text The tab's text and tooltip
38916      */
38917     setText : function(text){
38918         this.text = text;
38919         this.textEl.update(text);
38920         this.setTooltip(text);
38921         //if(!this.tabPanel.resizeTabs){
38922         //    this.autoSize();
38923         //}
38924     },
38925     /**
38926      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38927      */
38928     activate : function(){
38929         this.tabPanel.activate(this.id);
38930     },
38931
38932     /**
38933      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38934      */
38935     disable : function(){
38936         if(this.tabPanel.active != this){
38937             this.disabled = true;
38938             this.status_node.addClass("disabled");
38939         }
38940     },
38941
38942     /**
38943      * Enables this TabPanelItem if it was previously disabled.
38944      */
38945     enable : function(){
38946         this.disabled = false;
38947         this.status_node.removeClass("disabled");
38948     },
38949
38950     /**
38951      * Sets the content for this TabPanelItem.
38952      * @param {String} content The content
38953      * @param {Boolean} loadScripts true to look for and load scripts
38954      */
38955     setContent : function(content, loadScripts){
38956         this.bodyEl.update(content, loadScripts);
38957     },
38958
38959     /**
38960      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38961      * @return {Roo.UpdateManager} The UpdateManager
38962      */
38963     getUpdateManager : function(){
38964         return this.bodyEl.getUpdateManager();
38965     },
38966
38967     /**
38968      * Set a URL to be used to load the content for this TabPanelItem.
38969      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38970      * @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)
38971      * @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)
38972      * @return {Roo.UpdateManager} The UpdateManager
38973      */
38974     setUrl : function(url, params, loadOnce){
38975         if(this.refreshDelegate){
38976             this.un('activate', this.refreshDelegate);
38977         }
38978         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38979         this.on("activate", this.refreshDelegate);
38980         return this.bodyEl.getUpdateManager();
38981     },
38982
38983     /** @private */
38984     _handleRefresh : function(url, params, loadOnce){
38985         if(!loadOnce || !this.loaded){
38986             var updater = this.bodyEl.getUpdateManager();
38987             updater.update(url, params, this._setLoaded.createDelegate(this));
38988         }
38989     },
38990
38991     /**
38992      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38993      *   Will fail silently if the setUrl method has not been called.
38994      *   This does not activate the panel, just updates its content.
38995      */
38996     refresh : function(){
38997         if(this.refreshDelegate){
38998            this.loaded = false;
38999            this.refreshDelegate();
39000         }
39001     },
39002
39003     /** @private */
39004     _setLoaded : function(){
39005         this.loaded = true;
39006     },
39007
39008     /** @private */
39009     closeClick : function(e){
39010         var o = {};
39011         e.stopEvent();
39012         this.fireEvent("beforeclose", this, o);
39013         if(o.cancel !== true){
39014             this.tabPanel.removeTab(this.id);
39015         }
39016     },
39017     /**
39018      * The text displayed in the tooltip for the close icon.
39019      * @type String
39020      */
39021     closeText : "Close this tab"
39022 });
39023 /**
39024 *    This script refer to:
39025 *    Title: International Telephone Input
39026 *    Author: Jack O'Connor
39027 *    Code version:  v12.1.12
39028 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39029 **/
39030
39031 Roo.bootstrap.PhoneInputData = function() {
39032     var d = [
39033       [
39034         "Afghanistan (‫افغانستان‬‎)",
39035         "af",
39036         "93"
39037       ],
39038       [
39039         "Albania (Shqipëri)",
39040         "al",
39041         "355"
39042       ],
39043       [
39044         "Algeria (‫الجزائر‬‎)",
39045         "dz",
39046         "213"
39047       ],
39048       [
39049         "American Samoa",
39050         "as",
39051         "1684"
39052       ],
39053       [
39054         "Andorra",
39055         "ad",
39056         "376"
39057       ],
39058       [
39059         "Angola",
39060         "ao",
39061         "244"
39062       ],
39063       [
39064         "Anguilla",
39065         "ai",
39066         "1264"
39067       ],
39068       [
39069         "Antigua and Barbuda",
39070         "ag",
39071         "1268"
39072       ],
39073       [
39074         "Argentina",
39075         "ar",
39076         "54"
39077       ],
39078       [
39079         "Armenia (Հայաստան)",
39080         "am",
39081         "374"
39082       ],
39083       [
39084         "Aruba",
39085         "aw",
39086         "297"
39087       ],
39088       [
39089         "Australia",
39090         "au",
39091         "61",
39092         0
39093       ],
39094       [
39095         "Austria (Österreich)",
39096         "at",
39097         "43"
39098       ],
39099       [
39100         "Azerbaijan (Azərbaycan)",
39101         "az",
39102         "994"
39103       ],
39104       [
39105         "Bahamas",
39106         "bs",
39107         "1242"
39108       ],
39109       [
39110         "Bahrain (‫البحرين‬‎)",
39111         "bh",
39112         "973"
39113       ],
39114       [
39115         "Bangladesh (বাংলাদেশ)",
39116         "bd",
39117         "880"
39118       ],
39119       [
39120         "Barbados",
39121         "bb",
39122         "1246"
39123       ],
39124       [
39125         "Belarus (Беларусь)",
39126         "by",
39127         "375"
39128       ],
39129       [
39130         "Belgium (België)",
39131         "be",
39132         "32"
39133       ],
39134       [
39135         "Belize",
39136         "bz",
39137         "501"
39138       ],
39139       [
39140         "Benin (Bénin)",
39141         "bj",
39142         "229"
39143       ],
39144       [
39145         "Bermuda",
39146         "bm",
39147         "1441"
39148       ],
39149       [
39150         "Bhutan (འབྲུག)",
39151         "bt",
39152         "975"
39153       ],
39154       [
39155         "Bolivia",
39156         "bo",
39157         "591"
39158       ],
39159       [
39160         "Bosnia and Herzegovina (Босна и Херцеговина)",
39161         "ba",
39162         "387"
39163       ],
39164       [
39165         "Botswana",
39166         "bw",
39167         "267"
39168       ],
39169       [
39170         "Brazil (Brasil)",
39171         "br",
39172         "55"
39173       ],
39174       [
39175         "British Indian Ocean Territory",
39176         "io",
39177         "246"
39178       ],
39179       [
39180         "British Virgin Islands",
39181         "vg",
39182         "1284"
39183       ],
39184       [
39185         "Brunei",
39186         "bn",
39187         "673"
39188       ],
39189       [
39190         "Bulgaria (България)",
39191         "bg",
39192         "359"
39193       ],
39194       [
39195         "Burkina Faso",
39196         "bf",
39197         "226"
39198       ],
39199       [
39200         "Burundi (Uburundi)",
39201         "bi",
39202         "257"
39203       ],
39204       [
39205         "Cambodia (កម្ពុជា)",
39206         "kh",
39207         "855"
39208       ],
39209       [
39210         "Cameroon (Cameroun)",
39211         "cm",
39212         "237"
39213       ],
39214       [
39215         "Canada",
39216         "ca",
39217         "1",
39218         1,
39219         ["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"]
39220       ],
39221       [
39222         "Cape Verde (Kabu Verdi)",
39223         "cv",
39224         "238"
39225       ],
39226       [
39227         "Caribbean Netherlands",
39228         "bq",
39229         "599",
39230         1
39231       ],
39232       [
39233         "Cayman Islands",
39234         "ky",
39235         "1345"
39236       ],
39237       [
39238         "Central African Republic (République centrafricaine)",
39239         "cf",
39240         "236"
39241       ],
39242       [
39243         "Chad (Tchad)",
39244         "td",
39245         "235"
39246       ],
39247       [
39248         "Chile",
39249         "cl",
39250         "56"
39251       ],
39252       [
39253         "China (中国)",
39254         "cn",
39255         "86"
39256       ],
39257       [
39258         "Christmas Island",
39259         "cx",
39260         "61",
39261         2
39262       ],
39263       [
39264         "Cocos (Keeling) Islands",
39265         "cc",
39266         "61",
39267         1
39268       ],
39269       [
39270         "Colombia",
39271         "co",
39272         "57"
39273       ],
39274       [
39275         "Comoros (‫جزر القمر‬‎)",
39276         "km",
39277         "269"
39278       ],
39279       [
39280         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
39281         "cd",
39282         "243"
39283       ],
39284       [
39285         "Congo (Republic) (Congo-Brazzaville)",
39286         "cg",
39287         "242"
39288       ],
39289       [
39290         "Cook Islands",
39291         "ck",
39292         "682"
39293       ],
39294       [
39295         "Costa Rica",
39296         "cr",
39297         "506"
39298       ],
39299       [
39300         "Côte d’Ivoire",
39301         "ci",
39302         "225"
39303       ],
39304       [
39305         "Croatia (Hrvatska)",
39306         "hr",
39307         "385"
39308       ],
39309       [
39310         "Cuba",
39311         "cu",
39312         "53"
39313       ],
39314       [
39315         "Curaçao",
39316         "cw",
39317         "599",
39318         0
39319       ],
39320       [
39321         "Cyprus (Κύπρος)",
39322         "cy",
39323         "357"
39324       ],
39325       [
39326         "Czech Republic (Česká republika)",
39327         "cz",
39328         "420"
39329       ],
39330       [
39331         "Denmark (Danmark)",
39332         "dk",
39333         "45"
39334       ],
39335       [
39336         "Djibouti",
39337         "dj",
39338         "253"
39339       ],
39340       [
39341         "Dominica",
39342         "dm",
39343         "1767"
39344       ],
39345       [
39346         "Dominican Republic (República Dominicana)",
39347         "do",
39348         "1",
39349         2,
39350         ["809", "829", "849"]
39351       ],
39352       [
39353         "Ecuador",
39354         "ec",
39355         "593"
39356       ],
39357       [
39358         "Egypt (‫مصر‬‎)",
39359         "eg",
39360         "20"
39361       ],
39362       [
39363         "El Salvador",
39364         "sv",
39365         "503"
39366       ],
39367       [
39368         "Equatorial Guinea (Guinea Ecuatorial)",
39369         "gq",
39370         "240"
39371       ],
39372       [
39373         "Eritrea",
39374         "er",
39375         "291"
39376       ],
39377       [
39378         "Estonia (Eesti)",
39379         "ee",
39380         "372"
39381       ],
39382       [
39383         "Ethiopia",
39384         "et",
39385         "251"
39386       ],
39387       [
39388         "Falkland Islands (Islas Malvinas)",
39389         "fk",
39390         "500"
39391       ],
39392       [
39393         "Faroe Islands (Føroyar)",
39394         "fo",
39395         "298"
39396       ],
39397       [
39398         "Fiji",
39399         "fj",
39400         "679"
39401       ],
39402       [
39403         "Finland (Suomi)",
39404         "fi",
39405         "358",
39406         0
39407       ],
39408       [
39409         "France",
39410         "fr",
39411         "33"
39412       ],
39413       [
39414         "French Guiana (Guyane française)",
39415         "gf",
39416         "594"
39417       ],
39418       [
39419         "French Polynesia (Polynésie française)",
39420         "pf",
39421         "689"
39422       ],
39423       [
39424         "Gabon",
39425         "ga",
39426         "241"
39427       ],
39428       [
39429         "Gambia",
39430         "gm",
39431         "220"
39432       ],
39433       [
39434         "Georgia (საქართველო)",
39435         "ge",
39436         "995"
39437       ],
39438       [
39439         "Germany (Deutschland)",
39440         "de",
39441         "49"
39442       ],
39443       [
39444         "Ghana (Gaana)",
39445         "gh",
39446         "233"
39447       ],
39448       [
39449         "Gibraltar",
39450         "gi",
39451         "350"
39452       ],
39453       [
39454         "Greece (Ελλάδα)",
39455         "gr",
39456         "30"
39457       ],
39458       [
39459         "Greenland (Kalaallit Nunaat)",
39460         "gl",
39461         "299"
39462       ],
39463       [
39464         "Grenada",
39465         "gd",
39466         "1473"
39467       ],
39468       [
39469         "Guadeloupe",
39470         "gp",
39471         "590",
39472         0
39473       ],
39474       [
39475         "Guam",
39476         "gu",
39477         "1671"
39478       ],
39479       [
39480         "Guatemala",
39481         "gt",
39482         "502"
39483       ],
39484       [
39485         "Guernsey",
39486         "gg",
39487         "44",
39488         1
39489       ],
39490       [
39491         "Guinea (Guinée)",
39492         "gn",
39493         "224"
39494       ],
39495       [
39496         "Guinea-Bissau (Guiné Bissau)",
39497         "gw",
39498         "245"
39499       ],
39500       [
39501         "Guyana",
39502         "gy",
39503         "592"
39504       ],
39505       [
39506         "Haiti",
39507         "ht",
39508         "509"
39509       ],
39510       [
39511         "Honduras",
39512         "hn",
39513         "504"
39514       ],
39515       [
39516         "Hong Kong (香港)",
39517         "hk",
39518         "852"
39519       ],
39520       [
39521         "Hungary (Magyarország)",
39522         "hu",
39523         "36"
39524       ],
39525       [
39526         "Iceland (Ísland)",
39527         "is",
39528         "354"
39529       ],
39530       [
39531         "India (भारत)",
39532         "in",
39533         "91"
39534       ],
39535       [
39536         "Indonesia",
39537         "id",
39538         "62"
39539       ],
39540       [
39541         "Iran (‫ایران‬‎)",
39542         "ir",
39543         "98"
39544       ],
39545       [
39546         "Iraq (‫العراق‬‎)",
39547         "iq",
39548         "964"
39549       ],
39550       [
39551         "Ireland",
39552         "ie",
39553         "353"
39554       ],
39555       [
39556         "Isle of Man",
39557         "im",
39558         "44",
39559         2
39560       ],
39561       [
39562         "Israel (‫ישראל‬‎)",
39563         "il",
39564         "972"
39565       ],
39566       [
39567         "Italy (Italia)",
39568         "it",
39569         "39",
39570         0
39571       ],
39572       [
39573         "Jamaica",
39574         "jm",
39575         "1876"
39576       ],
39577       [
39578         "Japan (日本)",
39579         "jp",
39580         "81"
39581       ],
39582       [
39583         "Jersey",
39584         "je",
39585         "44",
39586         3
39587       ],
39588       [
39589         "Jordan (‫الأردن‬‎)",
39590         "jo",
39591         "962"
39592       ],
39593       [
39594         "Kazakhstan (Казахстан)",
39595         "kz",
39596         "7",
39597         1
39598       ],
39599       [
39600         "Kenya",
39601         "ke",
39602         "254"
39603       ],
39604       [
39605         "Kiribati",
39606         "ki",
39607         "686"
39608       ],
39609       [
39610         "Kosovo",
39611         "xk",
39612         "383"
39613       ],
39614       [
39615         "Kuwait (‫الكويت‬‎)",
39616         "kw",
39617         "965"
39618       ],
39619       [
39620         "Kyrgyzstan (Кыргызстан)",
39621         "kg",
39622         "996"
39623       ],
39624       [
39625         "Laos (ລາວ)",
39626         "la",
39627         "856"
39628       ],
39629       [
39630         "Latvia (Latvija)",
39631         "lv",
39632         "371"
39633       ],
39634       [
39635         "Lebanon (‫لبنان‬‎)",
39636         "lb",
39637         "961"
39638       ],
39639       [
39640         "Lesotho",
39641         "ls",
39642         "266"
39643       ],
39644       [
39645         "Liberia",
39646         "lr",
39647         "231"
39648       ],
39649       [
39650         "Libya (‫ليبيا‬‎)",
39651         "ly",
39652         "218"
39653       ],
39654       [
39655         "Liechtenstein",
39656         "li",
39657         "423"
39658       ],
39659       [
39660         "Lithuania (Lietuva)",
39661         "lt",
39662         "370"
39663       ],
39664       [
39665         "Luxembourg",
39666         "lu",
39667         "352"
39668       ],
39669       [
39670         "Macau (澳門)",
39671         "mo",
39672         "853"
39673       ],
39674       [
39675         "Macedonia (FYROM) (Македонија)",
39676         "mk",
39677         "389"
39678       ],
39679       [
39680         "Madagascar (Madagasikara)",
39681         "mg",
39682         "261"
39683       ],
39684       [
39685         "Malawi",
39686         "mw",
39687         "265"
39688       ],
39689       [
39690         "Malaysia",
39691         "my",
39692         "60"
39693       ],
39694       [
39695         "Maldives",
39696         "mv",
39697         "960"
39698       ],
39699       [
39700         "Mali",
39701         "ml",
39702         "223"
39703       ],
39704       [
39705         "Malta",
39706         "mt",
39707         "356"
39708       ],
39709       [
39710         "Marshall Islands",
39711         "mh",
39712         "692"
39713       ],
39714       [
39715         "Martinique",
39716         "mq",
39717         "596"
39718       ],
39719       [
39720         "Mauritania (‫موريتانيا‬‎)",
39721         "mr",
39722         "222"
39723       ],
39724       [
39725         "Mauritius (Moris)",
39726         "mu",
39727         "230"
39728       ],
39729       [
39730         "Mayotte",
39731         "yt",
39732         "262",
39733         1
39734       ],
39735       [
39736         "Mexico (México)",
39737         "mx",
39738         "52"
39739       ],
39740       [
39741         "Micronesia",
39742         "fm",
39743         "691"
39744       ],
39745       [
39746         "Moldova (Republica Moldova)",
39747         "md",
39748         "373"
39749       ],
39750       [
39751         "Monaco",
39752         "mc",
39753         "377"
39754       ],
39755       [
39756         "Mongolia (Монгол)",
39757         "mn",
39758         "976"
39759       ],
39760       [
39761         "Montenegro (Crna Gora)",
39762         "me",
39763         "382"
39764       ],
39765       [
39766         "Montserrat",
39767         "ms",
39768         "1664"
39769       ],
39770       [
39771         "Morocco (‫المغرب‬‎)",
39772         "ma",
39773         "212",
39774         0
39775       ],
39776       [
39777         "Mozambique (Moçambique)",
39778         "mz",
39779         "258"
39780       ],
39781       [
39782         "Myanmar (Burma) (မြန်မာ)",
39783         "mm",
39784         "95"
39785       ],
39786       [
39787         "Namibia (Namibië)",
39788         "na",
39789         "264"
39790       ],
39791       [
39792         "Nauru",
39793         "nr",
39794         "674"
39795       ],
39796       [
39797         "Nepal (नेपाल)",
39798         "np",
39799         "977"
39800       ],
39801       [
39802         "Netherlands (Nederland)",
39803         "nl",
39804         "31"
39805       ],
39806       [
39807         "New Caledonia (Nouvelle-Calédonie)",
39808         "nc",
39809         "687"
39810       ],
39811       [
39812         "New Zealand",
39813         "nz",
39814         "64"
39815       ],
39816       [
39817         "Nicaragua",
39818         "ni",
39819         "505"
39820       ],
39821       [
39822         "Niger (Nijar)",
39823         "ne",
39824         "227"
39825       ],
39826       [
39827         "Nigeria",
39828         "ng",
39829         "234"
39830       ],
39831       [
39832         "Niue",
39833         "nu",
39834         "683"
39835       ],
39836       [
39837         "Norfolk Island",
39838         "nf",
39839         "672"
39840       ],
39841       [
39842         "North Korea (조선 민주주의 인민 공화국)",
39843         "kp",
39844         "850"
39845       ],
39846       [
39847         "Northern Mariana Islands",
39848         "mp",
39849         "1670"
39850       ],
39851       [
39852         "Norway (Norge)",
39853         "no",
39854         "47",
39855         0
39856       ],
39857       [
39858         "Oman (‫عُمان‬‎)",
39859         "om",
39860         "968"
39861       ],
39862       [
39863         "Pakistan (‫پاکستان‬‎)",
39864         "pk",
39865         "92"
39866       ],
39867       [
39868         "Palau",
39869         "pw",
39870         "680"
39871       ],
39872       [
39873         "Palestine (‫فلسطين‬‎)",
39874         "ps",
39875         "970"
39876       ],
39877       [
39878         "Panama (Panamá)",
39879         "pa",
39880         "507"
39881       ],
39882       [
39883         "Papua New Guinea",
39884         "pg",
39885         "675"
39886       ],
39887       [
39888         "Paraguay",
39889         "py",
39890         "595"
39891       ],
39892       [
39893         "Peru (Perú)",
39894         "pe",
39895         "51"
39896       ],
39897       [
39898         "Philippines",
39899         "ph",
39900         "63"
39901       ],
39902       [
39903         "Poland (Polska)",
39904         "pl",
39905         "48"
39906       ],
39907       [
39908         "Portugal",
39909         "pt",
39910         "351"
39911       ],
39912       [
39913         "Puerto Rico",
39914         "pr",
39915         "1",
39916         3,
39917         ["787", "939"]
39918       ],
39919       [
39920         "Qatar (‫قطر‬‎)",
39921         "qa",
39922         "974"
39923       ],
39924       [
39925         "Réunion (La Réunion)",
39926         "re",
39927         "262",
39928         0
39929       ],
39930       [
39931         "Romania (România)",
39932         "ro",
39933         "40"
39934       ],
39935       [
39936         "Russia (Россия)",
39937         "ru",
39938         "7",
39939         0
39940       ],
39941       [
39942         "Rwanda",
39943         "rw",
39944         "250"
39945       ],
39946       [
39947         "Saint Barthélemy",
39948         "bl",
39949         "590",
39950         1
39951       ],
39952       [
39953         "Saint Helena",
39954         "sh",
39955         "290"
39956       ],
39957       [
39958         "Saint Kitts and Nevis",
39959         "kn",
39960         "1869"
39961       ],
39962       [
39963         "Saint Lucia",
39964         "lc",
39965         "1758"
39966       ],
39967       [
39968         "Saint Martin (Saint-Martin (partie française))",
39969         "mf",
39970         "590",
39971         2
39972       ],
39973       [
39974         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39975         "pm",
39976         "508"
39977       ],
39978       [
39979         "Saint Vincent and the Grenadines",
39980         "vc",
39981         "1784"
39982       ],
39983       [
39984         "Samoa",
39985         "ws",
39986         "685"
39987       ],
39988       [
39989         "San Marino",
39990         "sm",
39991         "378"
39992       ],
39993       [
39994         "São Tomé and Príncipe (São Tomé e Príncipe)",
39995         "st",
39996         "239"
39997       ],
39998       [
39999         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
40000         "sa",
40001         "966"
40002       ],
40003       [
40004         "Senegal (Sénégal)",
40005         "sn",
40006         "221"
40007       ],
40008       [
40009         "Serbia (Србија)",
40010         "rs",
40011         "381"
40012       ],
40013       [
40014         "Seychelles",
40015         "sc",
40016         "248"
40017       ],
40018       [
40019         "Sierra Leone",
40020         "sl",
40021         "232"
40022       ],
40023       [
40024         "Singapore",
40025         "sg",
40026         "65"
40027       ],
40028       [
40029         "Sint Maarten",
40030         "sx",
40031         "1721"
40032       ],
40033       [
40034         "Slovakia (Slovensko)",
40035         "sk",
40036         "421"
40037       ],
40038       [
40039         "Slovenia (Slovenija)",
40040         "si",
40041         "386"
40042       ],
40043       [
40044         "Solomon Islands",
40045         "sb",
40046         "677"
40047       ],
40048       [
40049         "Somalia (Soomaaliya)",
40050         "so",
40051         "252"
40052       ],
40053       [
40054         "South Africa",
40055         "za",
40056         "27"
40057       ],
40058       [
40059         "South Korea (대한민국)",
40060         "kr",
40061         "82"
40062       ],
40063       [
40064         "South Sudan (‫جنوب السودان‬‎)",
40065         "ss",
40066         "211"
40067       ],
40068       [
40069         "Spain (España)",
40070         "es",
40071         "34"
40072       ],
40073       [
40074         "Sri Lanka (ශ්‍රී ලංකාව)",
40075         "lk",
40076         "94"
40077       ],
40078       [
40079         "Sudan (‫السودان‬‎)",
40080         "sd",
40081         "249"
40082       ],
40083       [
40084         "Suriname",
40085         "sr",
40086         "597"
40087       ],
40088       [
40089         "Svalbard and Jan Mayen",
40090         "sj",
40091         "47",
40092         1
40093       ],
40094       [
40095         "Swaziland",
40096         "sz",
40097         "268"
40098       ],
40099       [
40100         "Sweden (Sverige)",
40101         "se",
40102         "46"
40103       ],
40104       [
40105         "Switzerland (Schweiz)",
40106         "ch",
40107         "41"
40108       ],
40109       [
40110         "Syria (‫سوريا‬‎)",
40111         "sy",
40112         "963"
40113       ],
40114       [
40115         "Taiwan (台灣)",
40116         "tw",
40117         "886"
40118       ],
40119       [
40120         "Tajikistan",
40121         "tj",
40122         "992"
40123       ],
40124       [
40125         "Tanzania",
40126         "tz",
40127         "255"
40128       ],
40129       [
40130         "Thailand (ไทย)",
40131         "th",
40132         "66"
40133       ],
40134       [
40135         "Timor-Leste",
40136         "tl",
40137         "670"
40138       ],
40139       [
40140         "Togo",
40141         "tg",
40142         "228"
40143       ],
40144       [
40145         "Tokelau",
40146         "tk",
40147         "690"
40148       ],
40149       [
40150         "Tonga",
40151         "to",
40152         "676"
40153       ],
40154       [
40155         "Trinidad and Tobago",
40156         "tt",
40157         "1868"
40158       ],
40159       [
40160         "Tunisia (‫تونس‬‎)",
40161         "tn",
40162         "216"
40163       ],
40164       [
40165         "Turkey (Türkiye)",
40166         "tr",
40167         "90"
40168       ],
40169       [
40170         "Turkmenistan",
40171         "tm",
40172         "993"
40173       ],
40174       [
40175         "Turks and Caicos Islands",
40176         "tc",
40177         "1649"
40178       ],
40179       [
40180         "Tuvalu",
40181         "tv",
40182         "688"
40183       ],
40184       [
40185         "U.S. Virgin Islands",
40186         "vi",
40187         "1340"
40188       ],
40189       [
40190         "Uganda",
40191         "ug",
40192         "256"
40193       ],
40194       [
40195         "Ukraine (Україна)",
40196         "ua",
40197         "380"
40198       ],
40199       [
40200         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
40201         "ae",
40202         "971"
40203       ],
40204       [
40205         "United Kingdom",
40206         "gb",
40207         "44",
40208         0
40209       ],
40210       [
40211         "United States",
40212         "us",
40213         "1",
40214         0
40215       ],
40216       [
40217         "Uruguay",
40218         "uy",
40219         "598"
40220       ],
40221       [
40222         "Uzbekistan (Oʻzbekiston)",
40223         "uz",
40224         "998"
40225       ],
40226       [
40227         "Vanuatu",
40228         "vu",
40229         "678"
40230       ],
40231       [
40232         "Vatican City (Città del Vaticano)",
40233         "va",
40234         "39",
40235         1
40236       ],
40237       [
40238         "Venezuela",
40239         "ve",
40240         "58"
40241       ],
40242       [
40243         "Vietnam (Việt Nam)",
40244         "vn",
40245         "84"
40246       ],
40247       [
40248         "Wallis and Futuna (Wallis-et-Futuna)",
40249         "wf",
40250         "681"
40251       ],
40252       [
40253         "Western Sahara (‫الصحراء الغربية‬‎)",
40254         "eh",
40255         "212",
40256         1
40257       ],
40258       [
40259         "Yemen (‫اليمن‬‎)",
40260         "ye",
40261         "967"
40262       ],
40263       [
40264         "Zambia",
40265         "zm",
40266         "260"
40267       ],
40268       [
40269         "Zimbabwe",
40270         "zw",
40271         "263"
40272       ],
40273       [
40274         "Åland Islands",
40275         "ax",
40276         "358",
40277         1
40278       ]
40279   ];
40280   
40281   return d;
40282 }/**
40283 *    This script refer to:
40284 *    Title: International Telephone Input
40285 *    Author: Jack O'Connor
40286 *    Code version:  v12.1.12
40287 *    Availability: https://github.com/jackocnr/intl-tel-input.git
40288 **/
40289
40290 /**
40291  * @class Roo.bootstrap.PhoneInput
40292  * @extends Roo.bootstrap.TriggerField
40293  * An input with International dial-code selection
40294  
40295  * @cfg {String} defaultDialCode default '+852'
40296  * @cfg {Array} preferedCountries default []
40297   
40298  * @constructor
40299  * Create a new PhoneInput.
40300  * @param {Object} config Configuration options
40301  */
40302
40303 Roo.bootstrap.PhoneInput = function(config) {
40304     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
40305 };
40306
40307 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
40308         
40309         listWidth: undefined,
40310         
40311         selectedClass: 'active',
40312         
40313         invalidClass : "has-warning",
40314         
40315         validClass: 'has-success',
40316         
40317         allowed: '0123456789',
40318         
40319         max_length: 15,
40320         
40321         /**
40322          * @cfg {String} defaultDialCode The default dial code when initializing the input
40323          */
40324         defaultDialCode: '+852',
40325         
40326         /**
40327          * @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
40328          */
40329         preferedCountries: false,
40330         
40331         getAutoCreate : function()
40332         {
40333             var data = Roo.bootstrap.PhoneInputData();
40334             var align = this.labelAlign || this.parentLabelAlign();
40335             var id = Roo.id();
40336             
40337             this.allCountries = [];
40338             this.dialCodeMapping = [];
40339             
40340             for (var i = 0; i < data.length; i++) {
40341               var c = data[i];
40342               this.allCountries[i] = {
40343                 name: c[0],
40344                 iso2: c[1],
40345                 dialCode: c[2],
40346                 priority: c[3] || 0,
40347                 areaCodes: c[4] || null
40348               };
40349               this.dialCodeMapping[c[2]] = {
40350                   name: c[0],
40351                   iso2: c[1],
40352                   priority: c[3] || 0,
40353                   areaCodes: c[4] || null
40354               };
40355             }
40356             
40357             var cfg = {
40358                 cls: 'form-group',
40359                 cn: []
40360             };
40361             
40362             var input =  {
40363                 tag: 'input',
40364                 id : id,
40365                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40366                 maxlength: this.max_length,
40367                 cls : 'form-control tel-input',
40368                 autocomplete: 'new-password'
40369             };
40370             
40371             var hiddenInput = {
40372                 tag: 'input',
40373                 type: 'hidden',
40374                 cls: 'hidden-tel-input'
40375             };
40376             
40377             if (this.name) {
40378                 hiddenInput.name = this.name;
40379             }
40380             
40381             if (this.disabled) {
40382                 input.disabled = true;
40383             }
40384             
40385             var flag_container = {
40386                 tag: 'div',
40387                 cls: 'flag-box',
40388                 cn: [
40389                     {
40390                         tag: 'div',
40391                         cls: 'flag'
40392                     },
40393                     {
40394                         tag: 'div',
40395                         cls: 'caret'
40396                     }
40397                 ]
40398             };
40399             
40400             var box = {
40401                 tag: 'div',
40402                 cls: this.hasFeedback ? 'has-feedback' : '',
40403                 cn: [
40404                     hiddenInput,
40405                     input,
40406                     {
40407                         tag: 'input',
40408                         cls: 'dial-code-holder',
40409                         disabled: true
40410                     }
40411                 ]
40412             };
40413             
40414             var container = {
40415                 cls: 'roo-select2-container input-group',
40416                 cn: [
40417                     flag_container,
40418                     box
40419                 ]
40420             };
40421             
40422             if (this.fieldLabel.length) {
40423                 var indicator = {
40424                     tag: 'i',
40425                     tooltip: 'This field is required'
40426                 };
40427                 
40428                 var label = {
40429                     tag: 'label',
40430                     'for':  id,
40431                     cls: 'control-label',
40432                     cn: []
40433                 };
40434                 
40435                 var label_text = {
40436                     tag: 'span',
40437                     html: this.fieldLabel
40438                 };
40439                 
40440                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40441                 label.cn = [
40442                     indicator,
40443                     label_text
40444                 ];
40445                 
40446                 if(this.indicatorpos == 'right') {
40447                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40448                     label.cn = [
40449                         label_text,
40450                         indicator
40451                     ];
40452                 }
40453                 
40454                 if(align == 'left') {
40455                     container = {
40456                         tag: 'div',
40457                         cn: [
40458                             container
40459                         ]
40460                     };
40461                     
40462                     if(this.labelWidth > 12){
40463                         label.style = "width: " + this.labelWidth + 'px';
40464                     }
40465                     if(this.labelWidth < 13 && this.labelmd == 0){
40466                         this.labelmd = this.labelWidth;
40467                     }
40468                     if(this.labellg > 0){
40469                         label.cls += ' col-lg-' + this.labellg;
40470                         input.cls += ' col-lg-' + (12 - this.labellg);
40471                     }
40472                     if(this.labelmd > 0){
40473                         label.cls += ' col-md-' + this.labelmd;
40474                         container.cls += ' col-md-' + (12 - this.labelmd);
40475                     }
40476                     if(this.labelsm > 0){
40477                         label.cls += ' col-sm-' + this.labelsm;
40478                         container.cls += ' col-sm-' + (12 - this.labelsm);
40479                     }
40480                     if(this.labelxs > 0){
40481                         label.cls += ' col-xs-' + this.labelxs;
40482                         container.cls += ' col-xs-' + (12 - this.labelxs);
40483                     }
40484                 }
40485             }
40486             
40487             cfg.cn = [
40488                 label,
40489                 container
40490             ];
40491             
40492             var settings = this;
40493             
40494             ['xs','sm','md','lg'].map(function(size){
40495                 if (settings[size]) {
40496                     cfg.cls += ' col-' + size + '-' + settings[size];
40497                 }
40498             });
40499             
40500             this.store = new Roo.data.Store({
40501                 proxy : new Roo.data.MemoryProxy({}),
40502                 reader : new Roo.data.JsonReader({
40503                     fields : [
40504                         {
40505                             'name' : 'name',
40506                             'type' : 'string'
40507                         },
40508                         {
40509                             'name' : 'iso2',
40510                             'type' : 'string'
40511                         },
40512                         {
40513                             'name' : 'dialCode',
40514                             'type' : 'string'
40515                         },
40516                         {
40517                             'name' : 'priority',
40518                             'type' : 'string'
40519                         },
40520                         {
40521                             'name' : 'areaCodes',
40522                             'type' : 'string'
40523                         }
40524                     ]
40525                 })
40526             });
40527             
40528             if(!this.preferedCountries) {
40529                 this.preferedCountries = [
40530                     'hk',
40531                     'gb',
40532                     'us'
40533                 ];
40534             }
40535             
40536             var p = this.preferedCountries.reverse();
40537             
40538             if(p) {
40539                 for (var i = 0; i < p.length; i++) {
40540                     for (var j = 0; j < this.allCountries.length; j++) {
40541                         if(this.allCountries[j].iso2 == p[i]) {
40542                             var t = this.allCountries[j];
40543                             this.allCountries.splice(j,1);
40544                             this.allCountries.unshift(t);
40545                         }
40546                     } 
40547                 }
40548             }
40549             
40550             this.store.proxy.data = {
40551                 success: true,
40552                 data: this.allCountries
40553             };
40554             
40555             return cfg;
40556         },
40557         
40558         initEvents : function()
40559         {
40560             this.createList();
40561             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40562             
40563             this.indicator = this.indicatorEl();
40564             this.flag = this.flagEl();
40565             this.dialCodeHolder = this.dialCodeHolderEl();
40566             
40567             this.trigger = this.el.select('div.flag-box',true).first();
40568             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40569             
40570             var _this = this;
40571             
40572             (function(){
40573                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40574                 _this.list.setWidth(lw);
40575             }).defer(100);
40576             
40577             this.list.on('mouseover', this.onViewOver, this);
40578             this.list.on('mousemove', this.onViewMove, this);
40579             this.inputEl().on("keyup", this.onKeyUp, this);
40580             this.inputEl().on("keypress", this.onKeyPress, this);
40581             
40582             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40583
40584             this.view = new Roo.View(this.list, this.tpl, {
40585                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40586             });
40587             
40588             this.view.on('click', this.onViewClick, this);
40589             this.setValue(this.defaultDialCode);
40590         },
40591         
40592         onTriggerClick : function(e)
40593         {
40594             Roo.log('trigger click');
40595             if(this.disabled){
40596                 return;
40597             }
40598             
40599             if(this.isExpanded()){
40600                 this.collapse();
40601                 this.hasFocus = false;
40602             }else {
40603                 this.store.load({});
40604                 this.hasFocus = true;
40605                 this.expand();
40606             }
40607         },
40608         
40609         isExpanded : function()
40610         {
40611             return this.list.isVisible();
40612         },
40613         
40614         collapse : function()
40615         {
40616             if(!this.isExpanded()){
40617                 return;
40618             }
40619             this.list.hide();
40620             Roo.get(document).un('mousedown', this.collapseIf, this);
40621             Roo.get(document).un('mousewheel', this.collapseIf, this);
40622             this.fireEvent('collapse', this);
40623             this.validate();
40624         },
40625         
40626         expand : function()
40627         {
40628             Roo.log('expand');
40629
40630             if(this.isExpanded() || !this.hasFocus){
40631                 return;
40632             }
40633             
40634             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40635             this.list.setWidth(lw);
40636             
40637             this.list.show();
40638             this.restrictHeight();
40639             
40640             Roo.get(document).on('mousedown', this.collapseIf, this);
40641             Roo.get(document).on('mousewheel', this.collapseIf, this);
40642             
40643             this.fireEvent('expand', this);
40644         },
40645         
40646         restrictHeight : function()
40647         {
40648             this.list.alignTo(this.inputEl(), this.listAlign);
40649             this.list.alignTo(this.inputEl(), this.listAlign);
40650         },
40651         
40652         onViewOver : function(e, t)
40653         {
40654             if(this.inKeyMode){
40655                 return;
40656             }
40657             var item = this.view.findItemFromChild(t);
40658             
40659             if(item){
40660                 var index = this.view.indexOf(item);
40661                 this.select(index, false);
40662             }
40663         },
40664
40665         // private
40666         onViewClick : function(view, doFocus, el, e)
40667         {
40668             var index = this.view.getSelectedIndexes()[0];
40669             
40670             var r = this.store.getAt(index);
40671             
40672             if(r){
40673                 this.onSelect(r, index);
40674             }
40675             if(doFocus !== false && !this.blockFocus){
40676                 this.inputEl().focus();
40677             }
40678         },
40679         
40680         onViewMove : function(e, t)
40681         {
40682             this.inKeyMode = false;
40683         },
40684         
40685         select : function(index, scrollIntoView)
40686         {
40687             this.selectedIndex = index;
40688             this.view.select(index);
40689             if(scrollIntoView !== false){
40690                 var el = this.view.getNode(index);
40691                 if(el){
40692                     this.list.scrollChildIntoView(el, false);
40693                 }
40694             }
40695         },
40696         
40697         createList : function()
40698         {
40699             this.list = Roo.get(document.body).createChild({
40700                 tag: 'ul',
40701                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40702                 style: 'display:none'
40703             });
40704             
40705             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40706         },
40707         
40708         collapseIf : function(e)
40709         {
40710             var in_combo  = e.within(this.el);
40711             var in_list =  e.within(this.list);
40712             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40713             
40714             if (in_combo || in_list || is_list) {
40715                 return;
40716             }
40717             this.collapse();
40718         },
40719         
40720         onSelect : function(record, index)
40721         {
40722             if(this.fireEvent('beforeselect', this, record, index) !== false){
40723                 
40724                 this.setFlagClass(record.data.iso2);
40725                 this.setDialCode(record.data.dialCode);
40726                 this.hasFocus = false;
40727                 this.collapse();
40728                 this.fireEvent('select', this, record, index);
40729             }
40730         },
40731         
40732         flagEl : function()
40733         {
40734             var flag = this.el.select('div.flag',true).first();
40735             if(!flag){
40736                 return false;
40737             }
40738             return flag;
40739         },
40740         
40741         dialCodeHolderEl : function()
40742         {
40743             var d = this.el.select('input.dial-code-holder',true).first();
40744             if(!d){
40745                 return false;
40746             }
40747             return d;
40748         },
40749         
40750         setDialCode : function(v)
40751         {
40752             this.dialCodeHolder.dom.value = '+'+v;
40753         },
40754         
40755         setFlagClass : function(n)
40756         {
40757             this.flag.dom.className = 'flag '+n;
40758         },
40759         
40760         getValue : function()
40761         {
40762             var v = this.inputEl().getValue();
40763             if(this.dialCodeHolder) {
40764                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40765             }
40766             return v;
40767         },
40768         
40769         setValue : function(v)
40770         {
40771             var d = this.getDialCode(v);
40772             
40773             //invalid dial code
40774             if(v.length == 0 || !d || d.length == 0) {
40775                 if(this.rendered){
40776                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40777                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40778                 }
40779                 return;
40780             }
40781             
40782             //valid dial code
40783             this.setFlagClass(this.dialCodeMapping[d].iso2);
40784             this.setDialCode(d);
40785             this.inputEl().dom.value = v.replace('+'+d,'');
40786             this.hiddenEl().dom.value = this.getValue();
40787             
40788             this.validate();
40789         },
40790         
40791         getDialCode : function(v)
40792         {
40793             v = v ||  '';
40794             
40795             if (v.length == 0) {
40796                 return this.dialCodeHolder.dom.value;
40797             }
40798             
40799             var dialCode = "";
40800             if (v.charAt(0) != "+") {
40801                 return false;
40802             }
40803             var numericChars = "";
40804             for (var i = 1; i < v.length; i++) {
40805               var c = v.charAt(i);
40806               if (!isNaN(c)) {
40807                 numericChars += c;
40808                 if (this.dialCodeMapping[numericChars]) {
40809                   dialCode = v.substr(1, i);
40810                 }
40811                 if (numericChars.length == 4) {
40812                   break;
40813                 }
40814               }
40815             }
40816             return dialCode;
40817         },
40818         
40819         reset : function()
40820         {
40821             this.setValue(this.defaultDialCode);
40822             this.validate();
40823         },
40824         
40825         hiddenEl : function()
40826         {
40827             return this.el.select('input.hidden-tel-input',true).first();
40828         },
40829         
40830         // after setting val
40831         onKeyUp : function(e){
40832             this.setValue(this.getValue());
40833         },
40834         
40835         onKeyPress : function(e){
40836             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40837                 e.stopEvent();
40838             }
40839         }
40840         
40841 });
40842 /**
40843  * @class Roo.bootstrap.MoneyField
40844  * @extends Roo.bootstrap.ComboBox
40845  * Bootstrap MoneyField class
40846  * 
40847  * @constructor
40848  * Create a new MoneyField.
40849  * @param {Object} config Configuration options
40850  */
40851
40852 Roo.bootstrap.MoneyField = function(config) {
40853     
40854     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40855     
40856 };
40857
40858 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40859     
40860     /**
40861      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40862      */
40863     allowDecimals : true,
40864     /**
40865      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40866      */
40867     decimalSeparator : ".",
40868     /**
40869      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40870      */
40871     decimalPrecision : 0,
40872     /**
40873      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40874      */
40875     allowNegative : true,
40876     /**
40877      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40878      */
40879     allowZero: true,
40880     /**
40881      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40882      */
40883     minValue : Number.NEGATIVE_INFINITY,
40884     /**
40885      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40886      */
40887     maxValue : Number.MAX_VALUE,
40888     /**
40889      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40890      */
40891     minText : "The minimum value for this field is {0}",
40892     /**
40893      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40894      */
40895     maxText : "The maximum value for this field is {0}",
40896     /**
40897      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40898      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40899      */
40900     nanText : "{0} is not a valid number",
40901     /**
40902      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40903      */
40904     castInt : true,
40905     /**
40906      * @cfg {String} defaults currency of the MoneyField
40907      * value should be in lkey
40908      */
40909     defaultCurrency : false,
40910     /**
40911      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40912      */
40913     thousandsDelimiter : false,
40914     /**
40915      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40916      */
40917     max_length: false,
40918     
40919     inputlg : 9,
40920     inputmd : 9,
40921     inputsm : 9,
40922     inputxs : 6,
40923     
40924     store : false,
40925     
40926     getAutoCreate : function()
40927     {
40928         var align = this.labelAlign || this.parentLabelAlign();
40929         
40930         var id = Roo.id();
40931
40932         var cfg = {
40933             cls: 'form-group',
40934             cn: []
40935         };
40936
40937         var input =  {
40938             tag: 'input',
40939             id : id,
40940             cls : 'form-control roo-money-amount-input',
40941             autocomplete: 'new-password'
40942         };
40943         
40944         var hiddenInput = {
40945             tag: 'input',
40946             type: 'hidden',
40947             id: Roo.id(),
40948             cls: 'hidden-number-input'
40949         };
40950         
40951         if(this.max_length) {
40952             input.maxlength = this.max_length; 
40953         }
40954         
40955         if (this.name) {
40956             hiddenInput.name = this.name;
40957         }
40958
40959         if (this.disabled) {
40960             input.disabled = true;
40961         }
40962
40963         var clg = 12 - this.inputlg;
40964         var cmd = 12 - this.inputmd;
40965         var csm = 12 - this.inputsm;
40966         var cxs = 12 - this.inputxs;
40967         
40968         var container = {
40969             tag : 'div',
40970             cls : 'row roo-money-field',
40971             cn : [
40972                 {
40973                     tag : 'div',
40974                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40975                     cn : [
40976                         {
40977                             tag : 'div',
40978                             cls: 'roo-select2-container input-group',
40979                             cn: [
40980                                 {
40981                                     tag : 'input',
40982                                     cls : 'form-control roo-money-currency-input',
40983                                     autocomplete: 'new-password',
40984                                     readOnly : 1,
40985                                     name : this.currencyName
40986                                 },
40987                                 {
40988                                     tag :'span',
40989                                     cls : 'input-group-addon',
40990                                     cn : [
40991                                         {
40992                                             tag: 'span',
40993                                             cls: 'caret'
40994                                         }
40995                                     ]
40996                                 }
40997                             ]
40998                         }
40999                     ]
41000                 },
41001                 {
41002                     tag : 'div',
41003                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
41004                     cn : [
41005                         {
41006                             tag: 'div',
41007                             cls: this.hasFeedback ? 'has-feedback' : '',
41008                             cn: [
41009                                 input
41010                             ]
41011                         }
41012                     ]
41013                 }
41014             ]
41015             
41016         };
41017         
41018         if (this.fieldLabel.length) {
41019             var indicator = {
41020                 tag: 'i',
41021                 tooltip: 'This field is required'
41022             };
41023
41024             var label = {
41025                 tag: 'label',
41026                 'for':  id,
41027                 cls: 'control-label',
41028                 cn: []
41029             };
41030
41031             var label_text = {
41032                 tag: 'span',
41033                 html: this.fieldLabel
41034             };
41035
41036             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
41037             label.cn = [
41038                 indicator,
41039                 label_text
41040             ];
41041
41042             if(this.indicatorpos == 'right') {
41043                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
41044                 label.cn = [
41045                     label_text,
41046                     indicator
41047                 ];
41048             }
41049
41050             if(align == 'left') {
41051                 container = {
41052                     tag: 'div',
41053                     cn: [
41054                         container
41055                     ]
41056                 };
41057
41058                 if(this.labelWidth > 12){
41059                     label.style = "width: " + this.labelWidth + 'px';
41060                 }
41061                 if(this.labelWidth < 13 && this.labelmd == 0){
41062                     this.labelmd = this.labelWidth;
41063                 }
41064                 if(this.labellg > 0){
41065                     label.cls += ' col-lg-' + this.labellg;
41066                     input.cls += ' col-lg-' + (12 - this.labellg);
41067                 }
41068                 if(this.labelmd > 0){
41069                     label.cls += ' col-md-' + this.labelmd;
41070                     container.cls += ' col-md-' + (12 - this.labelmd);
41071                 }
41072                 if(this.labelsm > 0){
41073                     label.cls += ' col-sm-' + this.labelsm;
41074                     container.cls += ' col-sm-' + (12 - this.labelsm);
41075                 }
41076                 if(this.labelxs > 0){
41077                     label.cls += ' col-xs-' + this.labelxs;
41078                     container.cls += ' col-xs-' + (12 - this.labelxs);
41079                 }
41080             }
41081         }
41082
41083         cfg.cn = [
41084             label,
41085             container,
41086             hiddenInput
41087         ];
41088         
41089         var settings = this;
41090
41091         ['xs','sm','md','lg'].map(function(size){
41092             if (settings[size]) {
41093                 cfg.cls += ' col-' + size + '-' + settings[size];
41094             }
41095         });
41096         
41097         return cfg;
41098     },
41099     
41100     initEvents : function()
41101     {
41102         this.indicator = this.indicatorEl();
41103         
41104         this.initCurrencyEvent();
41105         
41106         this.initNumberEvent();
41107     },
41108     
41109     initCurrencyEvent : function()
41110     {
41111         if (!this.store) {
41112             throw "can not find store for combo";
41113         }
41114         
41115         this.store = Roo.factory(this.store, Roo.data);
41116         this.store.parent = this;
41117         
41118         this.createList();
41119         
41120         this.triggerEl = this.el.select('.input-group-addon', true).first();
41121         
41122         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
41123         
41124         var _this = this;
41125         
41126         (function(){
41127             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
41128             _this.list.setWidth(lw);
41129         }).defer(100);
41130         
41131         this.list.on('mouseover', this.onViewOver, this);
41132         this.list.on('mousemove', this.onViewMove, this);
41133         this.list.on('scroll', this.onViewScroll, this);
41134         
41135         if(!this.tpl){
41136             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
41137         }
41138         
41139         this.view = new Roo.View(this.list, this.tpl, {
41140             singleSelect:true, store: this.store, selectedClass: this.selectedClass
41141         });
41142         
41143         this.view.on('click', this.onViewClick, this);
41144         
41145         this.store.on('beforeload', this.onBeforeLoad, this);
41146         this.store.on('load', this.onLoad, this);
41147         this.store.on('loadexception', this.onLoadException, this);
41148         
41149         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
41150             "up" : function(e){
41151                 this.inKeyMode = true;
41152                 this.selectPrev();
41153             },
41154
41155             "down" : function(e){
41156                 if(!this.isExpanded()){
41157                     this.onTriggerClick();
41158                 }else{
41159                     this.inKeyMode = true;
41160                     this.selectNext();
41161                 }
41162             },
41163
41164             "enter" : function(e){
41165                 this.collapse();
41166                 
41167                 if(this.fireEvent("specialkey", this, e)){
41168                     this.onViewClick(false);
41169                 }
41170                 
41171                 return true;
41172             },
41173
41174             "esc" : function(e){
41175                 this.collapse();
41176             },
41177
41178             "tab" : function(e){
41179                 this.collapse();
41180                 
41181                 if(this.fireEvent("specialkey", this, e)){
41182                     this.onViewClick(false);
41183                 }
41184                 
41185                 return true;
41186             },
41187
41188             scope : this,
41189
41190             doRelay : function(foo, bar, hname){
41191                 if(hname == 'down' || this.scope.isExpanded()){
41192                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41193                 }
41194                 return true;
41195             },
41196
41197             forceKeyDown: true
41198         });
41199         
41200         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
41201         
41202     },
41203     
41204     initNumberEvent : function(e)
41205     {
41206         this.inputEl().on("keydown" , this.fireKey,  this);
41207         this.inputEl().on("focus", this.onFocus,  this);
41208         this.inputEl().on("blur", this.onBlur,  this);
41209         
41210         this.inputEl().relayEvent('keyup', this);
41211         
41212         if(this.indicator){
41213             this.indicator.addClass('invisible');
41214         }
41215  
41216         this.originalValue = this.getValue();
41217         
41218         if(this.validationEvent == 'keyup'){
41219             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
41220             this.inputEl().on('keyup', this.filterValidation, this);
41221         }
41222         else if(this.validationEvent !== false){
41223             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
41224         }
41225         
41226         if(this.selectOnFocus){
41227             this.on("focus", this.preFocus, this);
41228             
41229         }
41230         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41231             this.inputEl().on("keypress", this.filterKeys, this);
41232         } else {
41233             this.inputEl().relayEvent('keypress', this);
41234         }
41235         
41236         var allowed = "0123456789";
41237         
41238         if(this.allowDecimals){
41239             allowed += this.decimalSeparator;
41240         }
41241         
41242         if(this.allowNegative){
41243             allowed += "-";
41244         }
41245         
41246         if(this.thousandsDelimiter) {
41247             allowed += ",";
41248         }
41249         
41250         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41251         
41252         var keyPress = function(e){
41253             
41254             var k = e.getKey();
41255             
41256             var c = e.getCharCode();
41257             
41258             if(
41259                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41260                     allowed.indexOf(String.fromCharCode(c)) === -1
41261             ){
41262                 e.stopEvent();
41263                 return;
41264             }
41265             
41266             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41267                 return;
41268             }
41269             
41270             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41271                 e.stopEvent();
41272             }
41273         };
41274         
41275         this.inputEl().on("keypress", keyPress, this);
41276         
41277     },
41278     
41279     onTriggerClick : function(e)
41280     {   
41281         if(this.disabled){
41282             return;
41283         }
41284         
41285         this.page = 0;
41286         this.loadNext = false;
41287         
41288         if(this.isExpanded()){
41289             this.collapse();
41290             return;
41291         }
41292         
41293         this.hasFocus = true;
41294         
41295         if(this.triggerAction == 'all') {
41296             this.doQuery(this.allQuery, true);
41297             return;
41298         }
41299         
41300         this.doQuery(this.getRawValue());
41301     },
41302     
41303     getCurrency : function()
41304     {   
41305         var v = this.currencyEl().getValue();
41306         
41307         return v;
41308     },
41309     
41310     restrictHeight : function()
41311     {
41312         this.list.alignTo(this.currencyEl(), this.listAlign);
41313         this.list.alignTo(this.currencyEl(), this.listAlign);
41314     },
41315     
41316     onViewClick : function(view, doFocus, el, e)
41317     {
41318         var index = this.view.getSelectedIndexes()[0];
41319         
41320         var r = this.store.getAt(index);
41321         
41322         if(r){
41323             this.onSelect(r, index);
41324         }
41325     },
41326     
41327     onSelect : function(record, index){
41328         
41329         if(this.fireEvent('beforeselect', this, record, index) !== false){
41330         
41331             this.setFromCurrencyData(index > -1 ? record.data : false);
41332             
41333             this.collapse();
41334             
41335             this.fireEvent('select', this, record, index);
41336         }
41337     },
41338     
41339     setFromCurrencyData : function(o)
41340     {
41341         var currency = '';
41342         
41343         this.lastCurrency = o;
41344         
41345         if (this.currencyField) {
41346             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41347         } else {
41348             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
41349         }
41350         
41351         this.lastSelectionText = currency;
41352         
41353         //setting default currency
41354         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41355             this.setCurrency(this.defaultCurrency);
41356             return;
41357         }
41358         
41359         this.setCurrency(currency);
41360     },
41361     
41362     setFromData : function(o)
41363     {
41364         var c = {};
41365         
41366         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41367         
41368         this.setFromCurrencyData(c);
41369         
41370         var value = '';
41371         
41372         if (this.name) {
41373             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41374         } else {
41375             Roo.log('no value set for '+ (this.name ? this.name : this.id));
41376         }
41377         
41378         this.setValue(value);
41379         
41380     },
41381     
41382     setCurrency : function(v)
41383     {   
41384         this.currencyValue = v;
41385         
41386         if(this.rendered){
41387             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41388             this.validate();
41389         }
41390     },
41391     
41392     setValue : function(v)
41393     {
41394         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41395         
41396         this.value = v;
41397         
41398         if(this.rendered){
41399             
41400             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41401             
41402             this.inputEl().dom.value = (v == '') ? '' :
41403                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41404             
41405             if(!this.allowZero && v === '0') {
41406                 this.hiddenEl().dom.value = '';
41407                 this.inputEl().dom.value = '';
41408             }
41409             
41410             this.validate();
41411         }
41412     },
41413     
41414     getRawValue : function()
41415     {
41416         var v = this.inputEl().getValue();
41417         
41418         return v;
41419     },
41420     
41421     getValue : function()
41422     {
41423         return this.fixPrecision(this.parseValue(this.getRawValue()));
41424     },
41425     
41426     parseValue : function(value)
41427     {
41428         if(this.thousandsDelimiter) {
41429             value += "";
41430             r = new RegExp(",", "g");
41431             value = value.replace(r, "");
41432         }
41433         
41434         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41435         return isNaN(value) ? '' : value;
41436         
41437     },
41438     
41439     fixPrecision : function(value)
41440     {
41441         if(this.thousandsDelimiter) {
41442             value += "";
41443             r = new RegExp(",", "g");
41444             value = value.replace(r, "");
41445         }
41446         
41447         var nan = isNaN(value);
41448         
41449         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41450             return nan ? '' : value;
41451         }
41452         return parseFloat(value).toFixed(this.decimalPrecision);
41453     },
41454     
41455     decimalPrecisionFcn : function(v)
41456     {
41457         return Math.floor(v);
41458     },
41459     
41460     validateValue : function(value)
41461     {
41462         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41463             return false;
41464         }
41465         
41466         var num = this.parseValue(value);
41467         
41468         if(isNaN(num)){
41469             this.markInvalid(String.format(this.nanText, value));
41470             return false;
41471         }
41472         
41473         if(num < this.minValue){
41474             this.markInvalid(String.format(this.minText, this.minValue));
41475             return false;
41476         }
41477         
41478         if(num > this.maxValue){
41479             this.markInvalid(String.format(this.maxText, this.maxValue));
41480             return false;
41481         }
41482         
41483         return true;
41484     },
41485     
41486     validate : function()
41487     {
41488         if(this.disabled || this.allowBlank){
41489             this.markValid();
41490             return true;
41491         }
41492         
41493         var currency = this.getCurrency();
41494         
41495         if(this.validateValue(this.getRawValue()) && currency.length){
41496             this.markValid();
41497             return true;
41498         }
41499         
41500         this.markInvalid();
41501         return false;
41502     },
41503     
41504     getName: function()
41505     {
41506         return this.name;
41507     },
41508     
41509     beforeBlur : function()
41510     {
41511         if(!this.castInt){
41512             return;
41513         }
41514         
41515         var v = this.parseValue(this.getRawValue());
41516         
41517         if(v || v == 0){
41518             this.setValue(v);
41519         }
41520     },
41521     
41522     onBlur : function()
41523     {
41524         this.beforeBlur();
41525         
41526         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41527             //this.el.removeClass(this.focusClass);
41528         }
41529         
41530         this.hasFocus = false;
41531         
41532         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41533             this.validate();
41534         }
41535         
41536         var v = this.getValue();
41537         
41538         if(String(v) !== String(this.startValue)){
41539             this.fireEvent('change', this, v, this.startValue);
41540         }
41541         
41542         this.fireEvent("blur", this);
41543     },
41544     
41545     inputEl : function()
41546     {
41547         return this.el.select('.roo-money-amount-input', true).first();
41548     },
41549     
41550     currencyEl : function()
41551     {
41552         return this.el.select('.roo-money-currency-input', true).first();
41553     },
41554     
41555     hiddenEl : function()
41556     {
41557         return this.el.select('input.hidden-number-input',true).first();
41558     }
41559     
41560 });/**
41561  * @class Roo.bootstrap.BezierSignature
41562  * @extends Roo.bootstrap.Component
41563  * Bootstrap BezierSignature class
41564  * This script refer to:
41565  *    Title: Signature Pad
41566  *    Author: szimek
41567  *    Availability: https://github.com/szimek/signature_pad
41568  *
41569  * @constructor
41570  * Create a new BezierSignature
41571  * @param {Object} config The config object
41572  */
41573
41574 Roo.bootstrap.BezierSignature = function(config){
41575     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
41576     this.addEvents({
41577         "resize" : true
41578     });
41579 };
41580
41581 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
41582 {
41583      
41584     curve_data: [],
41585     
41586     is_empty: true,
41587     
41588     mouse_btn_down: true,
41589     
41590     /**
41591      * @cfg {int} canvas height
41592      */
41593     canvas_height: '200px',
41594     
41595     /**
41596      * @cfg {float|function} Radius of a single dot.
41597      */ 
41598     dot_size: false,
41599     
41600     /**
41601      * @cfg {float} Minimum width of a line. Defaults to 0.5.
41602      */
41603     min_width: 0.5,
41604     
41605     /**
41606      * @cfg {float} Maximum width of a line. Defaults to 2.5.
41607      */
41608     max_width: 2.5,
41609     
41610     /**
41611      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
41612      */
41613     throttle: 16,
41614     
41615     /**
41616      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
41617      */
41618     min_distance: 5,
41619     
41620     /**
41621      * @cfg {string} Color used to clear the background. Can be any color format accepted by context.fillStyle. Defaults to "rgba(0,0,0,0)" (transparent black). Use a non-transparent color e.g. "rgb(255,255,255)" (opaque white) if you'd like to save signatures as JPEG images.
41622      */
41623     bg_color: 'rgba(0, 0, 0, 0)',
41624     
41625     /**
41626      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
41627      */
41628     dot_color: 'black',
41629     
41630     /**
41631      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
41632      */ 
41633     velocity_filter_weight: 0.7,
41634     
41635     /**
41636      * @cfg {function} Callback when stroke begin. 
41637      */
41638     onBegin: false,
41639     
41640     /**
41641      * @cfg {function} Callback when stroke end.
41642      */
41643     onEnd: false,
41644     
41645     getAutoCreate : function()
41646     {
41647         var cls = 'roo-signature column';
41648         
41649         if(this.cls){
41650             cls += ' ' + this.cls;
41651         }
41652         
41653         var col_sizes = [
41654             'lg',
41655             'md',
41656             'sm',
41657             'xs'
41658         ];
41659         
41660         for(var i = 0; i < col_sizes.length; i++) {
41661             if(this[col_sizes[i]]) {
41662                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
41663             }
41664         }
41665         
41666         var cfg = {
41667             tag: 'div',
41668             cls: cls,
41669             cn: [
41670                 {
41671                     tag: 'div',
41672                     cls: 'roo-signature-body',
41673                     cn: [
41674                         {
41675                             tag: 'canvas',
41676                             cls: 'roo-signature-body-canvas',
41677                             height: this.canvas_height,
41678                             width: this.canvas_width
41679                         }
41680                     ]
41681                 },
41682                 {
41683                     tag: 'input',
41684                     type: 'file',
41685                     style: 'display: none'
41686                 }
41687             ]
41688         };
41689         
41690         return cfg;
41691     },
41692     
41693     initEvents: function() 
41694     {
41695         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
41696         
41697         var canvas = this.canvasEl();
41698         
41699         // mouse && touch event swapping...
41700         canvas.dom.style.touchAction = 'none';
41701         canvas.dom.style.msTouchAction = 'none';
41702         
41703         this.mouse_btn_down = false;
41704         canvas.on('mousedown', this._handleMouseDown, this);
41705         canvas.on('mousemove', this._handleMouseMove, this);
41706         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
41707         
41708         if (window.PointerEvent) {
41709             canvas.on('pointerdown', this._handleMouseDown, this);
41710             canvas.on('pointermove', this._handleMouseMove, this);
41711             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
41712         }
41713         
41714         if ('ontouchstart' in window) {
41715             canvas.on('touchstart', this._handleTouchStart, this);
41716             canvas.on('touchmove', this._handleTouchMove, this);
41717             canvas.on('touchend', this._handleTouchEnd, this);
41718         }
41719         
41720         Roo.EventManager.onWindowResize(this.resize, this, true);
41721         
41722         // file input event
41723         this.fileEl().on('change', this.uploadImage, this);
41724         
41725         this.clear();
41726         
41727         this.resize();
41728     },
41729     
41730     resize: function(){
41731         
41732         var canvas = this.canvasEl().dom;
41733         var ctx = this.canvasElCtx();
41734         var img_data = false;
41735         
41736         if(canvas.width > 0) {
41737             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
41738         }
41739         // setting canvas width will clean img data
41740         canvas.width = 0;
41741         
41742         var style = window.getComputedStyle ? 
41743             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
41744             
41745         var padding_left = parseInt(style.paddingLeft) || 0;
41746         var padding_right = parseInt(style.paddingRight) || 0;
41747         
41748         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
41749         
41750         if(img_data) {
41751             ctx.putImageData(img_data, 0, 0);
41752         }
41753     },
41754     
41755     _handleMouseDown: function(e)
41756     {
41757         if (e.browserEvent.which === 1) {
41758             this.mouse_btn_down = true;
41759             this.strokeBegin(e);
41760         }
41761     },
41762     
41763     _handleMouseMove: function (e)
41764     {
41765         if (this.mouse_btn_down) {
41766             this.strokeMoveUpdate(e);
41767         }
41768     },
41769     
41770     _handleMouseUp: function (e)
41771     {
41772         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
41773             this.mouse_btn_down = false;
41774             this.strokeEnd(e);
41775         }
41776     },
41777     
41778     _handleTouchStart: function (e) {
41779         
41780         e.preventDefault();
41781         if (e.browserEvent.targetTouches.length === 1) {
41782             // var touch = e.browserEvent.changedTouches[0];
41783             // this.strokeBegin(touch);
41784             
41785              this.strokeBegin(e); // assume e catching the correct xy...
41786         }
41787     },
41788     
41789     _handleTouchMove: function (e) {
41790         e.preventDefault();
41791         // var touch = event.targetTouches[0];
41792         // _this._strokeMoveUpdate(touch);
41793         this.strokeMoveUpdate(e);
41794     },
41795     
41796     _handleTouchEnd: function (e) {
41797         var wasCanvasTouched = e.target === this.canvasEl().dom;
41798         if (wasCanvasTouched) {
41799             e.preventDefault();
41800             // var touch = event.changedTouches[0];
41801             // _this._strokeEnd(touch);
41802             this.strokeEnd(e);
41803         }
41804     },
41805     
41806     reset: function () {
41807         this._lastPoints = [];
41808         this._lastVelocity = 0;
41809         this._lastWidth = (this.min_width + this.max_width) / 2;
41810         this.canvasElCtx().fillStyle = this.dot_color;
41811     },
41812     
41813     strokeMoveUpdate: function(e)
41814     {
41815         this.strokeUpdate(e);
41816         
41817         if (this.throttle) {
41818             this.throttleStroke(this.strokeUpdate, this.throttle);
41819         }
41820         else {
41821             this.strokeUpdate(e);
41822         }
41823     },
41824     
41825     strokeBegin: function(e)
41826     {
41827         var newPointGroup = {
41828             color: this.dot_color,
41829             points: []
41830         };
41831         
41832         if (typeof this.onBegin === 'function') {
41833             this.onBegin(e);
41834         }
41835         
41836         this.curve_data.push(newPointGroup);
41837         this.reset();
41838         this.strokeUpdate(e);
41839     },
41840     
41841     strokeUpdate: function(e)
41842     {
41843         var rect = this.canvasEl().dom.getBoundingClientRect();
41844         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
41845         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
41846         var lastPoints = lastPointGroup.points;
41847         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
41848         var isLastPointTooClose = lastPoint
41849             ? point.distanceTo(lastPoint) <= this.min_distance
41850             : false;
41851         var color = lastPointGroup.color;
41852         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
41853             var curve = this.addPoint(point);
41854             if (!lastPoint) {
41855                 this.drawDot({color: color, point: point});
41856             }
41857             else if (curve) {
41858                 this.drawCurve({color: color, curve: curve});
41859             }
41860             lastPoints.push({
41861                 time: point.time,
41862                 x: point.x,
41863                 y: point.y
41864             });
41865         }
41866     },
41867     
41868     strokeEnd: function(e)
41869     {
41870         this.strokeUpdate(e);
41871         if (typeof this.onEnd === 'function') {
41872             this.onEnd(e);
41873         }
41874     },
41875     
41876     addPoint:  function (point) {
41877         var _lastPoints = this._lastPoints;
41878         _lastPoints.push(point);
41879         if (_lastPoints.length > 2) {
41880             if (_lastPoints.length === 3) {
41881                 _lastPoints.unshift(_lastPoints[0]);
41882             }
41883             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
41884             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
41885             _lastPoints.shift();
41886             return curve;
41887         }
41888         return null;
41889     },
41890     
41891     calculateCurveWidths: function (startPoint, endPoint) {
41892         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
41893             (1 - this.velocity_filter_weight) * this._lastVelocity;
41894
41895         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
41896         var widths = {
41897             end: newWidth,
41898             start: this._lastWidth
41899         };
41900         
41901         this._lastVelocity = velocity;
41902         this._lastWidth = newWidth;
41903         return widths;
41904     },
41905     
41906     drawDot: function (_a) {
41907         var color = _a.color, point = _a.point;
41908         var ctx = this.canvasElCtx();
41909         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
41910         ctx.beginPath();
41911         this.drawCurveSegment(point.x, point.y, width);
41912         ctx.closePath();
41913         ctx.fillStyle = color;
41914         ctx.fill();
41915     },
41916     
41917     drawCurve: function (_a) {
41918         var color = _a.color, curve = _a.curve;
41919         var ctx = this.canvasElCtx();
41920         var widthDelta = curve.endWidth - curve.startWidth;
41921         var drawSteps = Math.floor(curve.length()) * 2;
41922         ctx.beginPath();
41923         ctx.fillStyle = color;
41924         for (var i = 0; i < drawSteps; i += 1) {
41925         var t = i / drawSteps;
41926         var tt = t * t;
41927         var ttt = tt * t;
41928         var u = 1 - t;
41929         var uu = u * u;
41930         var uuu = uu * u;
41931         var x = uuu * curve.startPoint.x;
41932         x += 3 * uu * t * curve.control1.x;
41933         x += 3 * u * tt * curve.control2.x;
41934         x += ttt * curve.endPoint.x;
41935         var y = uuu * curve.startPoint.y;
41936         y += 3 * uu * t * curve.control1.y;
41937         y += 3 * u * tt * curve.control2.y;
41938         y += ttt * curve.endPoint.y;
41939         var width = curve.startWidth + ttt * widthDelta;
41940         this.drawCurveSegment(x, y, width);
41941         }
41942         ctx.closePath();
41943         ctx.fill();
41944     },
41945     
41946     drawCurveSegment: function (x, y, width) {
41947         var ctx = this.canvasElCtx();
41948         ctx.moveTo(x, y);
41949         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
41950         this.is_empty = false;
41951     },
41952     
41953     clear: function()
41954     {
41955         var ctx = this.canvasElCtx();
41956         var canvas = this.canvasEl().dom;
41957         ctx.fillStyle = this.bg_color;
41958         ctx.clearRect(0, 0, canvas.width, canvas.height);
41959         ctx.fillRect(0, 0, canvas.width, canvas.height);
41960         this.curve_data = [];
41961         this.reset();
41962         this.is_empty = true;
41963     },
41964     
41965     fileEl: function()
41966     {
41967         return  this.el.select('input',true).first();
41968     },
41969     
41970     canvasEl: function()
41971     {
41972         return this.el.select('canvas',true).first();
41973     },
41974     
41975     canvasElCtx: function()
41976     {
41977         return this.el.select('canvas',true).first().dom.getContext('2d');
41978     },
41979     
41980     getImage: function(type)
41981     {
41982         if(this.is_empty) {
41983             return false;
41984         }
41985         
41986         // encryption ?
41987         return this.canvasEl().dom.toDataURL('image/'+type, 1);
41988     },
41989     
41990     drawFromImage: function(img_src)
41991     {
41992         var img = new Image();
41993         
41994         img.onload = function(){
41995             this.canvasElCtx().drawImage(img, 0, 0);
41996         }.bind(this);
41997         
41998         img.src = img_src;
41999         
42000         this.is_empty = false;
42001     },
42002     
42003     selectImage: function()
42004     {
42005         this.fileEl().dom.click();
42006     },
42007     
42008     uploadImage: function(e)
42009     {
42010         var reader = new FileReader();
42011         
42012         reader.onload = function(e){
42013             var img = new Image();
42014             img.onload = function(){
42015                 this.reset();
42016                 this.canvasElCtx().drawImage(img, 0, 0);
42017             }.bind(this);
42018             img.src = e.target.result;
42019         }.bind(this);
42020         
42021         reader.readAsDataURL(e.target.files[0]);
42022     },
42023     
42024     // Bezier Point Constructor
42025     Point: (function () {
42026         function Point(x, y, time) {
42027             this.x = x;
42028             this.y = y;
42029             this.time = time || Date.now();
42030         }
42031         Point.prototype.distanceTo = function (start) {
42032             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
42033         };
42034         Point.prototype.equals = function (other) {
42035             return this.x === other.x && this.y === other.y && this.time === other.time;
42036         };
42037         Point.prototype.velocityFrom = function (start) {
42038             return this.time !== start.time
42039             ? this.distanceTo(start) / (this.time - start.time)
42040             : 0;
42041         };
42042         return Point;
42043     }()),
42044     
42045     
42046     // Bezier Constructor
42047     Bezier: (function () {
42048         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
42049             this.startPoint = startPoint;
42050             this.control2 = control2;
42051             this.control1 = control1;
42052             this.endPoint = endPoint;
42053             this.startWidth = startWidth;
42054             this.endWidth = endWidth;
42055         }
42056         Bezier.fromPoints = function (points, widths, scope) {
42057             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
42058             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
42059             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
42060         };
42061         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
42062             var dx1 = s1.x - s2.x;
42063             var dy1 = s1.y - s2.y;
42064             var dx2 = s2.x - s3.x;
42065             var dy2 = s2.y - s3.y;
42066             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
42067             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
42068             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
42069             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
42070             var dxm = m1.x - m2.x;
42071             var dym = m1.y - m2.y;
42072             var k = l2 / (l1 + l2);
42073             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
42074             var tx = s2.x - cm.x;
42075             var ty = s2.y - cm.y;
42076             return {
42077                 c1: new scope.Point(m1.x + tx, m1.y + ty),
42078                 c2: new scope.Point(m2.x + tx, m2.y + ty)
42079             };
42080         };
42081         Bezier.prototype.length = function () {
42082             var steps = 10;
42083             var length = 0;
42084             var px;
42085             var py;
42086             for (var i = 0; i <= steps; i += 1) {
42087                 var t = i / steps;
42088                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
42089                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
42090                 if (i > 0) {
42091                     var xdiff = cx - px;
42092                     var ydiff = cy - py;
42093                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
42094                 }
42095                 px = cx;
42096                 py = cy;
42097             }
42098             return length;
42099         };
42100         Bezier.prototype.point = function (t, start, c1, c2, end) {
42101             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
42102             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
42103             + (3.0 * c2 * (1.0 - t) * t * t)
42104             + (end * t * t * t);
42105         };
42106         return Bezier;
42107     }()),
42108     
42109     throttleStroke: function(fn, wait) {
42110       if (wait === void 0) { wait = 250; }
42111       var previous = 0;
42112       var timeout = null;
42113       var result;
42114       var storedContext;
42115       var storedArgs;
42116       var later = function () {
42117           previous = Date.now();
42118           timeout = null;
42119           result = fn.apply(storedContext, storedArgs);
42120           if (!timeout) {
42121               storedContext = null;
42122               storedArgs = [];
42123           }
42124       };
42125       return function wrapper() {
42126           var args = [];
42127           for (var _i = 0; _i < arguments.length; _i++) {
42128               args[_i] = arguments[_i];
42129           }
42130           var now = Date.now();
42131           var remaining = wait - (now - previous);
42132           storedContext = this;
42133           storedArgs = args;
42134           if (remaining <= 0 || remaining > wait) {
42135               if (timeout) {
42136                   clearTimeout(timeout);
42137                   timeout = null;
42138               }
42139               previous = now;
42140               result = fn.apply(storedContext, storedArgs);
42141               if (!timeout) {
42142                   storedContext = null;
42143                   storedArgs = [];
42144               }
42145           }
42146           else if (!timeout) {
42147               timeout = window.setTimeout(later, remaining);
42148           }
42149           return result;
42150       };
42151   }
42152   
42153 });
42154
42155  
42156
42157