roojs-bootstrap.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 + ' hidden' + size + '-down';;
1076                 return;
1077             }
1078             cfg.cls += ' col-' + size + '-' + settings[size] + (
1079                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1080             );
1081             
1082         });
1083         
1084         if (this.hidden) {
1085             cfg.cls += ' hidden';
1086         }
1087         
1088         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1089             cfg.cls +=' alert alert-' + this.alert;
1090         }
1091         
1092         
1093         if (this.html.length) {
1094             cfg.html = this.html;
1095         }
1096         if (this.fa) {
1097             var fasize = '';
1098             if (this.fasize > 1) {
1099                 fasize = ' fa-' + this.fasize + 'x';
1100             }
1101             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1102             
1103             
1104         }
1105         if (this.icon) {
1106             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1107         }
1108         
1109         return cfg;
1110     }
1111    
1112 });
1113
1114  
1115
1116  /*
1117  * - LGPL
1118  *
1119  * page container.
1120  * 
1121  */
1122
1123
1124 /**
1125  * @class Roo.bootstrap.Container
1126  * @extends Roo.bootstrap.Component
1127  * Bootstrap Container class
1128  * @cfg {Boolean} jumbotron is it a jumbotron element
1129  * @cfg {String} html content of element
1130  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1131  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1132  * @cfg {String} header content of header (for panel)
1133  * @cfg {String} footer content of footer (for panel)
1134  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1135  * @cfg {String} tag (header|aside|section) type of HTML tag.
1136  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1137  * @cfg {String} fa font awesome icon
1138  * @cfg {String} icon (info-sign|check|...) glyphicon name
1139  * @cfg {Boolean} hidden (true|false) hide the element
1140  * @cfg {Boolean} expandable (true|false) default false
1141  * @cfg {Boolean} expanded (true|false) default true
1142  * @cfg {String} rheader contet on the right of header
1143  * @cfg {Boolean} clickable (true|false) default false
1144
1145  *     
1146  * @constructor
1147  * Create a new Container
1148  * @param {Object} config The config object
1149  */
1150
1151 Roo.bootstrap.Container = function(config){
1152     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1153     
1154     this.addEvents({
1155         // raw events
1156          /**
1157          * @event expand
1158          * After the panel has been expand
1159          * 
1160          * @param {Roo.bootstrap.Container} this
1161          */
1162         "expand" : true,
1163         /**
1164          * @event collapse
1165          * After the panel has been collapsed
1166          * 
1167          * @param {Roo.bootstrap.Container} this
1168          */
1169         "collapse" : true,
1170         /**
1171          * @event click
1172          * When a element is chick
1173          * @param {Roo.bootstrap.Container} this
1174          * @param {Roo.EventObject} e
1175          */
1176         "click" : true
1177     });
1178 };
1179
1180 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1181     
1182     jumbotron : false,
1183     well: '',
1184     panel : '',
1185     header: '',
1186     footer : '',
1187     sticky: '',
1188     tag : false,
1189     alert : false,
1190     fa: false,
1191     icon : false,
1192     expandable : false,
1193     rheader : '',
1194     expanded : true,
1195     clickable: false,
1196   
1197      
1198     getChildContainer : function() {
1199         
1200         if(!this.el){
1201             return false;
1202         }
1203         
1204         if (this.panel.length) {
1205             return this.el.select('.panel-body',true).first();
1206         }
1207         
1208         return this.el;
1209     },
1210     
1211     
1212     getAutoCreate : function(){
1213         
1214         var cfg = {
1215             tag : this.tag || 'div',
1216             html : '',
1217             cls : ''
1218         };
1219         if (this.jumbotron) {
1220             cfg.cls = 'jumbotron';
1221         }
1222         
1223         
1224         
1225         // - this is applied by the parent..
1226         //if (this.cls) {
1227         //    cfg.cls = this.cls + '';
1228         //}
1229         
1230         if (this.sticky.length) {
1231             
1232             var bd = Roo.get(document.body);
1233             if (!bd.hasClass('bootstrap-sticky')) {
1234                 bd.addClass('bootstrap-sticky');
1235                 Roo.select('html',true).setStyle('height', '100%');
1236             }
1237              
1238             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1239         }
1240         
1241         
1242         if (this.well.length) {
1243             switch (this.well) {
1244                 case 'lg':
1245                 case 'sm':
1246                     cfg.cls +=' well well-' +this.well;
1247                     break;
1248                 default:
1249                     cfg.cls +=' well';
1250                     break;
1251             }
1252         }
1253         
1254         if (this.hidden) {
1255             cfg.cls += ' hidden';
1256         }
1257         
1258         
1259         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1260             cfg.cls +=' alert alert-' + this.alert;
1261         }
1262         
1263         var body = cfg;
1264         
1265         if (this.panel.length) {
1266             cfg.cls += ' panel panel-' + this.panel;
1267             cfg.cn = [];
1268             if (this.header.length) {
1269                 
1270                 var h = [];
1271                 
1272                 if(this.expandable){
1273                     
1274                     cfg.cls = cfg.cls + ' expandable';
1275                     
1276                     h.push({
1277                         tag: 'i',
1278                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1279                     });
1280                     
1281                 }
1282                 
1283                 h.push(
1284                     {
1285                         tag: 'span',
1286                         cls : 'panel-title',
1287                         html : (this.expandable ? '&nbsp;' : '') + this.header
1288                     },
1289                     {
1290                         tag: 'span',
1291                         cls: 'panel-header-right',
1292                         html: this.rheader
1293                     }
1294                 );
1295                 
1296                 cfg.cn.push({
1297                     cls : 'panel-heading',
1298                     style : this.expandable ? 'cursor: pointer' : '',
1299                     cn : h
1300                 });
1301                 
1302             }
1303             
1304             body = false;
1305             cfg.cn.push({
1306                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1307                 html : this.html
1308             });
1309             
1310             
1311             if (this.footer.length) {
1312                 cfg.cn.push({
1313                     cls : 'panel-footer',
1314                     html : this.footer
1315                     
1316                 });
1317             }
1318             
1319         }
1320         
1321         if (body) {
1322             body.html = this.html || cfg.html;
1323             // prefix with the icons..
1324             if (this.fa) {
1325                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1326             }
1327             if (this.icon) {
1328                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1329             }
1330             
1331             
1332         }
1333         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1334             cfg.cls =  'container';
1335         }
1336         
1337         return cfg;
1338     },
1339     
1340     initEvents: function() 
1341     {
1342         if(this.expandable){
1343             var headerEl = this.headerEl();
1344         
1345             if(headerEl){
1346                 headerEl.on('click', this.onToggleClick, this);
1347             }
1348         }
1349         
1350         if(this.clickable){
1351             this.el.on('click', this.onClick, this);
1352         }
1353         
1354     },
1355     
1356     onToggleClick : function()
1357     {
1358         var headerEl = this.headerEl();
1359         
1360         if(!headerEl){
1361             return;
1362         }
1363         
1364         if(this.expanded){
1365             this.collapse();
1366             return;
1367         }
1368         
1369         this.expand();
1370     },
1371     
1372     expand : function()
1373     {
1374         if(this.fireEvent('expand', this)) {
1375             
1376             this.expanded = true;
1377             
1378             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1379             
1380             this.el.select('.panel-body',true).first().removeClass('hide');
1381             
1382             var toggleEl = this.toggleEl();
1383
1384             if(!toggleEl){
1385                 return;
1386             }
1387
1388             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1389         }
1390         
1391     },
1392     
1393     collapse : function()
1394     {
1395         if(this.fireEvent('collapse', this)) {
1396             
1397             this.expanded = false;
1398             
1399             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1400             this.el.select('.panel-body',true).first().addClass('hide');
1401         
1402             var toggleEl = this.toggleEl();
1403
1404             if(!toggleEl){
1405                 return;
1406             }
1407
1408             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1409         }
1410     },
1411     
1412     toggleEl : function()
1413     {
1414         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1415             return;
1416         }
1417         
1418         return this.el.select('.panel-heading .fa',true).first();
1419     },
1420     
1421     headerEl : function()
1422     {
1423         if(!this.el || !this.panel.length || !this.header.length){
1424             return;
1425         }
1426         
1427         return this.el.select('.panel-heading',true).first()
1428     },
1429     
1430     bodyEl : function()
1431     {
1432         if(!this.el || !this.panel.length){
1433             return;
1434         }
1435         
1436         return this.el.select('.panel-body',true).first()
1437     },
1438     
1439     titleEl : function()
1440     {
1441         if(!this.el || !this.panel.length || !this.header.length){
1442             return;
1443         }
1444         
1445         return this.el.select('.panel-title',true).first();
1446     },
1447     
1448     setTitle : function(v)
1449     {
1450         var titleEl = this.titleEl();
1451         
1452         if(!titleEl){
1453             return;
1454         }
1455         
1456         titleEl.dom.innerHTML = v;
1457     },
1458     
1459     getTitle : function()
1460     {
1461         
1462         var titleEl = this.titleEl();
1463         
1464         if(!titleEl){
1465             return '';
1466         }
1467         
1468         return titleEl.dom.innerHTML;
1469     },
1470     
1471     setRightTitle : function(v)
1472     {
1473         var t = this.el.select('.panel-header-right',true).first();
1474         
1475         if(!t){
1476             return;
1477         }
1478         
1479         t.dom.innerHTML = v;
1480     },
1481     
1482     onClick : function(e)
1483     {
1484         e.preventDefault();
1485         
1486         this.fireEvent('click', this, e);
1487     }
1488 });
1489
1490  /*
1491  * - LGPL
1492  *
1493  * image
1494  * 
1495  */
1496
1497
1498 /**
1499  * @class Roo.bootstrap.Img
1500  * @extends Roo.bootstrap.Component
1501  * Bootstrap Img class
1502  * @cfg {Boolean} imgResponsive false | true
1503  * @cfg {String} border rounded | circle | thumbnail
1504  * @cfg {String} src image source
1505  * @cfg {String} alt image alternative text
1506  * @cfg {String} href a tag href
1507  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1508  * @cfg {String} xsUrl xs image source
1509  * @cfg {String} smUrl sm image source
1510  * @cfg {String} mdUrl md image source
1511  * @cfg {String} lgUrl lg image source
1512  * 
1513  * @constructor
1514  * Create a new Input
1515  * @param {Object} config The config object
1516  */
1517
1518 Roo.bootstrap.Img = function(config){
1519     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1520     
1521     this.addEvents({
1522         // img events
1523         /**
1524          * @event click
1525          * The img click event for the img.
1526          * @param {Roo.EventObject} e
1527          */
1528         "click" : true
1529     });
1530 };
1531
1532 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1533     
1534     imgResponsive: true,
1535     border: '',
1536     src: 'about:blank',
1537     href: false,
1538     target: false,
1539     xsUrl: '',
1540     smUrl: '',
1541     mdUrl: '',
1542     lgUrl: '',
1543
1544     getAutoCreate : function()
1545     {   
1546         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1547             return this.createSingleImg();
1548         }
1549         
1550         var cfg = {
1551             tag: 'div',
1552             cls: 'roo-image-responsive-group',
1553             cn: []
1554         };
1555         var _this = this;
1556         
1557         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1558             
1559             if(!_this[size + 'Url']){
1560                 return;
1561             }
1562             
1563             var img = {
1564                 tag: 'img',
1565                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1566                 html: _this.html || cfg.html,
1567                 src: _this[size + 'Url']
1568             };
1569             
1570             img.cls += ' roo-image-responsive-' + size;
1571             
1572             var s = ['xs', 'sm', 'md', 'lg'];
1573             
1574             s.splice(s.indexOf(size), 1);
1575             
1576             Roo.each(s, function(ss){
1577                 img.cls += ' hidden-' + ss;
1578             });
1579             
1580             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1581                 cfg.cls += ' img-' + _this.border;
1582             }
1583             
1584             if(_this.alt){
1585                 cfg.alt = _this.alt;
1586             }
1587             
1588             if(_this.href){
1589                 var a = {
1590                     tag: 'a',
1591                     href: _this.href,
1592                     cn: [
1593                         img
1594                     ]
1595                 };
1596
1597                 if(this.target){
1598                     a.target = _this.target;
1599                 }
1600             }
1601             
1602             cfg.cn.push((_this.href) ? a : img);
1603             
1604         });
1605         
1606         return cfg;
1607     },
1608     
1609     createSingleImg : function()
1610     {
1611         var cfg = {
1612             tag: 'img',
1613             cls: (this.imgResponsive) ? 'img-responsive' : '',
1614             html : null,
1615             src : 'about:blank'  // just incase src get's set to undefined?!?
1616         };
1617         
1618         cfg.html = this.html || cfg.html;
1619         
1620         cfg.src = this.src || cfg.src;
1621         
1622         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1623             cfg.cls += ' img-' + this.border;
1624         }
1625         
1626         if(this.alt){
1627             cfg.alt = this.alt;
1628         }
1629         
1630         if(this.href){
1631             var a = {
1632                 tag: 'a',
1633                 href: this.href,
1634                 cn: [
1635                     cfg
1636                 ]
1637             };
1638             
1639             if(this.target){
1640                 a.target = this.target;
1641             }
1642             
1643         }
1644         
1645         return (this.href) ? a : cfg;
1646     },
1647     
1648     initEvents: function() 
1649     {
1650         if(!this.href){
1651             this.el.on('click', this.onClick, this);
1652         }
1653         
1654     },
1655     
1656     onClick : function(e)
1657     {
1658         Roo.log('img onclick');
1659         this.fireEvent('click', this, e);
1660     },
1661     /**
1662      * Sets the url of the image - used to update it
1663      * @param {String} url the url of the image
1664      */
1665     
1666     setSrc : function(url)
1667     {
1668         this.src =  url;
1669         
1670         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1671             this.el.dom.src =  url;
1672             return;
1673         }
1674         
1675         this.el.select('img', true).first().dom.src =  url;
1676     }
1677     
1678     
1679    
1680 });
1681
1682  /*
1683  * - LGPL
1684  *
1685  * image
1686  * 
1687  */
1688
1689
1690 /**
1691  * @class Roo.bootstrap.Link
1692  * @extends Roo.bootstrap.Component
1693  * Bootstrap Link Class
1694  * @cfg {String} alt image alternative text
1695  * @cfg {String} href a tag href
1696  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1697  * @cfg {String} html the content of the link.
1698  * @cfg {String} anchor name for the anchor link
1699  * @cfg {String} fa - favicon
1700
1701  * @cfg {Boolean} preventDefault (true | false) default false
1702
1703  * 
1704  * @constructor
1705  * Create a new Input
1706  * @param {Object} config The config object
1707  */
1708
1709 Roo.bootstrap.Link = function(config){
1710     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1711     
1712     this.addEvents({
1713         // img events
1714         /**
1715          * @event click
1716          * The img click event for the img.
1717          * @param {Roo.EventObject} e
1718          */
1719         "click" : true
1720     });
1721 };
1722
1723 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1724     
1725     href: false,
1726     target: false,
1727     preventDefault: false,
1728     anchor : false,
1729     alt : false,
1730     fa: false,
1731
1732
1733     getAutoCreate : function()
1734     {
1735         var html = this.html || '';
1736         
1737         if (this.fa !== false) {
1738             html = '<i class="fa fa-' + this.fa + '"></i>';
1739         }
1740         var cfg = {
1741             tag: 'a'
1742         };
1743         // anchor's do not require html/href...
1744         if (this.anchor === false) {
1745             cfg.html = html;
1746             cfg.href = this.href || '#';
1747         } else {
1748             cfg.name = this.anchor;
1749             if (this.html !== false || this.fa !== false) {
1750                 cfg.html = html;
1751             }
1752             if (this.href !== false) {
1753                 cfg.href = this.href;
1754             }
1755         }
1756         
1757         if(this.alt !== false){
1758             cfg.alt = this.alt;
1759         }
1760         
1761         
1762         if(this.target !== false) {
1763             cfg.target = this.target;
1764         }
1765         
1766         return cfg;
1767     },
1768     
1769     initEvents: function() {
1770         
1771         if(!this.href || this.preventDefault){
1772             this.el.on('click', this.onClick, this);
1773         }
1774     },
1775     
1776     onClick : function(e)
1777     {
1778         if(this.preventDefault){
1779             e.preventDefault();
1780         }
1781         //Roo.log('img onclick');
1782         this.fireEvent('click', this, e);
1783     }
1784    
1785 });
1786
1787  /*
1788  * - LGPL
1789  *
1790  * header
1791  * 
1792  */
1793
1794 /**
1795  * @class Roo.bootstrap.Header
1796  * @extends Roo.bootstrap.Component
1797  * Bootstrap Header class
1798  * @cfg {String} html content of header
1799  * @cfg {Number} level (1|2|3|4|5|6) default 1
1800  * 
1801  * @constructor
1802  * Create a new Header
1803  * @param {Object} config The config object
1804  */
1805
1806
1807 Roo.bootstrap.Header  = function(config){
1808     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1809 };
1810
1811 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1812     
1813     //href : false,
1814     html : false,
1815     level : 1,
1816     
1817     
1818     
1819     getAutoCreate : function(){
1820         
1821         
1822         
1823         var cfg = {
1824             tag: 'h' + (1 *this.level),
1825             html: this.html || ''
1826         } ;
1827         
1828         return cfg;
1829     }
1830    
1831 });
1832
1833  
1834
1835  /*
1836  * Based on:
1837  * Ext JS Library 1.1.1
1838  * Copyright(c) 2006-2007, Ext JS, LLC.
1839  *
1840  * Originally Released Under LGPL - original licence link has changed is not relivant.
1841  *
1842  * Fork - LGPL
1843  * <script type="text/javascript">
1844  */
1845  
1846 /**
1847  * @class Roo.bootstrap.MenuMgr
1848  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1849  * @singleton
1850  */
1851 Roo.bootstrap.MenuMgr = function(){
1852    var menus, active, groups = {}, attached = false, lastShow = new Date();
1853
1854    // private - called when first menu is created
1855    function init(){
1856        menus = {};
1857        active = new Roo.util.MixedCollection();
1858        Roo.get(document).addKeyListener(27, function(){
1859            if(active.length > 0){
1860                hideAll();
1861            }
1862        });
1863    }
1864
1865    // private
1866    function hideAll(){
1867        if(active && active.length > 0){
1868            var c = active.clone();
1869            c.each(function(m){
1870                m.hide();
1871            });
1872        }
1873    }
1874
1875    // private
1876    function onHide(m){
1877        active.remove(m);
1878        if(active.length < 1){
1879            Roo.get(document).un("mouseup", onMouseDown);
1880             
1881            attached = false;
1882        }
1883    }
1884
1885    // private
1886    function onShow(m){
1887        var last = active.last();
1888        lastShow = new Date();
1889        active.add(m);
1890        if(!attached){
1891           Roo.get(document).on("mouseup", onMouseDown);
1892            
1893            attached = true;
1894        }
1895        if(m.parentMenu){
1896           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1897           m.parentMenu.activeChild = m;
1898        }else if(last && last.isVisible()){
1899           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1900        }
1901    }
1902
1903    // private
1904    function onBeforeHide(m){
1905        if(m.activeChild){
1906            m.activeChild.hide();
1907        }
1908        if(m.autoHideTimer){
1909            clearTimeout(m.autoHideTimer);
1910            delete m.autoHideTimer;
1911        }
1912    }
1913
1914    // private
1915    function onBeforeShow(m){
1916        var pm = m.parentMenu;
1917        if(!pm && !m.allowOtherMenus){
1918            hideAll();
1919        }else if(pm && pm.activeChild && active != m){
1920            pm.activeChild.hide();
1921        }
1922    }
1923
1924    // private this should really trigger on mouseup..
1925    function onMouseDown(e){
1926         Roo.log("on Mouse Up");
1927         
1928         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1929             Roo.log("MenuManager hideAll");
1930             hideAll();
1931             e.stopEvent();
1932         }
1933         
1934         
1935    }
1936
1937    // private
1938    function onBeforeCheck(mi, state){
1939        if(state){
1940            var g = groups[mi.group];
1941            for(var i = 0, l = g.length; i < l; i++){
1942                if(g[i] != mi){
1943                    g[i].setChecked(false);
1944                }
1945            }
1946        }
1947    }
1948
1949    return {
1950
1951        /**
1952         * Hides all menus that are currently visible
1953         */
1954        hideAll : function(){
1955             hideAll();  
1956        },
1957
1958        // private
1959        register : function(menu){
1960            if(!menus){
1961                init();
1962            }
1963            menus[menu.id] = menu;
1964            menu.on("beforehide", onBeforeHide);
1965            menu.on("hide", onHide);
1966            menu.on("beforeshow", onBeforeShow);
1967            menu.on("show", onShow);
1968            var g = menu.group;
1969            if(g && menu.events["checkchange"]){
1970                if(!groups[g]){
1971                    groups[g] = [];
1972                }
1973                groups[g].push(menu);
1974                menu.on("checkchange", onCheck);
1975            }
1976        },
1977
1978         /**
1979          * Returns a {@link Roo.menu.Menu} object
1980          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1981          * be used to generate and return a new Menu instance.
1982          */
1983        get : function(menu){
1984            if(typeof menu == "string"){ // menu id
1985                return menus[menu];
1986            }else if(menu.events){  // menu instance
1987                return menu;
1988            }
1989            /*else if(typeof menu.length == 'number'){ // array of menu items?
1990                return new Roo.bootstrap.Menu({items:menu});
1991            }else{ // otherwise, must be a config
1992                return new Roo.bootstrap.Menu(menu);
1993            }
1994            */
1995            return false;
1996        },
1997
1998        // private
1999        unregister : function(menu){
2000            delete menus[menu.id];
2001            menu.un("beforehide", onBeforeHide);
2002            menu.un("hide", onHide);
2003            menu.un("beforeshow", onBeforeShow);
2004            menu.un("show", onShow);
2005            var g = menu.group;
2006            if(g && menu.events["checkchange"]){
2007                groups[g].remove(menu);
2008                menu.un("checkchange", onCheck);
2009            }
2010        },
2011
2012        // private
2013        registerCheckable : function(menuItem){
2014            var g = menuItem.group;
2015            if(g){
2016                if(!groups[g]){
2017                    groups[g] = [];
2018                }
2019                groups[g].push(menuItem);
2020                menuItem.on("beforecheckchange", onBeforeCheck);
2021            }
2022        },
2023
2024        // private
2025        unregisterCheckable : function(menuItem){
2026            var g = menuItem.group;
2027            if(g){
2028                groups[g].remove(menuItem);
2029                menuItem.un("beforecheckchange", onBeforeCheck);
2030            }
2031        }
2032    };
2033 }();/*
2034  * - LGPL
2035  *
2036  * menu
2037  * 
2038  */
2039
2040 /**
2041  * @class Roo.bootstrap.Menu
2042  * @extends Roo.bootstrap.Component
2043  * Bootstrap Menu class - container for MenuItems
2044  * @cfg {String} type (dropdown|treeview|submenu) type of menu
2045  * @cfg {bool} hidden  if the menu should be hidden when rendered.
2046  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
2047  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
2048  * 
2049  * @constructor
2050  * Create a new Menu
2051  * @param {Object} config The config object
2052  */
2053
2054
2055 Roo.bootstrap.Menu = function(config){
2056     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2057     if (this.registerMenu && this.type != 'treeview')  {
2058         Roo.bootstrap.MenuMgr.register(this);
2059     }
2060     
2061     
2062     this.addEvents({
2063         /**
2064          * @event beforeshow
2065          * Fires before this menu is displayed (return false to block)
2066          * @param {Roo.menu.Menu} this
2067          */
2068         beforeshow : true,
2069         /**
2070          * @event beforehide
2071          * Fires before this menu is hidden (return false to block)
2072          * @param {Roo.menu.Menu} this
2073          */
2074         beforehide : true,
2075         /**
2076          * @event show
2077          * Fires after this menu is displayed
2078          * @param {Roo.menu.Menu} this
2079          */
2080         show : true,
2081         /**
2082          * @event hide
2083          * Fires after this menu is hidden
2084          * @param {Roo.menu.Menu} this
2085          */
2086         hide : true,
2087         /**
2088          * @event click
2089          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2090          * @param {Roo.menu.Menu} this
2091          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2092          * @param {Roo.EventObject} e
2093          */
2094         click : true,
2095         /**
2096          * @event mouseover
2097          * Fires when the mouse is hovering over this menu
2098          * @param {Roo.menu.Menu} this
2099          * @param {Roo.EventObject} e
2100          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2101          */
2102         mouseover : true,
2103         /**
2104          * @event mouseout
2105          * Fires when the mouse exits this menu
2106          * @param {Roo.menu.Menu} this
2107          * @param {Roo.EventObject} e
2108          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2109          */
2110         mouseout : true,
2111         /**
2112          * @event itemclick
2113          * Fires when a menu item contained in this menu is clicked
2114          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2115          * @param {Roo.EventObject} e
2116          */
2117         itemclick: true
2118     });
2119     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2120 };
2121
2122 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2123     
2124    /// html : false,
2125     //align : '',
2126     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2127     type: false,
2128     /**
2129      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2130      */
2131     registerMenu : true,
2132     
2133     menuItems :false, // stores the menu items..
2134     
2135     hidden:true,
2136         
2137     parentMenu : false,
2138     
2139     stopEvent : true,
2140     
2141     isLink : false,
2142     
2143     getChildContainer : function() {
2144         return this.el;  
2145     },
2146     
2147     getAutoCreate : function(){
2148          
2149         //if (['right'].indexOf(this.align)!==-1) {
2150         //    cfg.cn[1].cls += ' pull-right'
2151         //}
2152         
2153         
2154         var cfg = {
2155             tag : 'ul',
2156             cls : 'dropdown-menu' ,
2157             style : 'z-index:1000'
2158             
2159         };
2160         
2161         if (this.type === 'submenu') {
2162             cfg.cls = 'submenu active';
2163         }
2164         if (this.type === 'treeview') {
2165             cfg.cls = 'treeview-menu';
2166         }
2167         
2168         return cfg;
2169     },
2170     initEvents : function() {
2171         
2172        // Roo.log("ADD event");
2173        // Roo.log(this.triggerEl.dom);
2174         
2175         this.triggerEl.on('click', this.onTriggerClick, this);
2176         
2177         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2178         
2179         
2180         if (this.triggerEl.hasClass('nav-item')) {
2181             // dropdown toggle on the 'a' in BS4?
2182             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
2183         } else {
2184             this.triggerEl.addClass('dropdown-toggle');
2185         }
2186         if (Roo.isTouch) {
2187             this.el.on('touchstart'  , this.onTouch, this);
2188         }
2189         this.el.on('click' , this.onClick, this);
2190
2191         this.el.on("mouseover", this.onMouseOver, this);
2192         this.el.on("mouseout", this.onMouseOut, this);
2193         
2194     },
2195     
2196     findTargetItem : function(e)
2197     {
2198         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2199         if(!t){
2200             return false;
2201         }
2202         //Roo.log(t);         Roo.log(t.id);
2203         if(t && t.id){
2204             //Roo.log(this.menuitems);
2205             return this.menuitems.get(t.id);
2206             
2207             //return this.items.get(t.menuItemId);
2208         }
2209         
2210         return false;
2211     },
2212     
2213     onTouch : function(e) 
2214     {
2215         Roo.log("menu.onTouch");
2216         //e.stopEvent(); this make the user popdown broken
2217         this.onClick(e);
2218     },
2219     
2220     onClick : function(e)
2221     {
2222         Roo.log("menu.onClick");
2223         
2224         var t = this.findTargetItem(e);
2225         if(!t || t.isContainer){
2226             return;
2227         }
2228         Roo.log(e);
2229         /*
2230         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2231             if(t == this.activeItem && t.shouldDeactivate(e)){
2232                 this.activeItem.deactivate();
2233                 delete this.activeItem;
2234                 return;
2235             }
2236             if(t.canActivate){
2237                 this.setActiveItem(t, true);
2238             }
2239             return;
2240             
2241             
2242         }
2243         */
2244        
2245         Roo.log('pass click event');
2246         
2247         t.onClick(e);
2248         
2249         this.fireEvent("click", this, t, e);
2250         
2251         var _this = this;
2252         
2253         if(!t.href.length || t.href == '#'){
2254             (function() { _this.hide(); }).defer(100);
2255         }
2256         
2257     },
2258     
2259     onMouseOver : function(e){
2260         var t  = this.findTargetItem(e);
2261         //Roo.log(t);
2262         //if(t){
2263         //    if(t.canActivate && !t.disabled){
2264         //        this.setActiveItem(t, true);
2265         //    }
2266         //}
2267         
2268         this.fireEvent("mouseover", this, e, t);
2269     },
2270     isVisible : function(){
2271         return !this.hidden;
2272     },
2273     onMouseOut : function(e){
2274         var t  = this.findTargetItem(e);
2275         
2276         //if(t ){
2277         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2278         //        this.activeItem.deactivate();
2279         //        delete this.activeItem;
2280         //    }
2281         //}
2282         this.fireEvent("mouseout", this, e, t);
2283     },
2284     
2285     
2286     /**
2287      * Displays this menu relative to another element
2288      * @param {String/HTMLElement/Roo.Element} element The element to align to
2289      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2290      * the element (defaults to this.defaultAlign)
2291      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2292      */
2293     show : function(el, pos, parentMenu)
2294     {
2295         if (false === this.fireEvent("beforeshow", this)) {
2296             Roo.log("show canceled");
2297             return;
2298         }
2299         this.parentMenu = parentMenu;
2300         if(!this.el){
2301             this.render();
2302         }
2303         
2304         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2305     },
2306      /**
2307      * Displays this menu at a specific xy position
2308      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2309      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2310      */
2311     showAt : function(xy, parentMenu, /* private: */_e){
2312         this.parentMenu = parentMenu;
2313         if(!this.el){
2314             this.render();
2315         }
2316         if(_e !== false){
2317             this.fireEvent("beforeshow", this);
2318             //xy = this.el.adjustForConstraints(xy);
2319         }
2320         
2321         //this.el.show();
2322         this.hideMenuItems();
2323         this.hidden = false;
2324         this.triggerEl.addClass('open');
2325         this.el.addClass('show');
2326         
2327         // reassign x when hitting right
2328         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2329             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2330         }
2331         
2332         // reassign y when hitting bottom
2333         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2334             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2335         }
2336         
2337         // but the list may align on trigger left or trigger top... should it be a properity?
2338         
2339         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2340             this.el.setXY(xy);
2341         }
2342         
2343         this.focus();
2344         this.fireEvent("show", this);
2345     },
2346     
2347     focus : function(){
2348         return;
2349         if(!this.hidden){
2350             this.doFocus.defer(50, this);
2351         }
2352     },
2353
2354     doFocus : function(){
2355         if(!this.hidden){
2356             this.focusEl.focus();
2357         }
2358     },
2359
2360     /**
2361      * Hides this menu and optionally all parent menus
2362      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2363      */
2364     hide : function(deep)
2365     {
2366         if (false === this.fireEvent("beforehide", this)) {
2367             Roo.log("hide canceled");
2368             return;
2369         }
2370         this.hideMenuItems();
2371         if(this.el && this.isVisible()){
2372            
2373             if(this.activeItem){
2374                 this.activeItem.deactivate();
2375                 this.activeItem = null;
2376             }
2377             this.triggerEl.removeClass('open');;
2378             this.el.removeClass('show');
2379             this.hidden = true;
2380             this.fireEvent("hide", this);
2381         }
2382         if(deep === true && this.parentMenu){
2383             this.parentMenu.hide(true);
2384         }
2385     },
2386     
2387     onTriggerClick : function(e)
2388     {
2389         Roo.log('trigger click');
2390         
2391         var target = e.getTarget();
2392         
2393         Roo.log(target.nodeName.toLowerCase());
2394         
2395         if(target.nodeName.toLowerCase() === 'i'){
2396             e.preventDefault();
2397         }
2398         
2399     },
2400     
2401     onTriggerPress  : function(e)
2402     {
2403         Roo.log('trigger press');
2404         //Roo.log(e.getTarget());
2405        // Roo.log(this.triggerEl.dom);
2406        
2407         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2408         var pel = Roo.get(e.getTarget());
2409         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2410             Roo.log('is treeview or dropdown?');
2411             return;
2412         }
2413         
2414         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2415             return;
2416         }
2417         
2418         if (this.isVisible()) {
2419             Roo.log('hide');
2420             this.hide();
2421         } else {
2422             Roo.log('show');
2423             this.show(this.triggerEl, '?', false);
2424         }
2425         
2426         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2427             e.stopEvent();
2428         }
2429         
2430     },
2431        
2432     
2433     hideMenuItems : function()
2434     {
2435         Roo.log("hide Menu Items");
2436         if (!this.el) { 
2437             return;
2438         }
2439         
2440         this.el.select('.open',true).each(function(aa) {
2441             
2442             aa.removeClass('open');
2443          
2444         });
2445     },
2446     addxtypeChild : function (tree, cntr) {
2447         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2448           
2449         this.menuitems.add(comp);
2450         return comp;
2451
2452     },
2453     getEl : function()
2454     {
2455         Roo.log(this.el);
2456         return this.el;
2457     },
2458     
2459     clear : function()
2460     {
2461         this.getEl().dom.innerHTML = '';
2462         this.menuitems.clear();
2463     }
2464 });
2465
2466  
2467  /*
2468  * - LGPL
2469  *
2470  * menu item
2471  * 
2472  */
2473
2474
2475 /**
2476  * @class Roo.bootstrap.MenuItem
2477  * @extends Roo.bootstrap.Component
2478  * Bootstrap MenuItem class
2479  * @cfg {String} html the menu label
2480  * @cfg {String} href the link
2481  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2482  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2483  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2484  * @cfg {String} fa favicon to show on left of menu item.
2485  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2486  * 
2487  * 
2488  * @constructor
2489  * Create a new MenuItem
2490  * @param {Object} config The config object
2491  */
2492
2493
2494 Roo.bootstrap.MenuItem = function(config){
2495     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2496     this.addEvents({
2497         // raw events
2498         /**
2499          * @event click
2500          * The raw click event for the entire grid.
2501          * @param {Roo.bootstrap.MenuItem} this
2502          * @param {Roo.EventObject} e
2503          */
2504         "click" : true
2505     });
2506 };
2507
2508 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2509     
2510     href : false,
2511     html : false,
2512     preventDefault: false,
2513     isContainer : false,
2514     active : false,
2515     fa: false,
2516     
2517     getAutoCreate : function(){
2518         
2519         if(this.isContainer){
2520             return {
2521                 tag: 'li',
2522                 cls: 'dropdown-menu-item '
2523             };
2524         }
2525         var ctag = {
2526             tag: 'span',
2527             html: 'Link'
2528         };
2529         
2530         var anc = {
2531             tag : 'a',
2532             cls : 'dropdown-item',
2533             href : '#',
2534             cn : [  ]
2535         };
2536         
2537         if (this.fa !== false) {
2538             anc.cn.push({
2539                 tag : 'i',
2540                 cls : 'fa fa-' + this.fa
2541             });
2542         }
2543         
2544         anc.cn.push(ctag);
2545         
2546         
2547         var cfg= {
2548             tag: 'li',
2549             cls: 'dropdown-menu-item',
2550             cn: [ anc ]
2551         };
2552         if (this.parent().type == 'treeview') {
2553             cfg.cls = 'treeview-menu';
2554         }
2555         if (this.active) {
2556             cfg.cls += ' active';
2557         }
2558         
2559         
2560         
2561         anc.href = this.href || cfg.cn[0].href ;
2562         ctag.html = this.html || cfg.cn[0].html ;
2563         return cfg;
2564     },
2565     
2566     initEvents: function()
2567     {
2568         if (this.parent().type == 'treeview') {
2569             this.el.select('a').on('click', this.onClick, this);
2570         }
2571         
2572         if (this.menu) {
2573             this.menu.parentType = this.xtype;
2574             this.menu.triggerEl = this.el;
2575             this.menu = this.addxtype(Roo.apply({}, this.menu));
2576         }
2577         
2578     },
2579     onClick : function(e)
2580     {
2581         Roo.log('item on click ');
2582         
2583         if(this.preventDefault){
2584             e.preventDefault();
2585         }
2586         //this.parent().hideMenuItems();
2587         
2588         this.fireEvent('click', this, e);
2589     },
2590     getEl : function()
2591     {
2592         return this.el;
2593     } 
2594 });
2595
2596  
2597
2598  /*
2599  * - LGPL
2600  *
2601  * menu separator
2602  * 
2603  */
2604
2605
2606 /**
2607  * @class Roo.bootstrap.MenuSeparator
2608  * @extends Roo.bootstrap.Component
2609  * Bootstrap MenuSeparator class
2610  * 
2611  * @constructor
2612  * Create a new MenuItem
2613  * @param {Object} config The config object
2614  */
2615
2616
2617 Roo.bootstrap.MenuSeparator = function(config){
2618     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2619 };
2620
2621 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2622     
2623     getAutoCreate : function(){
2624         var cfg = {
2625             cls: 'divider',
2626             tag : 'li'
2627         };
2628         
2629         return cfg;
2630     }
2631    
2632 });
2633
2634  
2635
2636  
2637 /*
2638 * Licence: LGPL
2639 */
2640
2641 /**
2642  * @class Roo.bootstrap.Modal
2643  * @extends Roo.bootstrap.Component
2644  * Bootstrap Modal class
2645  * @cfg {String} title Title of dialog
2646  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2647  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2648  * @cfg {Boolean} specificTitle default false
2649  * @cfg {Array} buttons Array of buttons or standard button set..
2650  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
2651  * @cfg {Boolean} animate default true
2652  * @cfg {Boolean} allow_close default true
2653  * @cfg {Boolean} fitwindow default false
2654  * @cfg {String} size (sm|lg) default empty
2655  * @cfg {Number} max_width set the max width of modal
2656  *
2657  *
2658  * @constructor
2659  * Create a new Modal Dialog
2660  * @param {Object} config The config object
2661  */
2662
2663 Roo.bootstrap.Modal = function(config){
2664     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2665     this.addEvents({
2666         // raw events
2667         /**
2668          * @event btnclick
2669          * The raw btnclick event for the button
2670          * @param {Roo.EventObject} e
2671          */
2672         "btnclick" : true,
2673         /**
2674          * @event resize
2675          * Fire when dialog resize
2676          * @param {Roo.bootstrap.Modal} this
2677          * @param {Roo.EventObject} e
2678          */
2679         "resize" : true
2680     });
2681     this.buttons = this.buttons || [];
2682
2683     if (this.tmpl) {
2684         this.tmpl = Roo.factory(this.tmpl);
2685     }
2686
2687 };
2688
2689 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2690
2691     title : 'test dialog',
2692
2693     buttons : false,
2694
2695     // set on load...
2696
2697     html: false,
2698
2699     tmp: false,
2700
2701     specificTitle: false,
2702
2703     buttonPosition: 'right',
2704
2705     allow_close : true,
2706
2707     animate : true,
2708
2709     fitwindow: false,
2710     
2711      // private
2712     dialogEl: false,
2713     bodyEl:  false,
2714     footerEl:  false,
2715     titleEl:  false,
2716     closeEl:  false,
2717
2718     size: '',
2719     
2720     max_width: 0,
2721     
2722     max_height: 0,
2723     
2724     fit_content: false,
2725
2726     onRender : function(ct, position)
2727     {
2728         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2729
2730         if(!this.el){
2731             var cfg = Roo.apply({},  this.getAutoCreate());
2732             cfg.id = Roo.id();
2733             //if(!cfg.name){
2734             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2735             //}
2736             //if (!cfg.name.length) {
2737             //    delete cfg.name;
2738            // }
2739             if (this.cls) {
2740                 cfg.cls += ' ' + this.cls;
2741             }
2742             if (this.style) {
2743                 cfg.style = this.style;
2744             }
2745             this.el = Roo.get(document.body).createChild(cfg, position);
2746         }
2747         //var type = this.el.dom.type;
2748
2749
2750         if(this.tabIndex !== undefined){
2751             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2752         }
2753
2754         this.dialogEl = this.el.select('.modal-dialog',true).first();
2755         this.bodyEl = this.el.select('.modal-body',true).first();
2756         this.closeEl = this.el.select('.modal-header .close', true).first();
2757         this.headerEl = this.el.select('.modal-header',true).first();
2758         this.titleEl = this.el.select('.modal-title',true).first();
2759         this.footerEl = this.el.select('.modal-footer',true).first();
2760
2761         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2762         
2763         //this.el.addClass("x-dlg-modal");
2764
2765         if (this.buttons.length) {
2766             Roo.each(this.buttons, function(bb) {
2767                 var b = Roo.apply({}, bb);
2768                 b.xns = b.xns || Roo.bootstrap;
2769                 b.xtype = b.xtype || 'Button';
2770                 if (typeof(b.listeners) == 'undefined') {
2771                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2772                 }
2773
2774                 var btn = Roo.factory(b);
2775
2776                 btn.render(this.getButtonContainer());
2777
2778             },this);
2779         }
2780         // render the children.
2781         var nitems = [];
2782
2783         if(typeof(this.items) != 'undefined'){
2784             var items = this.items;
2785             delete this.items;
2786
2787             for(var i =0;i < items.length;i++) {
2788                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2789             }
2790         }
2791
2792         this.items = nitems;
2793
2794         // where are these used - they used to be body/close/footer
2795
2796
2797         this.initEvents();
2798         //this.el.addClass([this.fieldClass, this.cls]);
2799
2800     },
2801
2802     getAutoCreate : function()
2803     {
2804         var bdy = {
2805                 cls : 'modal-body',
2806                 html : this.html || ''
2807         };
2808
2809         var title = {
2810             tag: 'h4',
2811             cls : 'modal-title',
2812             html : this.title
2813         };
2814
2815         if(this.specificTitle){
2816             title = this.title;
2817
2818         }
2819
2820         var header = [];
2821         if (this.allow_close && Roo.bootstrap.version == 3) {
2822             header.push({
2823                 tag: 'button',
2824                 cls : 'close',
2825                 html : '&times'
2826             });
2827         }
2828
2829         header.push(title);
2830
2831         if (this.allow_close && Roo.bootstrap.version == 4) {
2832             header.push({
2833                 tag: 'button',
2834                 cls : 'close',
2835                 html : '&times'
2836             });
2837         }
2838         
2839         var size = '';
2840
2841         if(this.size.length){
2842             size = 'modal-' + this.size;
2843         }
2844         
2845         var footer = Roo.bootstrap.version == 3 ?
2846             {
2847                 cls : 'modal-footer',
2848                 cn : [
2849                     {
2850                         tag: 'div',
2851                         cls: 'btn-' + this.buttonPosition
2852                     }
2853                 ]
2854
2855             } :
2856             {  // BS4 uses mr-auto on left buttons....
2857                 cls : 'modal-footer'
2858             };
2859
2860             
2861
2862         
2863         
2864         var modal = {
2865             cls: "modal",
2866              cn : [
2867                 {
2868                     cls: "modal-dialog " + size,
2869                     cn : [
2870                         {
2871                             cls : "modal-content",
2872                             cn : [
2873                                 {
2874                                     cls : 'modal-header',
2875                                     cn : header
2876                                 },
2877                                 bdy,
2878                                 footer
2879                             ]
2880
2881                         }
2882                     ]
2883
2884                 }
2885             ]
2886         };
2887
2888         if(this.animate){
2889             modal.cls += ' fade';
2890         }
2891
2892         return modal;
2893
2894     },
2895     getChildContainer : function() {
2896
2897          return this.bodyEl;
2898
2899     },
2900     getButtonContainer : function() {
2901         
2902          return Roo.bootstrap.version == 4 ?
2903             this.el.select('.modal-footer',true).first()
2904             : this.el.select('.modal-footer div',true).first();
2905
2906     },
2907     initEvents : function()
2908     {
2909         if (this.allow_close) {
2910             this.closeEl.on('click', this.hide, this);
2911         }
2912         Roo.EventManager.onWindowResize(this.resize, this, true);
2913
2914
2915     },
2916   
2917
2918     resize : function()
2919     {
2920         this.maskEl.setSize(
2921             Roo.lib.Dom.getViewWidth(true),
2922             Roo.lib.Dom.getViewHeight(true)
2923         );
2924         
2925         if (this.fitwindow) {
2926             
2927            
2928             this.setSize(
2929                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2930                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
2931             );
2932             return;
2933         }
2934         
2935         if(this.max_width !== 0) {
2936             
2937             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2938             
2939             if(this.height) {
2940                 this.setSize(w, this.height);
2941                 return;
2942             }
2943             
2944             if(this.max_height) {
2945                 this.setSize(w,Math.min(
2946                     this.max_height,
2947                     Roo.lib.Dom.getViewportHeight(true) - 60
2948                 ));
2949                 
2950                 return;
2951             }
2952             
2953             if(!this.fit_content) {
2954                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2955                 return;
2956             }
2957             
2958             this.setSize(w, Math.min(
2959                 60 +
2960                 this.headerEl.getHeight() + 
2961                 this.footerEl.getHeight() + 
2962                 this.getChildHeight(this.bodyEl.dom.childNodes),
2963                 Roo.lib.Dom.getViewportHeight(true) - 60)
2964             );
2965         }
2966         
2967     },
2968
2969     setSize : function(w,h)
2970     {
2971         if (!w && !h) {
2972             return;
2973         }
2974         
2975         this.resizeTo(w,h);
2976     },
2977
2978     show : function() {
2979
2980         if (!this.rendered) {
2981             this.render();
2982         }
2983
2984         //this.el.setStyle('display', 'block');
2985         this.el.removeClass('hideing');
2986         this.el.dom.style.display='block';
2987         
2988         Roo.get(document.body).addClass('modal-open');
2989  
2990         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2991             
2992             (function(){
2993                 this.el.addClass('show');
2994                 this.el.addClass('in');
2995             }).defer(50, this);
2996         }else{
2997             this.el.addClass('show');
2998             this.el.addClass('in');
2999         }
3000
3001         // not sure how we can show data in here..
3002         //if (this.tmpl) {
3003         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
3004         //}
3005
3006         Roo.get(document.body).addClass("x-body-masked");
3007         
3008         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
3009         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3010         this.maskEl.dom.style.display = 'block';
3011         this.maskEl.addClass('show');
3012         
3013         
3014         this.resize();
3015         
3016         this.fireEvent('show', this);
3017
3018         // set zindex here - otherwise it appears to be ignored...
3019         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3020
3021         (function () {
3022             this.items.forEach( function(e) {
3023                 e.layout ? e.layout() : false;
3024
3025             });
3026         }).defer(100,this);
3027
3028     },
3029     hide : function()
3030     {
3031         if(this.fireEvent("beforehide", this) !== false){
3032             
3033             this.maskEl.removeClass('show');
3034             
3035             this.maskEl.dom.style.display = '';
3036             Roo.get(document.body).removeClass("x-body-masked");
3037             this.el.removeClass('in');
3038             this.el.select('.modal-dialog', true).first().setStyle('transform','');
3039
3040             if(this.animate){ // why
3041                 this.el.addClass('hideing');
3042                 this.el.removeClass('show');
3043                 (function(){
3044                     if (!this.el.hasClass('hideing')) {
3045                         return; // it's been shown again...
3046                     }
3047                     
3048                     this.el.dom.style.display='';
3049
3050                     Roo.get(document.body).removeClass('modal-open');
3051                     this.el.removeClass('hideing');
3052                 }).defer(150,this);
3053                 
3054             }else{
3055                 this.el.removeClass('show');
3056                 this.el.dom.style.display='';
3057                 Roo.get(document.body).removeClass('modal-open');
3058
3059             }
3060             this.fireEvent('hide', this);
3061         }
3062     },
3063     isVisible : function()
3064     {
3065         
3066         return this.el.hasClass('show') && !this.el.hasClass('hideing');
3067         
3068     },
3069
3070     addButton : function(str, cb)
3071     {
3072
3073
3074         var b = Roo.apply({}, { html : str } );
3075         b.xns = b.xns || Roo.bootstrap;
3076         b.xtype = b.xtype || 'Button';
3077         if (typeof(b.listeners) == 'undefined') {
3078             b.listeners = { click : cb.createDelegate(this)  };
3079         }
3080
3081         var btn = Roo.factory(b);
3082
3083         btn.render(this.getButtonContainer());
3084
3085         return btn;
3086
3087     },
3088
3089     setDefaultButton : function(btn)
3090     {
3091         //this.el.select('.modal-footer').()
3092     },
3093
3094     resizeTo: function(w,h)
3095     {
3096         this.dialogEl.setWidth(w);
3097         
3098         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
3099
3100         this.bodyEl.setHeight(h - diff);
3101         
3102         this.fireEvent('resize', this);
3103     },
3104     
3105     setContentSize  : function(w, h)
3106     {
3107
3108     },
3109     onButtonClick: function(btn,e)
3110     {
3111         //Roo.log([a,b,c]);
3112         this.fireEvent('btnclick', btn.name, e);
3113     },
3114      /**
3115      * Set the title of the Dialog
3116      * @param {String} str new Title
3117      */
3118     setTitle: function(str) {
3119         this.titleEl.dom.innerHTML = str;
3120     },
3121     /**
3122      * Set the body of the Dialog
3123      * @param {String} str new Title
3124      */
3125     setBody: function(str) {
3126         this.bodyEl.dom.innerHTML = str;
3127     },
3128     /**
3129      * Set the body of the Dialog using the template
3130      * @param {Obj} data - apply this data to the template and replace the body contents.
3131      */
3132     applyBody: function(obj)
3133     {
3134         if (!this.tmpl) {
3135             Roo.log("Error - using apply Body without a template");
3136             //code
3137         }
3138         this.tmpl.overwrite(this.bodyEl, obj);
3139     },
3140     
3141     getChildHeight : function(child_nodes)
3142     {
3143         if(
3144             !child_nodes ||
3145             child_nodes.length == 0
3146         ) {
3147             return;
3148         }
3149         
3150         var child_height = 0;
3151         
3152         for(var i = 0; i < child_nodes.length; i++) {
3153             
3154             /*
3155             * for modal with tabs...
3156             if(child_nodes[i].classList.contains('roo-layout-panel')) {
3157                 
3158                 var layout_childs = child_nodes[i].childNodes;
3159                 
3160                 for(var j = 0; j < layout_childs.length; j++) {
3161                     
3162                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3163                         
3164                         var layout_body_childs = layout_childs[j].childNodes;
3165                         
3166                         for(var k = 0; k < layout_body_childs.length; k++) {
3167                             
3168                             if(layout_body_childs[k].classList.contains('navbar')) {
3169                                 child_height += layout_body_childs[k].offsetHeight;
3170                                 continue;
3171                             }
3172                             
3173                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3174                                 
3175                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3176                                 
3177                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3178                                     
3179                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3180                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3181                                         continue;
3182                                     }
3183                                     
3184                                 }
3185                                 
3186                             }
3187                             
3188                         }
3189                     }
3190                 }
3191                 continue;
3192             }
3193             */
3194             
3195             child_height += child_nodes[i].offsetHeight;
3196             // Roo.log(child_nodes[i].offsetHeight);
3197         }
3198         
3199         return child_height;
3200     }
3201
3202 });
3203
3204
3205 Roo.apply(Roo.bootstrap.Modal,  {
3206     /**
3207          * Button config that displays a single OK button
3208          * @type Object
3209          */
3210         OK :  [{
3211             name : 'ok',
3212             weight : 'primary',
3213             html : 'OK'
3214         }],
3215         /**
3216          * Button config that displays Yes and No buttons
3217          * @type Object
3218          */
3219         YESNO : [
3220             {
3221                 name  : 'no',
3222                 html : 'No'
3223             },
3224             {
3225                 name  :'yes',
3226                 weight : 'primary',
3227                 html : 'Yes'
3228             }
3229         ],
3230
3231         /**
3232          * Button config that displays OK and Cancel buttons
3233          * @type Object
3234          */
3235         OKCANCEL : [
3236             {
3237                name : 'cancel',
3238                 html : 'Cancel'
3239             },
3240             {
3241                 name : 'ok',
3242                 weight : 'primary',
3243                 html : 'OK'
3244             }
3245         ],
3246         /**
3247          * Button config that displays Yes, No and Cancel buttons
3248          * @type Object
3249          */
3250         YESNOCANCEL : [
3251             {
3252                 name : 'yes',
3253                 weight : 'primary',
3254                 html : 'Yes'
3255             },
3256             {
3257                 name : 'no',
3258                 html : 'No'
3259             },
3260             {
3261                 name : 'cancel',
3262                 html : 'Cancel'
3263             }
3264         ],
3265         
3266         zIndex : 10001
3267 });
3268 /*
3269  * - LGPL
3270  *
3271  * messagebox - can be used as a replace
3272  * 
3273  */
3274 /**
3275  * @class Roo.MessageBox
3276  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3277  * Example usage:
3278  *<pre><code>
3279 // Basic alert:
3280 Roo.Msg.alert('Status', 'Changes saved successfully.');
3281
3282 // Prompt for user data:
3283 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3284     if (btn == 'ok'){
3285         // process text value...
3286     }
3287 });
3288
3289 // Show a dialog using config options:
3290 Roo.Msg.show({
3291    title:'Save Changes?',
3292    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3293    buttons: Roo.Msg.YESNOCANCEL,
3294    fn: processResult,
3295    animEl: 'elId'
3296 });
3297 </code></pre>
3298  * @singleton
3299  */
3300 Roo.bootstrap.MessageBox = function(){
3301     var dlg, opt, mask, waitTimer;
3302     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3303     var buttons, activeTextEl, bwidth;
3304
3305     
3306     // private
3307     var handleButton = function(button){
3308         dlg.hide();
3309         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3310     };
3311
3312     // private
3313     var handleHide = function(){
3314         if(opt && opt.cls){
3315             dlg.el.removeClass(opt.cls);
3316         }
3317         //if(waitTimer){
3318         //    Roo.TaskMgr.stop(waitTimer);
3319         //    waitTimer = null;
3320         //}
3321     };
3322
3323     // private
3324     var updateButtons = function(b){
3325         var width = 0;
3326         if(!b){
3327             buttons["ok"].hide();
3328             buttons["cancel"].hide();
3329             buttons["yes"].hide();
3330             buttons["no"].hide();
3331             dlg.footerEl.hide();
3332             
3333             return width;
3334         }
3335         dlg.footerEl.show();
3336         for(var k in buttons){
3337             if(typeof buttons[k] != "function"){
3338                 if(b[k]){
3339                     buttons[k].show();
3340                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3341                     width += buttons[k].el.getWidth()+15;
3342                 }else{
3343                     buttons[k].hide();
3344                 }
3345             }
3346         }
3347         return width;
3348     };
3349
3350     // private
3351     var handleEsc = function(d, k, e){
3352         if(opt && opt.closable !== false){
3353             dlg.hide();
3354         }
3355         if(e){
3356             e.stopEvent();
3357         }
3358     };
3359
3360     return {
3361         /**
3362          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3363          * @return {Roo.BasicDialog} The BasicDialog element
3364          */
3365         getDialog : function(){
3366            if(!dlg){
3367                 dlg = new Roo.bootstrap.Modal( {
3368                     //draggable: true,
3369                     //resizable:false,
3370                     //constraintoviewport:false,
3371                     //fixedcenter:true,
3372                     //collapsible : false,
3373                     //shim:true,
3374                     //modal: true,
3375                 //    width: 'auto',
3376                   //  height:100,
3377                     //buttonAlign:"center",
3378                     closeClick : function(){
3379                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3380                             handleButton("no");
3381                         }else{
3382                             handleButton("cancel");
3383                         }
3384                     }
3385                 });
3386                 dlg.render();
3387                 dlg.on("hide", handleHide);
3388                 mask = dlg.mask;
3389                 //dlg.addKeyListener(27, handleEsc);
3390                 buttons = {};
3391                 this.buttons = buttons;
3392                 var bt = this.buttonText;
3393                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3394                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3395                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3396                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3397                 //Roo.log(buttons);
3398                 bodyEl = dlg.bodyEl.createChild({
3399
3400                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3401                         '<textarea class="roo-mb-textarea"></textarea>' +
3402                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3403                 });
3404                 msgEl = bodyEl.dom.firstChild;
3405                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3406                 textboxEl.enableDisplayMode();
3407                 textboxEl.addKeyListener([10,13], function(){
3408                     if(dlg.isVisible() && opt && opt.buttons){
3409                         if(opt.buttons.ok){
3410                             handleButton("ok");
3411                         }else if(opt.buttons.yes){
3412                             handleButton("yes");
3413                         }
3414                     }
3415                 });
3416                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3417                 textareaEl.enableDisplayMode();
3418                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3419                 progressEl.enableDisplayMode();
3420                 
3421                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3422                 var pf = progressEl.dom.firstChild;
3423                 if (pf) {
3424                     pp = Roo.get(pf.firstChild);
3425                     pp.setHeight(pf.offsetHeight);
3426                 }
3427                 
3428             }
3429             return dlg;
3430         },
3431
3432         /**
3433          * Updates the message box body text
3434          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3435          * the XHTML-compliant non-breaking space character '&amp;#160;')
3436          * @return {Roo.MessageBox} This message box
3437          */
3438         updateText : function(text)
3439         {
3440             if(!dlg.isVisible() && !opt.width){
3441                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3442                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3443             }
3444             msgEl.innerHTML = text || '&#160;';
3445       
3446             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3447             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3448             var w = Math.max(
3449                     Math.min(opt.width || cw , this.maxWidth), 
3450                     Math.max(opt.minWidth || this.minWidth, bwidth)
3451             );
3452             if(opt.prompt){
3453                 activeTextEl.setWidth(w);
3454             }
3455             if(dlg.isVisible()){
3456                 dlg.fixedcenter = false;
3457             }
3458             // to big, make it scroll. = But as usual stupid IE does not support
3459             // !important..
3460             
3461             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3462                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3463                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3464             } else {
3465                 bodyEl.dom.style.height = '';
3466                 bodyEl.dom.style.overflowY = '';
3467             }
3468             if (cw > w) {
3469                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3470             } else {
3471                 bodyEl.dom.style.overflowX = '';
3472             }
3473             
3474             dlg.setContentSize(w, bodyEl.getHeight());
3475             if(dlg.isVisible()){
3476                 dlg.fixedcenter = true;
3477             }
3478             return this;
3479         },
3480
3481         /**
3482          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3483          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3484          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3485          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3486          * @return {Roo.MessageBox} This message box
3487          */
3488         updateProgress : function(value, text){
3489             if(text){
3490                 this.updateText(text);
3491             }
3492             
3493             if (pp) { // weird bug on my firefox - for some reason this is not defined
3494                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3495                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3496             }
3497             return this;
3498         },        
3499
3500         /**
3501          * Returns true if the message box is currently displayed
3502          * @return {Boolean} True if the message box is visible, else false
3503          */
3504         isVisible : function(){
3505             return dlg && dlg.isVisible();  
3506         },
3507
3508         /**
3509          * Hides the message box if it is displayed
3510          */
3511         hide : function(){
3512             if(this.isVisible()){
3513                 dlg.hide();
3514             }  
3515         },
3516
3517         /**
3518          * Displays a new message box, or reinitializes an existing message box, based on the config options
3519          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3520          * The following config object properties are supported:
3521          * <pre>
3522 Property    Type             Description
3523 ----------  ---------------  ------------------------------------------------------------------------------------
3524 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3525                                    closes (defaults to undefined)
3526 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3527                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3528 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3529                                    progress and wait dialogs will ignore this property and always hide the
3530                                    close button as they can only be closed programmatically.
3531 cls               String           A custom CSS class to apply to the message box element
3532 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3533                                    displayed (defaults to 75)
3534 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3535                                    function will be btn (the name of the button that was clicked, if applicable,
3536                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3537                                    Progress and wait dialogs will ignore this option since they do not respond to
3538                                    user actions and can only be closed programmatically, so any required function
3539                                    should be called by the same code after it closes the dialog.
3540 icon              String           A CSS class that provides a background image to be used as an icon for
3541                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3542 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3543 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3544 modal             Boolean          False to allow user interaction with the page while the message box is
3545                                    displayed (defaults to true)
3546 msg               String           A string that will replace the existing message box body text (defaults
3547                                    to the XHTML-compliant non-breaking space character '&#160;')
3548 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3549 progress          Boolean          True to display a progress bar (defaults to false)
3550 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3551 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3552 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3553 title             String           The title text
3554 value             String           The string value to set into the active textbox element if displayed
3555 wait              Boolean          True to display a progress bar (defaults to false)
3556 width             Number           The width of the dialog in pixels
3557 </pre>
3558          *
3559          * Example usage:
3560          * <pre><code>
3561 Roo.Msg.show({
3562    title: 'Address',
3563    msg: 'Please enter your address:',
3564    width: 300,
3565    buttons: Roo.MessageBox.OKCANCEL,
3566    multiline: true,
3567    fn: saveAddress,
3568    animEl: 'addAddressBtn'
3569 });
3570 </code></pre>
3571          * @param {Object} config Configuration options
3572          * @return {Roo.MessageBox} This message box
3573          */
3574         show : function(options)
3575         {
3576             
3577             // this causes nightmares if you show one dialog after another
3578             // especially on callbacks..
3579              
3580             if(this.isVisible()){
3581                 
3582                 this.hide();
3583                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3584                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3585                 Roo.log("New Dialog Message:" +  options.msg )
3586                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3587                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3588                 
3589             }
3590             var d = this.getDialog();
3591             opt = options;
3592             d.setTitle(opt.title || "&#160;");
3593             d.closeEl.setDisplayed(opt.closable !== false);
3594             activeTextEl = textboxEl;
3595             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3596             if(opt.prompt){
3597                 if(opt.multiline){
3598                     textboxEl.hide();
3599                     textareaEl.show();
3600                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3601                         opt.multiline : this.defaultTextHeight);
3602                     activeTextEl = textareaEl;
3603                 }else{
3604                     textboxEl.show();
3605                     textareaEl.hide();
3606                 }
3607             }else{
3608                 textboxEl.hide();
3609                 textareaEl.hide();
3610             }
3611             progressEl.setDisplayed(opt.progress === true);
3612             if (opt.progress) {
3613                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
3614             }
3615             this.updateProgress(0);
3616             activeTextEl.dom.value = opt.value || "";
3617             if(opt.prompt){
3618                 dlg.setDefaultButton(activeTextEl);
3619             }else{
3620                 var bs = opt.buttons;
3621                 var db = null;
3622                 if(bs && bs.ok){
3623                     db = buttons["ok"];
3624                 }else if(bs && bs.yes){
3625                     db = buttons["yes"];
3626                 }
3627                 dlg.setDefaultButton(db);
3628             }
3629             bwidth = updateButtons(opt.buttons);
3630             this.updateText(opt.msg);
3631             if(opt.cls){
3632                 d.el.addClass(opt.cls);
3633             }
3634             d.proxyDrag = opt.proxyDrag === true;
3635             d.modal = opt.modal !== false;
3636             d.mask = opt.modal !== false ? mask : false;
3637             if(!d.isVisible()){
3638                 // force it to the end of the z-index stack so it gets a cursor in FF
3639                 document.body.appendChild(dlg.el.dom);
3640                 d.animateTarget = null;
3641                 d.show(options.animEl);
3642             }
3643             return this;
3644         },
3645
3646         /**
3647          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3648          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3649          * and closing the message box when the process is complete.
3650          * @param {String} title The title bar text
3651          * @param {String} msg The message box body text
3652          * @return {Roo.MessageBox} This message box
3653          */
3654         progress : function(title, msg){
3655             this.show({
3656                 title : title,
3657                 msg : msg,
3658                 buttons: false,
3659                 progress:true,
3660                 closable:false,
3661                 minWidth: this.minProgressWidth,
3662                 modal : true
3663             });
3664             return this;
3665         },
3666
3667         /**
3668          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3669          * If a callback function is passed it will be called after the user clicks the button, and the
3670          * id of the button that was clicked will be passed as the only parameter to the callback
3671          * (could also be the top-right close button).
3672          * @param {String} title The title bar text
3673          * @param {String} msg The message box body text
3674          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3675          * @param {Object} scope (optional) The scope of the callback function
3676          * @return {Roo.MessageBox} This message box
3677          */
3678         alert : function(title, msg, fn, scope)
3679         {
3680             this.show({
3681                 title : title,
3682                 msg : msg,
3683                 buttons: this.OK,
3684                 fn: fn,
3685                 closable : false,
3686                 scope : scope,
3687                 modal : true
3688             });
3689             return this;
3690         },
3691
3692         /**
3693          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3694          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3695          * You are responsible for closing the message box when the process is complete.
3696          * @param {String} msg The message box body text
3697          * @param {String} title (optional) The title bar text
3698          * @return {Roo.MessageBox} This message box
3699          */
3700         wait : function(msg, title){
3701             this.show({
3702                 title : title,
3703                 msg : msg,
3704                 buttons: false,
3705                 closable:false,
3706                 progress:true,
3707                 modal:true,
3708                 width:300,
3709                 wait:true
3710             });
3711             waitTimer = Roo.TaskMgr.start({
3712                 run: function(i){
3713                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3714                 },
3715                 interval: 1000
3716             });
3717             return this;
3718         },
3719
3720         /**
3721          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3722          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3723          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3724          * @param {String} title The title bar text
3725          * @param {String} msg The message box body text
3726          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3727          * @param {Object} scope (optional) The scope of the callback function
3728          * @return {Roo.MessageBox} This message box
3729          */
3730         confirm : function(title, msg, fn, scope){
3731             this.show({
3732                 title : title,
3733                 msg : msg,
3734                 buttons: this.YESNO,
3735                 fn: fn,
3736                 scope : scope,
3737                 modal : true
3738             });
3739             return this;
3740         },
3741
3742         /**
3743          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3744          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3745          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3746          * (could also be the top-right close button) and the text that was entered will be passed as the two
3747          * parameters to the callback.
3748          * @param {String} title The title bar text
3749          * @param {String} msg The message box body text
3750          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3751          * @param {Object} scope (optional) The scope of the callback function
3752          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3753          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3754          * @return {Roo.MessageBox} This message box
3755          */
3756         prompt : function(title, msg, fn, scope, multiline){
3757             this.show({
3758                 title : title,
3759                 msg : msg,
3760                 buttons: this.OKCANCEL,
3761                 fn: fn,
3762                 minWidth:250,
3763                 scope : scope,
3764                 prompt:true,
3765                 multiline: multiline,
3766                 modal : true
3767             });
3768             return this;
3769         },
3770
3771         /**
3772          * Button config that displays a single OK button
3773          * @type Object
3774          */
3775         OK : {ok:true},
3776         /**
3777          * Button config that displays Yes and No buttons
3778          * @type Object
3779          */
3780         YESNO : {yes:true, no:true},
3781         /**
3782          * Button config that displays OK and Cancel buttons
3783          * @type Object
3784          */
3785         OKCANCEL : {ok:true, cancel:true},
3786         /**
3787          * Button config that displays Yes, No and Cancel buttons
3788          * @type Object
3789          */
3790         YESNOCANCEL : {yes:true, no:true, cancel:true},
3791
3792         /**
3793          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3794          * @type Number
3795          */
3796         defaultTextHeight : 75,
3797         /**
3798          * The maximum width in pixels of the message box (defaults to 600)
3799          * @type Number
3800          */
3801         maxWidth : 600,
3802         /**
3803          * The minimum width in pixels of the message box (defaults to 100)
3804          * @type Number
3805          */
3806         minWidth : 100,
3807         /**
3808          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3809          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3810          * @type Number
3811          */
3812         minProgressWidth : 250,
3813         /**
3814          * An object containing the default button text strings that can be overriden for localized language support.
3815          * Supported properties are: ok, cancel, yes and no.
3816          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3817          * @type Object
3818          */
3819         buttonText : {
3820             ok : "OK",
3821             cancel : "Cancel",
3822             yes : "Yes",
3823             no : "No"
3824         }
3825     };
3826 }();
3827
3828 /**
3829  * Shorthand for {@link Roo.MessageBox}
3830  */
3831 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3832 Roo.Msg = Roo.Msg || Roo.MessageBox;
3833 /*
3834  * - LGPL
3835  *
3836  * navbar
3837  * 
3838  */
3839
3840 /**
3841  * @class Roo.bootstrap.Navbar
3842  * @extends Roo.bootstrap.Component
3843  * Bootstrap Navbar class
3844
3845  * @constructor
3846  * Create a new Navbar
3847  * @param {Object} config The config object
3848  */
3849
3850
3851 Roo.bootstrap.Navbar = function(config){
3852     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3853     this.addEvents({
3854         // raw events
3855         /**
3856          * @event beforetoggle
3857          * Fire before toggle the menu
3858          * @param {Roo.EventObject} e
3859          */
3860         "beforetoggle" : true
3861     });
3862 };
3863
3864 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3865     
3866     
3867    
3868     // private
3869     navItems : false,
3870     loadMask : false,
3871     
3872     
3873     getAutoCreate : function(){
3874         
3875         
3876         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3877         
3878     },
3879     
3880     initEvents :function ()
3881     {
3882         //Roo.log(this.el.select('.navbar-toggle',true));
3883         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
3884         
3885         var mark = {
3886             tag: "div",
3887             cls:"x-dlg-mask"
3888         };
3889         
3890         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3891         
3892         var size = this.el.getSize();
3893         this.maskEl.setSize(size.width, size.height);
3894         this.maskEl.enableDisplayMode("block");
3895         this.maskEl.hide();
3896         
3897         if(this.loadMask){
3898             this.maskEl.show();
3899         }
3900     },
3901     
3902     
3903     getChildContainer : function()
3904     {
3905         if (this.el && this.el.select('.collapse').getCount()) {
3906             return this.el.select('.collapse',true).first();
3907         }
3908         
3909         return this.el;
3910     },
3911     
3912     mask : function()
3913     {
3914         this.maskEl.show();
3915     },
3916     
3917     unmask : function()
3918     {
3919         this.maskEl.hide();
3920     },
3921     onToggle : function()
3922     {
3923         
3924         if(this.fireEvent('beforetoggle', this) === false){
3925             return;
3926         }
3927         var ce = this.el.select('.navbar-collapse',true).first();
3928       
3929         if (!ce.hasClass('show')) {
3930            this.expand();
3931         } else {
3932             this.collapse();
3933         }
3934         
3935         
3936     
3937     },
3938     /**
3939      * Expand the navbar pulldown 
3940      */
3941     expand : function ()
3942     {
3943        
3944         var ce = this.el.select('.navbar-collapse',true).first();
3945         if (ce.hasClass('collapsing')) {
3946             return;
3947         }
3948         ce.dom.style.height = '';
3949                // show it...
3950         ce.addClass('in'); // old...
3951         ce.removeClass('collapse');
3952         ce.addClass('show');
3953         var h = ce.getHeight();
3954         Roo.log(h);
3955         ce.removeClass('show');
3956         // at this point we should be able to see it..
3957         ce.addClass('collapsing');
3958         
3959         ce.setHeight(0); // resize it ...
3960         ce.on('transitionend', function() {
3961             //Roo.log('done transition');
3962             ce.removeClass('collapsing');
3963             ce.addClass('show');
3964             ce.removeClass('collapse');
3965
3966             ce.dom.style.height = '';
3967         }, this, { single: true} );
3968         ce.setHeight(h);
3969         ce.dom.scrollTop = 0;
3970     },
3971     /**
3972      * Collapse the navbar pulldown 
3973      */
3974     collapse : function()
3975     {
3976          var ce = this.el.select('.navbar-collapse',true).first();
3977        
3978         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
3979             // it's collapsed or collapsing..
3980             return;
3981         }
3982         ce.removeClass('in'); // old...
3983         ce.setHeight(ce.getHeight());
3984         ce.removeClass('show');
3985         ce.addClass('collapsing');
3986         
3987         ce.on('transitionend', function() {
3988             ce.dom.style.height = '';
3989             ce.removeClass('collapsing');
3990             ce.addClass('collapse');
3991         }, this, { single: true} );
3992         ce.setHeight(0);
3993     }
3994     
3995     
3996     
3997 });
3998
3999
4000
4001  
4002
4003  /*
4004  * - LGPL
4005  *
4006  * navbar
4007  * 
4008  */
4009
4010 /**
4011  * @class Roo.bootstrap.NavSimplebar
4012  * @extends Roo.bootstrap.Navbar
4013  * Bootstrap Sidebar class
4014  *
4015  * @cfg {Boolean} inverse is inverted color
4016  * 
4017  * @cfg {String} type (nav | pills | tabs)
4018  * @cfg {Boolean} arrangement stacked | justified
4019  * @cfg {String} align (left | right) alignment
4020  * 
4021  * @cfg {Boolean} main (true|false) main nav bar? default false
4022  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
4023  * 
4024  * @cfg {String} tag (header|footer|nav|div) default is nav 
4025
4026  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
4027  * 
4028  * 
4029  * @constructor
4030  * Create a new Sidebar
4031  * @param {Object} config The config object
4032  */
4033
4034
4035 Roo.bootstrap.NavSimplebar = function(config){
4036     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
4037 };
4038
4039 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
4040     
4041     inverse: false,
4042     
4043     type: false,
4044     arrangement: '',
4045     align : false,
4046     
4047     weight : 'light',
4048     
4049     main : false,
4050     
4051     
4052     tag : false,
4053     
4054     
4055     getAutoCreate : function(){
4056         
4057         
4058         var cfg = {
4059             tag : this.tag || 'div',
4060             cls : 'navbar navbar-expand-lg roo-navbar-simple'
4061         };
4062         if (['light','white'].indexOf(this.weight) > -1) {
4063             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4064         }
4065         cfg.cls += ' bg-' + this.weight;
4066         
4067         if (this.inverse) {
4068             cfg.cls += ' navbar-inverse';
4069             
4070         }
4071         
4072         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
4073         
4074         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
4075             return cfg;
4076         }
4077         
4078         
4079     
4080         
4081         cfg.cn = [
4082             {
4083                 cls: 'nav nav-' + this.xtype,
4084                 tag : 'ul'
4085             }
4086         ];
4087         
4088          
4089         this.type = this.type || 'nav';
4090         if (['tabs','pills'].indexOf(this.type) != -1) {
4091             cfg.cn[0].cls += ' nav-' + this.type
4092         
4093         
4094         } else {
4095             if (this.type!=='nav') {
4096                 Roo.log('nav type must be nav/tabs/pills')
4097             }
4098             cfg.cn[0].cls += ' navbar-nav'
4099         }
4100         
4101         
4102         
4103         
4104         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
4105             cfg.cn[0].cls += ' nav-' + this.arrangement;
4106         }
4107         
4108         
4109         if (this.align === 'right') {
4110             cfg.cn[0].cls += ' navbar-right';
4111         }
4112         
4113         
4114         
4115         
4116         return cfg;
4117     
4118         
4119     }
4120     
4121     
4122     
4123 });
4124
4125
4126
4127  
4128
4129  
4130        /*
4131  * - LGPL
4132  *
4133  * navbar
4134  * navbar-fixed-top
4135  * navbar-expand-md  fixed-top 
4136  */
4137
4138 /**
4139  * @class Roo.bootstrap.NavHeaderbar
4140  * @extends Roo.bootstrap.NavSimplebar
4141  * Bootstrap Sidebar class
4142  *
4143  * @cfg {String} brand what is brand
4144  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4145  * @cfg {String} brand_href href of the brand
4146  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
4147  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4148  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4149  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4150  * 
4151  * @constructor
4152  * Create a new Sidebar
4153  * @param {Object} config The config object
4154  */
4155
4156
4157 Roo.bootstrap.NavHeaderbar = function(config){
4158     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4159       
4160 };
4161
4162 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
4163     
4164     position: '',
4165     brand: '',
4166     brand_href: false,
4167     srButton : true,
4168     autohide : false,
4169     desktopCenter : false,
4170    
4171     
4172     getAutoCreate : function(){
4173         
4174         var   cfg = {
4175             tag: this.nav || 'nav',
4176             cls: 'navbar navbar-expand-md',
4177             role: 'navigation',
4178             cn: []
4179         };
4180         
4181         var cn = cfg.cn;
4182         if (this.desktopCenter) {
4183             cn.push({cls : 'container', cn : []});
4184             cn = cn[0].cn;
4185         }
4186         
4187         if(this.srButton){
4188             var btn = {
4189                 tag: 'button',
4190                 type: 'button',
4191                 cls: 'navbar-toggle navbar-toggler',
4192                 'data-toggle': 'collapse',
4193                 cn: [
4194                     {
4195                         tag: 'span',
4196                         cls: 'sr-only',
4197                         html: 'Toggle navigation'
4198                     },
4199                     {
4200                         tag: 'span',
4201                         cls: 'icon-bar navbar-toggler-icon'
4202                     },
4203                     {
4204                         tag: 'span',
4205                         cls: 'icon-bar'
4206                     },
4207                     {
4208                         tag: 'span',
4209                         cls: 'icon-bar'
4210                     }
4211                 ]
4212             };
4213             
4214             cn.push( Roo.bootstrap.version == 4 ? btn : {
4215                 tag: 'div',
4216                 cls: 'navbar-header',
4217                 cn: [
4218                     btn
4219                 ]
4220             });
4221         }
4222         
4223         cn.push({
4224             tag: 'div',
4225             cls: 'collapse navbar-collapse',
4226             cn : []
4227         });
4228         
4229         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4230         
4231         if (['light','white'].indexOf(this.weight) > -1) {
4232             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4233         }
4234         cfg.cls += ' bg-' + this.weight;
4235         
4236         
4237         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4238             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4239             
4240             // tag can override this..
4241             
4242             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4243         }
4244         
4245         if (this.brand !== '') {
4246             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4247             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4248                 tag: 'a',
4249                 href: this.brand_href ? this.brand_href : '#',
4250                 cls: 'navbar-brand',
4251                 cn: [
4252                 this.brand
4253                 ]
4254             });
4255         }
4256         
4257         if(this.main){
4258             cfg.cls += ' main-nav';
4259         }
4260         
4261         
4262         return cfg;
4263
4264         
4265     },
4266     getHeaderChildContainer : function()
4267     {
4268         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4269             return this.el.select('.navbar-header',true).first();
4270         }
4271         
4272         return this.getChildContainer();
4273     },
4274     
4275     
4276     initEvents : function()
4277     {
4278         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4279         
4280         if (this.autohide) {
4281             
4282             var prevScroll = 0;
4283             var ft = this.el;
4284             
4285             Roo.get(document).on('scroll',function(e) {
4286                 var ns = Roo.get(document).getScroll().top;
4287                 var os = prevScroll;
4288                 prevScroll = ns;
4289                 
4290                 if(ns > os){
4291                     ft.removeClass('slideDown');
4292                     ft.addClass('slideUp');
4293                     return;
4294                 }
4295                 ft.removeClass('slideUp');
4296                 ft.addClass('slideDown');
4297                  
4298               
4299           },this);
4300         }
4301     }    
4302     
4303 });
4304
4305
4306
4307  
4308
4309  /*
4310  * - LGPL
4311  *
4312  * navbar
4313  * 
4314  */
4315
4316 /**
4317  * @class Roo.bootstrap.NavSidebar
4318  * @extends Roo.bootstrap.Navbar
4319  * Bootstrap Sidebar class
4320  * 
4321  * @constructor
4322  * Create a new Sidebar
4323  * @param {Object} config The config object
4324  */
4325
4326
4327 Roo.bootstrap.NavSidebar = function(config){
4328     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4329 };
4330
4331 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4332     
4333     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4334     
4335     getAutoCreate : function(){
4336         
4337         
4338         return  {
4339             tag: 'div',
4340             cls: 'sidebar sidebar-nav'
4341         };
4342     
4343         
4344     }
4345     
4346     
4347     
4348 });
4349
4350
4351
4352  
4353
4354  /*
4355  * - LGPL
4356  *
4357  * nav group
4358  * 
4359  */
4360
4361 /**
4362  * @class Roo.bootstrap.NavGroup
4363  * @extends Roo.bootstrap.Component
4364  * Bootstrap NavGroup class
4365  * @cfg {String} align (left|right)
4366  * @cfg {Boolean} inverse
4367  * @cfg {String} type (nav|pills|tab) default nav
4368  * @cfg {String} navId - reference Id for navbar.
4369
4370  * 
4371  * @constructor
4372  * Create a new nav group
4373  * @param {Object} config The config object
4374  */
4375
4376 Roo.bootstrap.NavGroup = function(config){
4377     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4378     this.navItems = [];
4379    
4380     Roo.bootstrap.NavGroup.register(this);
4381      this.addEvents({
4382         /**
4383              * @event changed
4384              * Fires when the active item changes
4385              * @param {Roo.bootstrap.NavGroup} this
4386              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4387              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4388          */
4389         'changed': true
4390      });
4391     
4392 };
4393
4394 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4395     
4396     align: '',
4397     inverse: false,
4398     form: false,
4399     type: 'nav',
4400     navId : '',
4401     // private
4402     
4403     navItems : false, 
4404     
4405     getAutoCreate : function()
4406     {
4407         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4408         
4409         cfg = {
4410             tag : 'ul',
4411             cls: 'nav' 
4412         };
4413         if (Roo.bootstrap.version == 4) {
4414             if (['tabs','pills'].indexOf(this.type) != -1) {
4415                 cfg.cls += ' nav-' + this.type; 
4416             } else {
4417                 cfg.cls += ' navbar-nav';
4418             }
4419         } else {
4420             if (['tabs','pills'].indexOf(this.type) != -1) {
4421                 cfg.cls += ' nav-' + this.type
4422             } else {
4423                 if (this.type !== 'nav') {
4424                     Roo.log('nav type must be nav/tabs/pills')
4425                 }
4426                 cfg.cls += ' navbar-nav'
4427             }
4428         }
4429         
4430         if (this.parent() && this.parent().sidebar) {
4431             cfg = {
4432                 tag: 'ul',
4433                 cls: 'dashboard-menu sidebar-menu'
4434             };
4435             
4436             return cfg;
4437         }
4438         
4439         if (this.form === true) {
4440             cfg = {
4441                 tag: 'form',
4442                 cls: 'navbar-form form-inline'
4443             };
4444             
4445             if (this.align === 'right') {
4446                 cfg.cls += ' navbar-right ml-md-auto';
4447             } else {
4448                 cfg.cls += ' navbar-left';
4449             }
4450         }
4451         
4452         if (this.align === 'right') {
4453             cfg.cls += ' navbar-right ml-md-auto';
4454         } else {
4455             cfg.cls += ' mr-auto';
4456         }
4457         
4458         if (this.inverse) {
4459             cfg.cls += ' navbar-inverse';
4460             
4461         }
4462         
4463         
4464         return cfg;
4465     },
4466     /**
4467     * sets the active Navigation item
4468     * @param {Roo.bootstrap.NavItem} the new current navitem
4469     */
4470     setActiveItem : function(item)
4471     {
4472         var prev = false;
4473         Roo.each(this.navItems, function(v){
4474             if (v == item) {
4475                 return ;
4476             }
4477             if (v.isActive()) {
4478                 v.setActive(false, true);
4479                 prev = v;
4480                 
4481             }
4482             
4483         });
4484
4485         item.setActive(true, true);
4486         this.fireEvent('changed', this, item, prev);
4487         
4488         
4489     },
4490     /**
4491     * gets the active Navigation item
4492     * @return {Roo.bootstrap.NavItem} the current navitem
4493     */
4494     getActive : function()
4495     {
4496         
4497         var prev = false;
4498         Roo.each(this.navItems, function(v){
4499             
4500             if (v.isActive()) {
4501                 prev = v;
4502                 
4503             }
4504             
4505         });
4506         return prev;
4507     },
4508     
4509     indexOfNav : function()
4510     {
4511         
4512         var prev = false;
4513         Roo.each(this.navItems, function(v,i){
4514             
4515             if (v.isActive()) {
4516                 prev = i;
4517                 
4518             }
4519             
4520         });
4521         return prev;
4522     },
4523     /**
4524     * adds a Navigation item
4525     * @param {Roo.bootstrap.NavItem} the navitem to add
4526     */
4527     addItem : function(cfg)
4528     {
4529         if (this.form && Roo.bootstrap.version == 4) {
4530             cfg.tag = 'div';
4531         }
4532         var cn = new Roo.bootstrap.NavItem(cfg);
4533         this.register(cn);
4534         cn.parentId = this.id;
4535         cn.onRender(this.el, null);
4536         return cn;
4537     },
4538     /**
4539     * register a Navigation item
4540     * @param {Roo.bootstrap.NavItem} the navitem to add
4541     */
4542     register : function(item)
4543     {
4544         this.navItems.push( item);
4545         item.navId = this.navId;
4546     
4547     },
4548     
4549     /**
4550     * clear all the Navigation item
4551     */
4552    
4553     clearAll : function()
4554     {
4555         this.navItems = [];
4556         this.el.dom.innerHTML = '';
4557     },
4558     
4559     getNavItem: function(tabId)
4560     {
4561         var ret = false;
4562         Roo.each(this.navItems, function(e) {
4563             if (e.tabId == tabId) {
4564                ret =  e;
4565                return false;
4566             }
4567             return true;
4568             
4569         });
4570         return ret;
4571     },
4572     
4573     setActiveNext : function()
4574     {
4575         var i = this.indexOfNav(this.getActive());
4576         if (i > this.navItems.length) {
4577             return;
4578         }
4579         this.setActiveItem(this.navItems[i+1]);
4580     },
4581     setActivePrev : function()
4582     {
4583         var i = this.indexOfNav(this.getActive());
4584         if (i  < 1) {
4585             return;
4586         }
4587         this.setActiveItem(this.navItems[i-1]);
4588     },
4589     clearWasActive : function(except) {
4590         Roo.each(this.navItems, function(e) {
4591             if (e.tabId != except.tabId && e.was_active) {
4592                e.was_active = false;
4593                return false;
4594             }
4595             return true;
4596             
4597         });
4598     },
4599     getWasActive : function ()
4600     {
4601         var r = false;
4602         Roo.each(this.navItems, function(e) {
4603             if (e.was_active) {
4604                r = e;
4605                return false;
4606             }
4607             return true;
4608             
4609         });
4610         return r;
4611     }
4612     
4613     
4614 });
4615
4616  
4617 Roo.apply(Roo.bootstrap.NavGroup, {
4618     
4619     groups: {},
4620      /**
4621     * register a Navigation Group
4622     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4623     */
4624     register : function(navgrp)
4625     {
4626         this.groups[navgrp.navId] = navgrp;
4627         
4628     },
4629     /**
4630     * fetch a Navigation Group based on the navigation ID
4631     * @param {string} the navgroup to add
4632     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4633     */
4634     get: function(navId) {
4635         if (typeof(this.groups[navId]) == 'undefined') {
4636             return false;
4637             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4638         }
4639         return this.groups[navId] ;
4640     }
4641     
4642     
4643     
4644 });
4645
4646  /*
4647  * - LGPL
4648  *
4649  * row
4650  * 
4651  */
4652
4653 /**
4654  * @class Roo.bootstrap.NavItem
4655  * @extends Roo.bootstrap.Component
4656  * Bootstrap Navbar.NavItem class
4657  * @cfg {String} href  link to
4658  * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
4659
4660  * @cfg {String} html content of button
4661  * @cfg {String} badge text inside badge
4662  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4663  * @cfg {String} glyphicon DEPRICATED - use fa
4664  * @cfg {String} icon DEPRICATED - use fa
4665  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4666  * @cfg {Boolean} active Is item active
4667  * @cfg {Boolean} disabled Is item disabled
4668  
4669  * @cfg {Boolean} preventDefault (true | false) default false
4670  * @cfg {String} tabId the tab that this item activates.
4671  * @cfg {String} tagtype (a|span) render as a href or span?
4672  * @cfg {Boolean} animateRef (true|false) link to element default false  
4673   
4674  * @constructor
4675  * Create a new Navbar Item
4676  * @param {Object} config The config object
4677  */
4678 Roo.bootstrap.NavItem = function(config){
4679     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4680     this.addEvents({
4681         // raw events
4682         /**
4683          * @event click
4684          * The raw click event for the entire grid.
4685          * @param {Roo.EventObject} e
4686          */
4687         "click" : true,
4688          /**
4689             * @event changed
4690             * Fires when the active item active state changes
4691             * @param {Roo.bootstrap.NavItem} this
4692             * @param {boolean} state the new state
4693              
4694          */
4695         'changed': true,
4696         /**
4697             * @event scrollto
4698             * Fires when scroll to element
4699             * @param {Roo.bootstrap.NavItem} this
4700             * @param {Object} options
4701             * @param {Roo.EventObject} e
4702              
4703          */
4704         'scrollto': true
4705     });
4706    
4707 };
4708
4709 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4710     
4711     href: false,
4712     html: '',
4713     badge: '',
4714     icon: false,
4715     fa : false,
4716     glyphicon: false,
4717     active: false,
4718     preventDefault : false,
4719     tabId : false,
4720     tagtype : 'a',
4721     tag: 'li',
4722     disabled : false,
4723     animateRef : false,
4724     was_active : false,
4725     button_weight : '',
4726     button_outline : false,
4727     
4728     navLink: false,
4729     
4730     getAutoCreate : function(){
4731          
4732         var cfg = {
4733             tag: this.tag,
4734             cls: 'nav-item'
4735         };
4736         
4737         if (this.active) {
4738             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4739         }
4740         if (this.disabled) {
4741             cfg.cls += ' disabled';
4742         }
4743         
4744         // BS4 only?
4745         if (this.button_weight.length) {
4746             cfg.tag = this.href ? 'a' : 'button';
4747             cfg.html = this.html || '';
4748             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
4749             if (this.href) {
4750                 cfg.href = this.href;
4751             }
4752             if (this.fa) {
4753                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
4754             }
4755             
4756             // menu .. should add dropdown-menu class - so no need for carat..
4757             
4758             if (this.badge !== '') {
4759                  
4760                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4761             }
4762             return cfg;
4763         }
4764         
4765         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
4766             cfg.cn = [
4767                 {
4768                     tag: this.tagtype,
4769                     href : this.href || "#",
4770                     html: this.html || ''
4771                 }
4772             ];
4773             if (this.tagtype == 'a') {
4774                 cfg.cn[0].cls = 'nav-link';
4775             }
4776             if (this.icon) {
4777                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
4778             }
4779             if (this.fa) {
4780                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
4781             }
4782             if(this.glyphicon) {
4783                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4784             }
4785             
4786             if (this.menu) {
4787                 
4788                 cfg.cn[0].html += " <span class='caret'></span>";
4789              
4790             }
4791             
4792             if (this.badge !== '') {
4793                  
4794                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4795             }
4796         }
4797         
4798         
4799         
4800         return cfg;
4801     },
4802     onRender : function(ct, position)
4803     {
4804        // Roo.log("Call onRender: " + this.xtype);
4805         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
4806             this.tag = 'div';
4807         }
4808         
4809         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
4810         this.navLink = this.el.select('.nav-link',true).first();
4811         return ret;
4812     },
4813       
4814     
4815     initEvents: function() 
4816     {
4817         if (typeof (this.menu) != 'undefined') {
4818             this.menu.parentType = this.xtype;
4819             this.menu.triggerEl = this.el;
4820             this.menu = this.addxtype(Roo.apply({}, this.menu));
4821         }
4822         
4823         this.el.select('a',true).on('click', this.onClick, this);
4824         
4825         if(this.tagtype == 'span'){
4826             this.el.select('span',true).on('click', this.onClick, this);
4827         }
4828        
4829         // at this point parent should be available..
4830         this.parent().register(this);
4831     },
4832     
4833     onClick : function(e)
4834     {
4835         if (e.getTarget('.dropdown-menu-item')) {
4836             // did you click on a menu itemm.... - then don't trigger onclick..
4837             return;
4838         }
4839         
4840         if(
4841                 this.preventDefault || 
4842                 this.href == '#' 
4843         ){
4844             Roo.log("NavItem - prevent Default?");
4845             e.preventDefault();
4846         }
4847         
4848         if (this.disabled) {
4849             return;
4850         }
4851         
4852         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4853         if (tg && tg.transition) {
4854             Roo.log("waiting for the transitionend");
4855             return;
4856         }
4857         
4858         
4859         
4860         //Roo.log("fire event clicked");
4861         if(this.fireEvent('click', this, e) === false){
4862             return;
4863         };
4864         
4865         if(this.tagtype == 'span'){
4866             return;
4867         }
4868         
4869         //Roo.log(this.href);
4870         var ael = this.el.select('a',true).first();
4871         //Roo.log(ael);
4872         
4873         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4874             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4875             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4876                 return; // ignore... - it's a 'hash' to another page.
4877             }
4878             Roo.log("NavItem - prevent Default?");
4879             e.preventDefault();
4880             this.scrollToElement(e);
4881         }
4882         
4883         
4884         var p =  this.parent();
4885    
4886         if (['tabs','pills'].indexOf(p.type)!==-1) {
4887             if (typeof(p.setActiveItem) !== 'undefined') {
4888                 p.setActiveItem(this);
4889             }
4890         }
4891         
4892         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4893         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4894             // remove the collapsed menu expand...
4895             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4896         }
4897     },
4898     
4899     isActive: function () {
4900         return this.active
4901     },
4902     setActive : function(state, fire, is_was_active)
4903     {
4904         if (this.active && !state && this.navId) {
4905             this.was_active = true;
4906             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4907             if (nv) {
4908                 nv.clearWasActive(this);
4909             }
4910             
4911         }
4912         this.active = state;
4913         
4914         if (!state ) {
4915             this.el.removeClass('active');
4916             this.navLink ? this.navLink.removeClass('active') : false;
4917         } else if (!this.el.hasClass('active')) {
4918             
4919             this.el.addClass('active');
4920             if (Roo.bootstrap.version == 4 && this.navLink ) {
4921                 this.navLink.addClass('active');
4922             }
4923             
4924         }
4925         if (fire) {
4926             this.fireEvent('changed', this, state);
4927         }
4928         
4929         // show a panel if it's registered and related..
4930         
4931         if (!this.navId || !this.tabId || !state || is_was_active) {
4932             return;
4933         }
4934         
4935         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4936         if (!tg) {
4937             return;
4938         }
4939         var pan = tg.getPanelByName(this.tabId);
4940         if (!pan) {
4941             return;
4942         }
4943         // if we can not flip to new panel - go back to old nav highlight..
4944         if (false == tg.showPanel(pan)) {
4945             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4946             if (nv) {
4947                 var onav = nv.getWasActive();
4948                 if (onav) {
4949                     onav.setActive(true, false, true);
4950                 }
4951             }
4952             
4953         }
4954         
4955         
4956         
4957     },
4958      // this should not be here...
4959     setDisabled : function(state)
4960     {
4961         this.disabled = state;
4962         if (!state ) {
4963             this.el.removeClass('disabled');
4964         } else if (!this.el.hasClass('disabled')) {
4965             this.el.addClass('disabled');
4966         }
4967         
4968     },
4969     
4970     /**
4971      * Fetch the element to display the tooltip on.
4972      * @return {Roo.Element} defaults to this.el
4973      */
4974     tooltipEl : function()
4975     {
4976         return this.el.select('' + this.tagtype + '', true).first();
4977     },
4978     
4979     scrollToElement : function(e)
4980     {
4981         var c = document.body;
4982         
4983         /*
4984          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4985          */
4986         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4987             c = document.documentElement;
4988         }
4989         
4990         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4991         
4992         if(!target){
4993             return;
4994         }
4995
4996         var o = target.calcOffsetsTo(c);
4997         
4998         var options = {
4999             target : target,
5000             value : o[1]
5001         };
5002         
5003         this.fireEvent('scrollto', this, options, e);
5004         
5005         Roo.get(c).scrollTo('top', options.value, true);
5006         
5007         return;
5008     }
5009 });
5010  
5011
5012  /*
5013  * - LGPL
5014  *
5015  * sidebar item
5016  *
5017  *  li
5018  *    <span> icon </span>
5019  *    <span> text </span>
5020  *    <span>badge </span>
5021  */
5022
5023 /**
5024  * @class Roo.bootstrap.NavSidebarItem
5025  * @extends Roo.bootstrap.NavItem
5026  * Bootstrap Navbar.NavSidebarItem class
5027  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
5028  * {Boolean} open is the menu open
5029  * {Boolean} buttonView use button as the tigger el rather that a (default false)
5030  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
5031  * {String} buttonSize (sm|md|lg)the extra classes for the button
5032  * {Boolean} showArrow show arrow next to the text (default true)
5033  * @constructor
5034  * Create a new Navbar Button
5035  * @param {Object} config The config object
5036  */
5037 Roo.bootstrap.NavSidebarItem = function(config){
5038     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
5039     this.addEvents({
5040         // raw events
5041         /**
5042          * @event click
5043          * The raw click event for the entire grid.
5044          * @param {Roo.EventObject} e
5045          */
5046         "click" : true,
5047          /**
5048             * @event changed
5049             * Fires when the active item active state changes
5050             * @param {Roo.bootstrap.NavSidebarItem} this
5051             * @param {boolean} state the new state
5052              
5053          */
5054         'changed': true
5055     });
5056    
5057 };
5058
5059 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
5060     
5061     badgeWeight : 'default',
5062     
5063     open: false,
5064     
5065     buttonView : false,
5066     
5067     buttonWeight : 'default',
5068     
5069     buttonSize : 'md',
5070     
5071     showArrow : true,
5072     
5073     getAutoCreate : function(){
5074         
5075         
5076         var a = {
5077                 tag: 'a',
5078                 href : this.href || '#',
5079                 cls: '',
5080                 html : '',
5081                 cn : []
5082         };
5083         
5084         if(this.buttonView){
5085             a = {
5086                 tag: 'button',
5087                 href : this.href || '#',
5088                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
5089                 html : this.html,
5090                 cn : []
5091             };
5092         }
5093         
5094         var cfg = {
5095             tag: 'li',
5096             cls: '',
5097             cn: [ a ]
5098         };
5099         
5100         if (this.active) {
5101             cfg.cls += ' active';
5102         }
5103         
5104         if (this.disabled) {
5105             cfg.cls += ' disabled';
5106         }
5107         if (this.open) {
5108             cfg.cls += ' open x-open';
5109         }
5110         // left icon..
5111         if (this.glyphicon || this.icon) {
5112             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
5113             a.cn.push({ tag : 'i', cls : c }) ;
5114         }
5115         
5116         if(!this.buttonView){
5117             var span = {
5118                 tag: 'span',
5119                 html : this.html || ''
5120             };
5121
5122             a.cn.push(span);
5123             
5124         }
5125         
5126         if (this.badge !== '') {
5127             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
5128         }
5129         
5130         if (this.menu) {
5131             
5132             if(this.showArrow){
5133                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5134             }
5135             
5136             a.cls += ' dropdown-toggle treeview' ;
5137         }
5138         
5139         return cfg;
5140     },
5141     
5142     initEvents : function()
5143     { 
5144         if (typeof (this.menu) != 'undefined') {
5145             this.menu.parentType = this.xtype;
5146             this.menu.triggerEl = this.el;
5147             this.menu = this.addxtype(Roo.apply({}, this.menu));
5148         }
5149         
5150         this.el.on('click', this.onClick, this);
5151         
5152         if(this.badge !== ''){
5153             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5154         }
5155         
5156     },
5157     
5158     onClick : function(e)
5159     {
5160         if(this.disabled){
5161             e.preventDefault();
5162             return;
5163         }
5164         
5165         if(this.preventDefault){
5166             e.preventDefault();
5167         }
5168         
5169         this.fireEvent('click', this, e);
5170     },
5171     
5172     disable : function()
5173     {
5174         this.setDisabled(true);
5175     },
5176     
5177     enable : function()
5178     {
5179         this.setDisabled(false);
5180     },
5181     
5182     setDisabled : function(state)
5183     {
5184         if(this.disabled == state){
5185             return;
5186         }
5187         
5188         this.disabled = state;
5189         
5190         if (state) {
5191             this.el.addClass('disabled');
5192             return;
5193         }
5194         
5195         this.el.removeClass('disabled');
5196         
5197         return;
5198     },
5199     
5200     setActive : function(state)
5201     {
5202         if(this.active == state){
5203             return;
5204         }
5205         
5206         this.active = state;
5207         
5208         if (state) {
5209             this.el.addClass('active');
5210             return;
5211         }
5212         
5213         this.el.removeClass('active');
5214         
5215         return;
5216     },
5217     
5218     isActive: function () 
5219     {
5220         return this.active;
5221     },
5222     
5223     setBadge : function(str)
5224     {
5225         if(!this.badgeEl){
5226             return;
5227         }
5228         
5229         this.badgeEl.dom.innerHTML = str;
5230     }
5231     
5232    
5233      
5234  
5235 });
5236  
5237
5238  /*
5239  * - LGPL
5240  *
5241  * row
5242  * 
5243  */
5244
5245 /**
5246  * @class Roo.bootstrap.Row
5247  * @extends Roo.bootstrap.Component
5248  * Bootstrap Row class (contains columns...)
5249  * 
5250  * @constructor
5251  * Create a new Row
5252  * @param {Object} config The config object
5253  */
5254
5255 Roo.bootstrap.Row = function(config){
5256     Roo.bootstrap.Row.superclass.constructor.call(this, config);
5257 };
5258
5259 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
5260     
5261     getAutoCreate : function(){
5262        return {
5263             cls: 'row clearfix'
5264        };
5265     }
5266     
5267     
5268 });
5269
5270  
5271
5272  /*
5273  * - LGPL
5274  *
5275  * element
5276  * 
5277  */
5278
5279 /**
5280  * @class Roo.bootstrap.Element
5281  * @extends Roo.bootstrap.Component
5282  * Bootstrap Element class
5283  * @cfg {String} html contents of the element
5284  * @cfg {String} tag tag of the element
5285  * @cfg {String} cls class of the element
5286  * @cfg {Boolean} preventDefault (true|false) default false
5287  * @cfg {Boolean} clickable (true|false) default false
5288  * 
5289  * @constructor
5290  * Create a new Element
5291  * @param {Object} config The config object
5292  */
5293
5294 Roo.bootstrap.Element = function(config){
5295     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5296     
5297     this.addEvents({
5298         // raw events
5299         /**
5300          * @event click
5301          * When a element is chick
5302          * @param {Roo.bootstrap.Element} this
5303          * @param {Roo.EventObject} e
5304          */
5305         "click" : true
5306     });
5307 };
5308
5309 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5310     
5311     tag: 'div',
5312     cls: '',
5313     html: '',
5314     preventDefault: false, 
5315     clickable: false,
5316     
5317     getAutoCreate : function(){
5318         
5319         var cfg = {
5320             tag: this.tag,
5321             // cls: this.cls, double assign in parent class Component.js :: onRender
5322             html: this.html
5323         };
5324         
5325         return cfg;
5326     },
5327     
5328     initEvents: function() 
5329     {
5330         Roo.bootstrap.Element.superclass.initEvents.call(this);
5331         
5332         if(this.clickable){
5333             this.el.on('click', this.onClick, this);
5334         }
5335         
5336     },
5337     
5338     onClick : function(e)
5339     {
5340         if(this.preventDefault){
5341             e.preventDefault();
5342         }
5343         
5344         this.fireEvent('click', this, e);
5345     },
5346     
5347     getValue : function()
5348     {
5349         return this.el.dom.innerHTML;
5350     },
5351     
5352     setValue : function(value)
5353     {
5354         this.el.dom.innerHTML = value;
5355     }
5356    
5357 });
5358
5359  
5360
5361  /*
5362  * - LGPL
5363  *
5364  * pagination
5365  * 
5366  */
5367
5368 /**
5369  * @class Roo.bootstrap.Pagination
5370  * @extends Roo.bootstrap.Component
5371  * Bootstrap Pagination class
5372  * @cfg {String} size xs | sm | md | lg
5373  * @cfg {Boolean} inverse false | true
5374  * 
5375  * @constructor
5376  * Create a new Pagination
5377  * @param {Object} config The config object
5378  */
5379
5380 Roo.bootstrap.Pagination = function(config){
5381     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5382 };
5383
5384 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5385     
5386     cls: false,
5387     size: false,
5388     inverse: false,
5389     
5390     getAutoCreate : function(){
5391         var cfg = {
5392             tag: 'ul',
5393                 cls: 'pagination'
5394         };
5395         if (this.inverse) {
5396             cfg.cls += ' inverse';
5397         }
5398         if (this.html) {
5399             cfg.html=this.html;
5400         }
5401         if (this.cls) {
5402             cfg.cls += " " + this.cls;
5403         }
5404         return cfg;
5405     }
5406    
5407 });
5408
5409  
5410
5411  /*
5412  * - LGPL
5413  *
5414  * Pagination item
5415  * 
5416  */
5417
5418
5419 /**
5420  * @class Roo.bootstrap.PaginationItem
5421  * @extends Roo.bootstrap.Component
5422  * Bootstrap PaginationItem class
5423  * @cfg {String} html text
5424  * @cfg {String} href the link
5425  * @cfg {Boolean} preventDefault (true | false) default true
5426  * @cfg {Boolean} active (true | false) default false
5427  * @cfg {Boolean} disabled default false
5428  * 
5429  * 
5430  * @constructor
5431  * Create a new PaginationItem
5432  * @param {Object} config The config object
5433  */
5434
5435
5436 Roo.bootstrap.PaginationItem = function(config){
5437     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5438     this.addEvents({
5439         // raw events
5440         /**
5441          * @event click
5442          * The raw click event for the entire grid.
5443          * @param {Roo.EventObject} e
5444          */
5445         "click" : true
5446     });
5447 };
5448
5449 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5450     
5451     href : false,
5452     html : false,
5453     preventDefault: true,
5454     active : false,
5455     cls : false,
5456     disabled: false,
5457     
5458     getAutoCreate : function(){
5459         var cfg= {
5460             tag: 'li',
5461             cn: [
5462                 {
5463                     tag : 'a',
5464                     href : this.href ? this.href : '#',
5465                     html : this.html ? this.html : ''
5466                 }
5467             ]
5468         };
5469         
5470         if(this.cls){
5471             cfg.cls = this.cls;
5472         }
5473         
5474         if(this.disabled){
5475             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5476         }
5477         
5478         if(this.active){
5479             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5480         }
5481         
5482         return cfg;
5483     },
5484     
5485     initEvents: function() {
5486         
5487         this.el.on('click', this.onClick, this);
5488         
5489     },
5490     onClick : function(e)
5491     {
5492         Roo.log('PaginationItem on click ');
5493         if(this.preventDefault){
5494             e.preventDefault();
5495         }
5496         
5497         if(this.disabled){
5498             return;
5499         }
5500         
5501         this.fireEvent('click', this, e);
5502     }
5503    
5504 });
5505
5506  
5507
5508  /*
5509  * - LGPL
5510  *
5511  * slider
5512  * 
5513  */
5514
5515
5516 /**
5517  * @class Roo.bootstrap.Slider
5518  * @extends Roo.bootstrap.Component
5519  * Bootstrap Slider class
5520  *    
5521  * @constructor
5522  * Create a new Slider
5523  * @param {Object} config The config object
5524  */
5525
5526 Roo.bootstrap.Slider = function(config){
5527     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5528 };
5529
5530 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5531     
5532     getAutoCreate : function(){
5533         
5534         var cfg = {
5535             tag: 'div',
5536             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5537             cn: [
5538                 {
5539                     tag: 'a',
5540                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5541                 }
5542             ]
5543         };
5544         
5545         return cfg;
5546     }
5547    
5548 });
5549
5550  /*
5551  * Based on:
5552  * Ext JS Library 1.1.1
5553  * Copyright(c) 2006-2007, Ext JS, LLC.
5554  *
5555  * Originally Released Under LGPL - original licence link has changed is not relivant.
5556  *
5557  * Fork - LGPL
5558  * <script type="text/javascript">
5559  */
5560  
5561
5562 /**
5563  * @class Roo.grid.ColumnModel
5564  * @extends Roo.util.Observable
5565  * This is the default implementation of a ColumnModel used by the Grid. It defines
5566  * the columns in the grid.
5567  * <br>Usage:<br>
5568  <pre><code>
5569  var colModel = new Roo.grid.ColumnModel([
5570         {header: "Ticker", width: 60, sortable: true, locked: true},
5571         {header: "Company Name", width: 150, sortable: true},
5572         {header: "Market Cap.", width: 100, sortable: true},
5573         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5574         {header: "Employees", width: 100, sortable: true, resizable: false}
5575  ]);
5576  </code></pre>
5577  * <p>
5578  
5579  * The config options listed for this class are options which may appear in each
5580  * individual column definition.
5581  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5582  * @constructor
5583  * @param {Object} config An Array of column config objects. See this class's
5584  * config objects for details.
5585 */
5586 Roo.grid.ColumnModel = function(config){
5587         /**
5588      * The config passed into the constructor
5589      */
5590     this.config = config;
5591     this.lookup = {};
5592
5593     // if no id, create one
5594     // if the column does not have a dataIndex mapping,
5595     // map it to the order it is in the config
5596     for(var i = 0, len = config.length; i < len; i++){
5597         var c = config[i];
5598         if(typeof c.dataIndex == "undefined"){
5599             c.dataIndex = i;
5600         }
5601         if(typeof c.renderer == "string"){
5602             c.renderer = Roo.util.Format[c.renderer];
5603         }
5604         if(typeof c.id == "undefined"){
5605             c.id = Roo.id();
5606         }
5607         if(c.editor && c.editor.xtype){
5608             c.editor  = Roo.factory(c.editor, Roo.grid);
5609         }
5610         if(c.editor && c.editor.isFormField){
5611             c.editor = new Roo.grid.GridEditor(c.editor);
5612         }
5613         this.lookup[c.id] = c;
5614     }
5615
5616     /**
5617      * The width of columns which have no width specified (defaults to 100)
5618      * @type Number
5619      */
5620     this.defaultWidth = 100;
5621
5622     /**
5623      * Default sortable of columns which have no sortable specified (defaults to false)
5624      * @type Boolean
5625      */
5626     this.defaultSortable = false;
5627
5628     this.addEvents({
5629         /**
5630              * @event widthchange
5631              * Fires when the width of a column changes.
5632              * @param {ColumnModel} this
5633              * @param {Number} columnIndex The column index
5634              * @param {Number} newWidth The new width
5635              */
5636             "widthchange": true,
5637         /**
5638              * @event headerchange
5639              * Fires when the text of a header changes.
5640              * @param {ColumnModel} this
5641              * @param {Number} columnIndex The column index
5642              * @param {Number} newText The new header text
5643              */
5644             "headerchange": true,
5645         /**
5646              * @event hiddenchange
5647              * Fires when a column is hidden or "unhidden".
5648              * @param {ColumnModel} this
5649              * @param {Number} columnIndex The column index
5650              * @param {Boolean} hidden true if hidden, false otherwise
5651              */
5652             "hiddenchange": true,
5653             /**
5654          * @event columnmoved
5655          * Fires when a column is moved.
5656          * @param {ColumnModel} this
5657          * @param {Number} oldIndex
5658          * @param {Number} newIndex
5659          */
5660         "columnmoved" : true,
5661         /**
5662          * @event columlockchange
5663          * Fires when a column's locked state is changed
5664          * @param {ColumnModel} this
5665          * @param {Number} colIndex
5666          * @param {Boolean} locked true if locked
5667          */
5668         "columnlockchange" : true
5669     });
5670     Roo.grid.ColumnModel.superclass.constructor.call(this);
5671 };
5672 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5673     /**
5674      * @cfg {String} header The header text to display in the Grid view.
5675      */
5676     /**
5677      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5678      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5679      * specified, the column's index is used as an index into the Record's data Array.
5680      */
5681     /**
5682      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5683      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5684      */
5685     /**
5686      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5687      * Defaults to the value of the {@link #defaultSortable} property.
5688      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5689      */
5690     /**
5691      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5692      */
5693     /**
5694      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5695      */
5696     /**
5697      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5698      */
5699     /**
5700      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5701      */
5702     /**
5703      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5704      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5705      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5706      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5707      */
5708        /**
5709      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5710      */
5711     /**
5712      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5713      */
5714     /**
5715      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5716      */
5717     /**
5718      * @cfg {String} cursor (Optional)
5719      */
5720     /**
5721      * @cfg {String} tooltip (Optional)
5722      */
5723     /**
5724      * @cfg {Number} xs (Optional)
5725      */
5726     /**
5727      * @cfg {Number} sm (Optional)
5728      */
5729     /**
5730      * @cfg {Number} md (Optional)
5731      */
5732     /**
5733      * @cfg {Number} lg (Optional)
5734      */
5735     /**
5736      * Returns the id of the column at the specified index.
5737      * @param {Number} index The column index
5738      * @return {String} the id
5739      */
5740     getColumnId : function(index){
5741         return this.config[index].id;
5742     },
5743
5744     /**
5745      * Returns the column for a specified id.
5746      * @param {String} id The column id
5747      * @return {Object} the column
5748      */
5749     getColumnById : function(id){
5750         return this.lookup[id];
5751     },
5752
5753     
5754     /**
5755      * Returns the column for a specified dataIndex.
5756      * @param {String} dataIndex The column dataIndex
5757      * @return {Object|Boolean} the column or false if not found
5758      */
5759     getColumnByDataIndex: function(dataIndex){
5760         var index = this.findColumnIndex(dataIndex);
5761         return index > -1 ? this.config[index] : false;
5762     },
5763     
5764     /**
5765      * Returns the index for a specified column id.
5766      * @param {String} id The column id
5767      * @return {Number} the index, or -1 if not found
5768      */
5769     getIndexById : function(id){
5770         for(var i = 0, len = this.config.length; i < len; i++){
5771             if(this.config[i].id == id){
5772                 return i;
5773             }
5774         }
5775         return -1;
5776     },
5777     
5778     /**
5779      * Returns the index for a specified column dataIndex.
5780      * @param {String} dataIndex The column dataIndex
5781      * @return {Number} the index, or -1 if not found
5782      */
5783     
5784     findColumnIndex : function(dataIndex){
5785         for(var i = 0, len = this.config.length; i < len; i++){
5786             if(this.config[i].dataIndex == dataIndex){
5787                 return i;
5788             }
5789         }
5790         return -1;
5791     },
5792     
5793     
5794     moveColumn : function(oldIndex, newIndex){
5795         var c = this.config[oldIndex];
5796         this.config.splice(oldIndex, 1);
5797         this.config.splice(newIndex, 0, c);
5798         this.dataMap = null;
5799         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5800     },
5801
5802     isLocked : function(colIndex){
5803         return this.config[colIndex].locked === true;
5804     },
5805
5806     setLocked : function(colIndex, value, suppressEvent){
5807         if(this.isLocked(colIndex) == value){
5808             return;
5809         }
5810         this.config[colIndex].locked = value;
5811         if(!suppressEvent){
5812             this.fireEvent("columnlockchange", this, colIndex, value);
5813         }
5814     },
5815
5816     getTotalLockedWidth : function(){
5817         var totalWidth = 0;
5818         for(var i = 0; i < this.config.length; i++){
5819             if(this.isLocked(i) && !this.isHidden(i)){
5820                 this.totalWidth += this.getColumnWidth(i);
5821             }
5822         }
5823         return totalWidth;
5824     },
5825
5826     getLockedCount : function(){
5827         for(var i = 0, len = this.config.length; i < len; i++){
5828             if(!this.isLocked(i)){
5829                 return i;
5830             }
5831         }
5832         
5833         return this.config.length;
5834     },
5835
5836     /**
5837      * Returns the number of columns.
5838      * @return {Number}
5839      */
5840     getColumnCount : function(visibleOnly){
5841         if(visibleOnly === true){
5842             var c = 0;
5843             for(var i = 0, len = this.config.length; i < len; i++){
5844                 if(!this.isHidden(i)){
5845                     c++;
5846                 }
5847             }
5848             return c;
5849         }
5850         return this.config.length;
5851     },
5852
5853     /**
5854      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5855      * @param {Function} fn
5856      * @param {Object} scope (optional)
5857      * @return {Array} result
5858      */
5859     getColumnsBy : function(fn, scope){
5860         var r = [];
5861         for(var i = 0, len = this.config.length; i < len; i++){
5862             var c = this.config[i];
5863             if(fn.call(scope||this, c, i) === true){
5864                 r[r.length] = c;
5865             }
5866         }
5867         return r;
5868     },
5869
5870     /**
5871      * Returns true if the specified column is sortable.
5872      * @param {Number} col The column index
5873      * @return {Boolean}
5874      */
5875     isSortable : function(col){
5876         if(typeof this.config[col].sortable == "undefined"){
5877             return this.defaultSortable;
5878         }
5879         return this.config[col].sortable;
5880     },
5881
5882     /**
5883      * Returns the rendering (formatting) function defined for the column.
5884      * @param {Number} col The column index.
5885      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5886      */
5887     getRenderer : function(col){
5888         if(!this.config[col].renderer){
5889             return Roo.grid.ColumnModel.defaultRenderer;
5890         }
5891         return this.config[col].renderer;
5892     },
5893
5894     /**
5895      * Sets the rendering (formatting) function for a column.
5896      * @param {Number} col The column index
5897      * @param {Function} fn The function to use to process the cell's raw data
5898      * to return HTML markup for the grid view. The render function is called with
5899      * the following parameters:<ul>
5900      * <li>Data value.</li>
5901      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5902      * <li>css A CSS style string to apply to the table cell.</li>
5903      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5904      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5905      * <li>Row index</li>
5906      * <li>Column index</li>
5907      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5908      */
5909     setRenderer : function(col, fn){
5910         this.config[col].renderer = fn;
5911     },
5912
5913     /**
5914      * Returns the width for the specified column.
5915      * @param {Number} col The column index
5916      * @return {Number}
5917      */
5918     getColumnWidth : function(col){
5919         return this.config[col].width * 1 || this.defaultWidth;
5920     },
5921
5922     /**
5923      * Sets the width for a column.
5924      * @param {Number} col The column index
5925      * @param {Number} width The new width
5926      */
5927     setColumnWidth : function(col, width, suppressEvent){
5928         this.config[col].width = width;
5929         this.totalWidth = null;
5930         if(!suppressEvent){
5931              this.fireEvent("widthchange", this, col, width);
5932         }
5933     },
5934
5935     /**
5936      * Returns the total width of all columns.
5937      * @param {Boolean} includeHidden True to include hidden column widths
5938      * @return {Number}
5939      */
5940     getTotalWidth : function(includeHidden){
5941         if(!this.totalWidth){
5942             this.totalWidth = 0;
5943             for(var i = 0, len = this.config.length; i < len; i++){
5944                 if(includeHidden || !this.isHidden(i)){
5945                     this.totalWidth += this.getColumnWidth(i);
5946                 }
5947             }
5948         }
5949         return this.totalWidth;
5950     },
5951
5952     /**
5953      * Returns the header for the specified column.
5954      * @param {Number} col The column index
5955      * @return {String}
5956      */
5957     getColumnHeader : function(col){
5958         return this.config[col].header;
5959     },
5960
5961     /**
5962      * Sets the header for a column.
5963      * @param {Number} col The column index
5964      * @param {String} header The new header
5965      */
5966     setColumnHeader : function(col, header){
5967         this.config[col].header = header;
5968         this.fireEvent("headerchange", this, col, header);
5969     },
5970
5971     /**
5972      * Returns the tooltip for the specified column.
5973      * @param {Number} col The column index
5974      * @return {String}
5975      */
5976     getColumnTooltip : function(col){
5977             return this.config[col].tooltip;
5978     },
5979     /**
5980      * Sets the tooltip for a column.
5981      * @param {Number} col The column index
5982      * @param {String} tooltip The new tooltip
5983      */
5984     setColumnTooltip : function(col, tooltip){
5985             this.config[col].tooltip = tooltip;
5986     },
5987
5988     /**
5989      * Returns the dataIndex for the specified column.
5990      * @param {Number} col The column index
5991      * @return {Number}
5992      */
5993     getDataIndex : function(col){
5994         return this.config[col].dataIndex;
5995     },
5996
5997     /**
5998      * Sets the dataIndex for a column.
5999      * @param {Number} col The column index
6000      * @param {Number} dataIndex The new dataIndex
6001      */
6002     setDataIndex : function(col, dataIndex){
6003         this.config[col].dataIndex = dataIndex;
6004     },
6005
6006     
6007     
6008     /**
6009      * Returns true if the cell is editable.
6010      * @param {Number} colIndex The column index
6011      * @param {Number} rowIndex The row index - this is nto actually used..?
6012      * @return {Boolean}
6013      */
6014     isCellEditable : function(colIndex, rowIndex){
6015         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
6016     },
6017
6018     /**
6019      * Returns the editor defined for the cell/column.
6020      * return false or null to disable editing.
6021      * @param {Number} colIndex The column index
6022      * @param {Number} rowIndex The row index
6023      * @return {Object}
6024      */
6025     getCellEditor : function(colIndex, rowIndex){
6026         return this.config[colIndex].editor;
6027     },
6028
6029     /**
6030      * Sets if a column is editable.
6031      * @param {Number} col The column index
6032      * @param {Boolean} editable True if the column is editable
6033      */
6034     setEditable : function(col, editable){
6035         this.config[col].editable = editable;
6036     },
6037
6038
6039     /**
6040      * Returns true if the column is hidden.
6041      * @param {Number} colIndex The column index
6042      * @return {Boolean}
6043      */
6044     isHidden : function(colIndex){
6045         return this.config[colIndex].hidden;
6046     },
6047
6048
6049     /**
6050      * Returns true if the column width cannot be changed
6051      */
6052     isFixed : function(colIndex){
6053         return this.config[colIndex].fixed;
6054     },
6055
6056     /**
6057      * Returns true if the column can be resized
6058      * @return {Boolean}
6059      */
6060     isResizable : function(colIndex){
6061         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
6062     },
6063     /**
6064      * Sets if a column is hidden.
6065      * @param {Number} colIndex The column index
6066      * @param {Boolean} hidden True if the column is hidden
6067      */
6068     setHidden : function(colIndex, hidden){
6069         this.config[colIndex].hidden = hidden;
6070         this.totalWidth = null;
6071         this.fireEvent("hiddenchange", this, colIndex, hidden);
6072     },
6073
6074     /**
6075      * Sets the editor for a column.
6076      * @param {Number} col The column index
6077      * @param {Object} editor The editor object
6078      */
6079     setEditor : function(col, editor){
6080         this.config[col].editor = editor;
6081     }
6082 });
6083
6084 Roo.grid.ColumnModel.defaultRenderer = function(value)
6085 {
6086     if(typeof value == "object") {
6087         return value;
6088     }
6089         if(typeof value == "string" && value.length < 1){
6090             return "&#160;";
6091         }
6092     
6093         return String.format("{0}", value);
6094 };
6095
6096 // Alias for backwards compatibility
6097 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
6098 /*
6099  * Based on:
6100  * Ext JS Library 1.1.1
6101  * Copyright(c) 2006-2007, Ext JS, LLC.
6102  *
6103  * Originally Released Under LGPL - original licence link has changed is not relivant.
6104  *
6105  * Fork - LGPL
6106  * <script type="text/javascript">
6107  */
6108  
6109 /**
6110  * @class Roo.LoadMask
6111  * A simple utility class for generically masking elements while loading data.  If the element being masked has
6112  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
6113  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
6114  * element's UpdateManager load indicator and will be destroyed after the initial load.
6115  * @constructor
6116  * Create a new LoadMask
6117  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
6118  * @param {Object} config The config object
6119  */
6120 Roo.LoadMask = function(el, config){
6121     this.el = Roo.get(el);
6122     Roo.apply(this, config);
6123     if(this.store){
6124         this.store.on('beforeload', this.onBeforeLoad, this);
6125         this.store.on('load', this.onLoad, this);
6126         this.store.on('loadexception', this.onLoadException, this);
6127         this.removeMask = false;
6128     }else{
6129         var um = this.el.getUpdateManager();
6130         um.showLoadIndicator = false; // disable the default indicator
6131         um.on('beforeupdate', this.onBeforeLoad, this);
6132         um.on('update', this.onLoad, this);
6133         um.on('failure', this.onLoad, this);
6134         this.removeMask = true;
6135     }
6136 };
6137
6138 Roo.LoadMask.prototype = {
6139     /**
6140      * @cfg {Boolean} removeMask
6141      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6142      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
6143      */
6144     /**
6145      * @cfg {String} msg
6146      * The text to display in a centered loading message box (defaults to 'Loading...')
6147      */
6148     msg : 'Loading...',
6149     /**
6150      * @cfg {String} msgCls
6151      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6152      */
6153     msgCls : 'x-mask-loading',
6154
6155     /**
6156      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6157      * @type Boolean
6158      */
6159     disabled: false,
6160
6161     /**
6162      * Disables the mask to prevent it from being displayed
6163      */
6164     disable : function(){
6165        this.disabled = true;
6166     },
6167
6168     /**
6169      * Enables the mask so that it can be displayed
6170      */
6171     enable : function(){
6172         this.disabled = false;
6173     },
6174     
6175     onLoadException : function()
6176     {
6177         Roo.log(arguments);
6178         
6179         if (typeof(arguments[3]) != 'undefined') {
6180             Roo.MessageBox.alert("Error loading",arguments[3]);
6181         } 
6182         /*
6183         try {
6184             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6185                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6186             }   
6187         } catch(e) {
6188             
6189         }
6190         */
6191     
6192         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6193     },
6194     // private
6195     onLoad : function()
6196     {
6197         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6198     },
6199
6200     // private
6201     onBeforeLoad : function(){
6202         if(!this.disabled){
6203             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6204         }
6205     },
6206
6207     // private
6208     destroy : function(){
6209         if(this.store){
6210             this.store.un('beforeload', this.onBeforeLoad, this);
6211             this.store.un('load', this.onLoad, this);
6212             this.store.un('loadexception', this.onLoadException, this);
6213         }else{
6214             var um = this.el.getUpdateManager();
6215             um.un('beforeupdate', this.onBeforeLoad, this);
6216             um.un('update', this.onLoad, this);
6217             um.un('failure', this.onLoad, this);
6218         }
6219     }
6220 };/*
6221  * - LGPL
6222  *
6223  * table
6224  * 
6225  */
6226
6227 /**
6228  * @class Roo.bootstrap.Table
6229  * @extends Roo.bootstrap.Component
6230  * Bootstrap Table class
6231  * @cfg {String} cls table class
6232  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6233  * @cfg {String} bgcolor Specifies the background color for a table
6234  * @cfg {Number} border Specifies whether the table cells should have borders or not
6235  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6236  * @cfg {Number} cellspacing Specifies the space between cells
6237  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6238  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6239  * @cfg {String} sortable Specifies that the table should be sortable
6240  * @cfg {String} summary Specifies a summary of the content of a table
6241  * @cfg {Number} width Specifies the width of a table
6242  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6243  * 
6244  * @cfg {boolean} striped Should the rows be alternative striped
6245  * @cfg {boolean} bordered Add borders to the table
6246  * @cfg {boolean} hover Add hover highlighting
6247  * @cfg {boolean} condensed Format condensed
6248  * @cfg {boolean} responsive Format condensed
6249  * @cfg {Boolean} loadMask (true|false) default false
6250  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6251  * @cfg {Boolean} headerShow (true|false) generate thead, default true
6252  * @cfg {Boolean} rowSelection (true|false) default false
6253  * @cfg {Boolean} cellSelection (true|false) default false
6254  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6255  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
6256  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
6257  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
6258  
6259  * 
6260  * @constructor
6261  * Create a new Table
6262  * @param {Object} config The config object
6263  */
6264
6265 Roo.bootstrap.Table = function(config){
6266     Roo.bootstrap.Table.superclass.constructor.call(this, config);
6267     
6268   
6269     
6270     // BC...
6271     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6272     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6273     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6274     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6275     
6276     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6277     if (this.sm) {
6278         this.sm.grid = this;
6279         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6280         this.sm = this.selModel;
6281         this.sm.xmodule = this.xmodule || false;
6282     }
6283     
6284     if (this.cm && typeof(this.cm.config) == 'undefined') {
6285         this.colModel = new Roo.grid.ColumnModel(this.cm);
6286         this.cm = this.colModel;
6287         this.cm.xmodule = this.xmodule || false;
6288     }
6289     if (this.store) {
6290         this.store= Roo.factory(this.store, Roo.data);
6291         this.ds = this.store;
6292         this.ds.xmodule = this.xmodule || false;
6293          
6294     }
6295     if (this.footer && this.store) {
6296         this.footer.dataSource = this.ds;
6297         this.footer = Roo.factory(this.footer);
6298     }
6299     
6300     /** @private */
6301     this.addEvents({
6302         /**
6303          * @event cellclick
6304          * Fires when a cell is clicked
6305          * @param {Roo.bootstrap.Table} this
6306          * @param {Roo.Element} el
6307          * @param {Number} rowIndex
6308          * @param {Number} columnIndex
6309          * @param {Roo.EventObject} e
6310          */
6311         "cellclick" : true,
6312         /**
6313          * @event celldblclick
6314          * Fires when a cell is double clicked
6315          * @param {Roo.bootstrap.Table} this
6316          * @param {Roo.Element} el
6317          * @param {Number} rowIndex
6318          * @param {Number} columnIndex
6319          * @param {Roo.EventObject} e
6320          */
6321         "celldblclick" : true,
6322         /**
6323          * @event rowclick
6324          * Fires when a row is clicked
6325          * @param {Roo.bootstrap.Table} this
6326          * @param {Roo.Element} el
6327          * @param {Number} rowIndex
6328          * @param {Roo.EventObject} e
6329          */
6330         "rowclick" : true,
6331         /**
6332          * @event rowdblclick
6333          * Fires when a row is double clicked
6334          * @param {Roo.bootstrap.Table} this
6335          * @param {Roo.Element} el
6336          * @param {Number} rowIndex
6337          * @param {Roo.EventObject} e
6338          */
6339         "rowdblclick" : true,
6340         /**
6341          * @event mouseover
6342          * Fires when a mouseover occur
6343          * @param {Roo.bootstrap.Table} this
6344          * @param {Roo.Element} el
6345          * @param {Number} rowIndex
6346          * @param {Number} columnIndex
6347          * @param {Roo.EventObject} e
6348          */
6349         "mouseover" : true,
6350         /**
6351          * @event mouseout
6352          * Fires when a mouseout occur
6353          * @param {Roo.bootstrap.Table} this
6354          * @param {Roo.Element} el
6355          * @param {Number} rowIndex
6356          * @param {Number} columnIndex
6357          * @param {Roo.EventObject} e
6358          */
6359         "mouseout" : true,
6360         /**
6361          * @event rowclass
6362          * Fires when a row is rendered, so you can change add a style to it.
6363          * @param {Roo.bootstrap.Table} this
6364          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6365          */
6366         'rowclass' : true,
6367           /**
6368          * @event rowsrendered
6369          * Fires when all the  rows have been rendered
6370          * @param {Roo.bootstrap.Table} this
6371          */
6372         'rowsrendered' : true,
6373         /**
6374          * @event contextmenu
6375          * The raw contextmenu event for the entire grid.
6376          * @param {Roo.EventObject} e
6377          */
6378         "contextmenu" : true,
6379         /**
6380          * @event rowcontextmenu
6381          * Fires when a row is right clicked
6382          * @param {Roo.bootstrap.Table} this
6383          * @param {Number} rowIndex
6384          * @param {Roo.EventObject} e
6385          */
6386         "rowcontextmenu" : true,
6387         /**
6388          * @event cellcontextmenu
6389          * Fires when a cell is right clicked
6390          * @param {Roo.bootstrap.Table} this
6391          * @param {Number} rowIndex
6392          * @param {Number} cellIndex
6393          * @param {Roo.EventObject} e
6394          */
6395          "cellcontextmenu" : true,
6396          /**
6397          * @event headercontextmenu
6398          * Fires when a header is right clicked
6399          * @param {Roo.bootstrap.Table} this
6400          * @param {Number} columnIndex
6401          * @param {Roo.EventObject} e
6402          */
6403         "headercontextmenu" : true
6404     });
6405 };
6406
6407 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6408     
6409     cls: false,
6410     align: false,
6411     bgcolor: false,
6412     border: false,
6413     cellpadding: false,
6414     cellspacing: false,
6415     frame: false,
6416     rules: false,
6417     sortable: false,
6418     summary: false,
6419     width: false,
6420     striped : false,
6421     scrollBody : false,
6422     bordered: false,
6423     hover:  false,
6424     condensed : false,
6425     responsive : false,
6426     sm : false,
6427     cm : false,
6428     store : false,
6429     loadMask : false,
6430     footerShow : true,
6431     headerShow : true,
6432   
6433     rowSelection : false,
6434     cellSelection : false,
6435     layout : false,
6436     
6437     // Roo.Element - the tbody
6438     mainBody: false,
6439     // Roo.Element - thead element
6440     mainHead: false,
6441     
6442     container: false, // used by gridpanel...
6443     
6444     lazyLoad : false,
6445     
6446     CSS : Roo.util.CSS,
6447     
6448     auto_hide_footer : false,
6449     
6450     getAutoCreate : function()
6451     {
6452         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6453         
6454         cfg = {
6455             tag: 'table',
6456             cls : 'table',
6457             cn : []
6458         };
6459         if (this.scrollBody) {
6460             cfg.cls += ' table-body-fixed';
6461         }    
6462         if (this.striped) {
6463             cfg.cls += ' table-striped';
6464         }
6465         
6466         if (this.hover) {
6467             cfg.cls += ' table-hover';
6468         }
6469         if (this.bordered) {
6470             cfg.cls += ' table-bordered';
6471         }
6472         if (this.condensed) {
6473             cfg.cls += ' table-condensed';
6474         }
6475         if (this.responsive) {
6476             cfg.cls += ' table-responsive';
6477         }
6478         
6479         if (this.cls) {
6480             cfg.cls+=  ' ' +this.cls;
6481         }
6482         
6483         // this lot should be simplifed...
6484         var _t = this;
6485         var cp = [
6486             'align',
6487             'bgcolor',
6488             'border',
6489             'cellpadding',
6490             'cellspacing',
6491             'frame',
6492             'rules',
6493             'sortable',
6494             'summary',
6495             'width'
6496         ].forEach(function(k) {
6497             if (_t[k]) {
6498                 cfg[k] = _t[k];
6499             }
6500         });
6501         
6502         
6503         if (this.layout) {
6504             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6505         }
6506         
6507         if(this.store || this.cm){
6508             if(this.headerShow){
6509                 cfg.cn.push(this.renderHeader());
6510             }
6511             
6512             cfg.cn.push(this.renderBody());
6513             
6514             if(this.footerShow){
6515                 cfg.cn.push(this.renderFooter());
6516             }
6517             // where does this come from?
6518             //cfg.cls+=  ' TableGrid';
6519         }
6520         
6521         return { cn : [ cfg ] };
6522     },
6523     
6524     initEvents : function()
6525     {   
6526         if(!this.store || !this.cm){
6527             return;
6528         }
6529         if (this.selModel) {
6530             this.selModel.initEvents();
6531         }
6532         
6533         
6534         //Roo.log('initEvents with ds!!!!');
6535         
6536         this.mainBody = this.el.select('tbody', true).first();
6537         this.mainHead = this.el.select('thead', true).first();
6538         this.mainFoot = this.el.select('tfoot', true).first();
6539         
6540         
6541         
6542         var _this = this;
6543         
6544         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6545             e.on('click', _this.sort, _this);
6546         });
6547         
6548         this.mainBody.on("click", this.onClick, this);
6549         this.mainBody.on("dblclick", this.onDblClick, this);
6550         
6551         // why is this done????? = it breaks dialogs??
6552         //this.parent().el.setStyle('position', 'relative');
6553         
6554         
6555         if (this.footer) {
6556             this.footer.parentId = this.id;
6557             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6558             
6559             if(this.lazyLoad){
6560                 this.el.select('tfoot tr td').first().addClass('hide');
6561             }
6562         } 
6563         
6564         if(this.loadMask) {
6565             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6566         }
6567         
6568         this.store.on('load', this.onLoad, this);
6569         this.store.on('beforeload', this.onBeforeLoad, this);
6570         this.store.on('update', this.onUpdate, this);
6571         this.store.on('add', this.onAdd, this);
6572         this.store.on("clear", this.clear, this);
6573         
6574         this.el.on("contextmenu", this.onContextMenu, this);
6575         
6576         this.mainBody.on('scroll', this.onBodyScroll, this);
6577         
6578         this.cm.on("headerchange", this.onHeaderChange, this);
6579         
6580         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6581         
6582     },
6583     
6584     onContextMenu : function(e, t)
6585     {
6586         this.processEvent("contextmenu", e);
6587     },
6588     
6589     processEvent : function(name, e)
6590     {
6591         if (name != 'touchstart' ) {
6592             this.fireEvent(name, e);    
6593         }
6594         
6595         var t = e.getTarget();
6596         
6597         var cell = Roo.get(t);
6598         
6599         if(!cell){
6600             return;
6601         }
6602         
6603         if(cell.findParent('tfoot', false, true)){
6604             return;
6605         }
6606         
6607         if(cell.findParent('thead', false, true)){
6608             
6609             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6610                 cell = Roo.get(t).findParent('th', false, true);
6611                 if (!cell) {
6612                     Roo.log("failed to find th in thead?");
6613                     Roo.log(e.getTarget());
6614                     return;
6615                 }
6616             }
6617             
6618             var cellIndex = cell.dom.cellIndex;
6619             
6620             var ename = name == 'touchstart' ? 'click' : name;
6621             this.fireEvent("header" + ename, this, cellIndex, e);
6622             
6623             return;
6624         }
6625         
6626         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6627             cell = Roo.get(t).findParent('td', false, true);
6628             if (!cell) {
6629                 Roo.log("failed to find th in tbody?");
6630                 Roo.log(e.getTarget());
6631                 return;
6632             }
6633         }
6634         
6635         var row = cell.findParent('tr', false, true);
6636         var cellIndex = cell.dom.cellIndex;
6637         var rowIndex = row.dom.rowIndex - 1;
6638         
6639         if(row !== false){
6640             
6641             this.fireEvent("row" + name, this, rowIndex, e);
6642             
6643             if(cell !== false){
6644             
6645                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6646             }
6647         }
6648         
6649     },
6650     
6651     onMouseover : function(e, el)
6652     {
6653         var cell = Roo.get(el);
6654         
6655         if(!cell){
6656             return;
6657         }
6658         
6659         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6660             cell = cell.findParent('td', false, true);
6661         }
6662         
6663         var row = cell.findParent('tr', false, true);
6664         var cellIndex = cell.dom.cellIndex;
6665         var rowIndex = row.dom.rowIndex - 1; // start from 0
6666         
6667         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6668         
6669     },
6670     
6671     onMouseout : function(e, el)
6672     {
6673         var cell = Roo.get(el);
6674         
6675         if(!cell){
6676             return;
6677         }
6678         
6679         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6680             cell = cell.findParent('td', false, true);
6681         }
6682         
6683         var row = cell.findParent('tr', false, true);
6684         var cellIndex = cell.dom.cellIndex;
6685         var rowIndex = row.dom.rowIndex - 1; // start from 0
6686         
6687         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6688         
6689     },
6690     
6691     onClick : function(e, el)
6692     {
6693         var cell = Roo.get(el);
6694         
6695         if(!cell || (!this.cellSelection && !this.rowSelection)){
6696             return;
6697         }
6698         
6699         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6700             cell = cell.findParent('td', false, true);
6701         }
6702         
6703         if(!cell || typeof(cell) == 'undefined'){
6704             return;
6705         }
6706         
6707         var row = cell.findParent('tr', false, true);
6708         
6709         if(!row || typeof(row) == 'undefined'){
6710             return;
6711         }
6712         
6713         var cellIndex = cell.dom.cellIndex;
6714         var rowIndex = this.getRowIndex(row);
6715         
6716         // why??? - should these not be based on SelectionModel?
6717         if(this.cellSelection){
6718             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6719         }
6720         
6721         if(this.rowSelection){
6722             this.fireEvent('rowclick', this, row, rowIndex, e);
6723         }
6724         
6725         
6726     },
6727         
6728     onDblClick : function(e,el)
6729     {
6730         var cell = Roo.get(el);
6731         
6732         if(!cell || (!this.cellSelection && !this.rowSelection)){
6733             return;
6734         }
6735         
6736         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6737             cell = cell.findParent('td', false, true);
6738         }
6739         
6740         if(!cell || typeof(cell) == 'undefined'){
6741             return;
6742         }
6743         
6744         var row = cell.findParent('tr', false, true);
6745         
6746         if(!row || typeof(row) == 'undefined'){
6747             return;
6748         }
6749         
6750         var cellIndex = cell.dom.cellIndex;
6751         var rowIndex = this.getRowIndex(row);
6752         
6753         if(this.cellSelection){
6754             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6755         }
6756         
6757         if(this.rowSelection){
6758             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6759         }
6760     },
6761     
6762     sort : function(e,el)
6763     {
6764         var col = Roo.get(el);
6765         
6766         if(!col.hasClass('sortable')){
6767             return;
6768         }
6769         
6770         var sort = col.attr('sort');
6771         var dir = 'ASC';
6772         
6773         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6774             dir = 'DESC';
6775         }
6776         
6777         this.store.sortInfo = {field : sort, direction : dir};
6778         
6779         if (this.footer) {
6780             Roo.log("calling footer first");
6781             this.footer.onClick('first');
6782         } else {
6783         
6784             this.store.load({ params : { start : 0 } });
6785         }
6786     },
6787     
6788     renderHeader : function()
6789     {
6790         var header = {
6791             tag: 'thead',
6792             cn : []
6793         };
6794         
6795         var cm = this.cm;
6796         this.totalWidth = 0;
6797         
6798         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6799             
6800             var config = cm.config[i];
6801             
6802             var c = {
6803                 tag: 'th',
6804                 cls : 'x-hcol-' + i,
6805                 style : '',
6806                 html: cm.getColumnHeader(i)
6807             };
6808             
6809             var hh = '';
6810             
6811             if(typeof(config.sortable) != 'undefined' && config.sortable){
6812                 c.cls = 'sortable';
6813                 c.html = '<i class="glyphicon"></i>' + c.html;
6814             }
6815             
6816             // could use BS4 hidden-..-down 
6817             
6818             if(typeof(config.lgHeader) != 'undefined'){
6819                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
6820             }
6821             
6822             if(typeof(config.mdHeader) != 'undefined'){
6823                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6824             }
6825             
6826             if(typeof(config.smHeader) != 'undefined'){
6827                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6828             }
6829             
6830             if(typeof(config.xsHeader) != 'undefined'){
6831                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6832             }
6833             
6834             if(hh.length){
6835                 c.html = hh;
6836             }
6837             
6838             if(typeof(config.tooltip) != 'undefined'){
6839                 c.tooltip = config.tooltip;
6840             }
6841             
6842             if(typeof(config.colspan) != 'undefined'){
6843                 c.colspan = config.colspan;
6844             }
6845             
6846             if(typeof(config.hidden) != 'undefined' && config.hidden){
6847                 c.style += ' display:none;';
6848             }
6849             
6850             if(typeof(config.dataIndex) != 'undefined'){
6851                 c.sort = config.dataIndex;
6852             }
6853             
6854            
6855             
6856             if(typeof(config.align) != 'undefined' && config.align.length){
6857                 c.style += ' text-align:' + config.align + ';';
6858             }
6859             
6860             if(typeof(config.width) != 'undefined'){
6861                 c.style += ' width:' + config.width + 'px;';
6862                 this.totalWidth += config.width;
6863             } else {
6864                 this.totalWidth += 100; // assume minimum of 100 per column?
6865             }
6866             
6867             if(typeof(config.cls) != 'undefined'){
6868                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6869             }
6870             
6871             ['xs','sm','md','lg'].map(function(size){
6872                 
6873                 if(typeof(config[size]) == 'undefined'){
6874                     return;
6875                 }
6876                  
6877                 if (!config[size]) { // 0 = hidden
6878                     // BS 4 '0' is treated as hide that column and below.
6879                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
6880                     return;
6881                 }
6882                 
6883                 c.cls += ' col-' + size + '-' + config[size] + (
6884                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
6885                 );
6886                 
6887                 
6888             });
6889             
6890             header.cn.push(c)
6891         }
6892         
6893         return header;
6894     },
6895     
6896     renderBody : function()
6897     {
6898         var body = {
6899             tag: 'tbody',
6900             cn : [
6901                 {
6902                     tag: 'tr',
6903                     cn : [
6904                         {
6905                             tag : 'td',
6906                             colspan :  this.cm.getColumnCount()
6907                         }
6908                     ]
6909                 }
6910             ]
6911         };
6912         
6913         return body;
6914     },
6915     
6916     renderFooter : function()
6917     {
6918         var footer = {
6919             tag: 'tfoot',
6920             cn : [
6921                 {
6922                     tag: 'tr',
6923                     cn : [
6924                         {
6925                             tag : 'td',
6926                             colspan :  this.cm.getColumnCount()
6927                         }
6928                     ]
6929                 }
6930             ]
6931         };
6932         
6933         return footer;
6934     },
6935     
6936     
6937     
6938     onLoad : function()
6939     {
6940 //        Roo.log('ds onload');
6941         this.clear();
6942         
6943         var _this = this;
6944         var cm = this.cm;
6945         var ds = this.store;
6946         
6947         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6948             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6949             if (_this.store.sortInfo) {
6950                     
6951                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6952                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6953                 }
6954                 
6955                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6956                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6957                 }
6958             }
6959         });
6960         
6961         var tbody =  this.mainBody;
6962               
6963         if(ds.getCount() > 0){
6964             ds.data.each(function(d,rowIndex){
6965                 var row =  this.renderRow(cm, ds, rowIndex);
6966                 
6967                 tbody.createChild(row);
6968                 
6969                 var _this = this;
6970                 
6971                 if(row.cellObjects.length){
6972                     Roo.each(row.cellObjects, function(r){
6973                         _this.renderCellObject(r);
6974                     })
6975                 }
6976                 
6977             }, this);
6978         }
6979         
6980         var tfoot = this.el.select('tfoot', true).first();
6981         
6982         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6983             
6984             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6985             
6986             var total = this.ds.getTotalCount();
6987             
6988             if(this.footer.pageSize < total){
6989                 this.mainFoot.show();
6990             }
6991         }
6992         
6993         Roo.each(this.el.select('tbody td', true).elements, function(e){
6994             e.on('mouseover', _this.onMouseover, _this);
6995         });
6996         
6997         Roo.each(this.el.select('tbody td', true).elements, function(e){
6998             e.on('mouseout', _this.onMouseout, _this);
6999         });
7000         this.fireEvent('rowsrendered', this);
7001         
7002         this.autoSize();
7003     },
7004     
7005     
7006     onUpdate : function(ds,record)
7007     {
7008         this.refreshRow(record);
7009         this.autoSize();
7010     },
7011     
7012     onRemove : function(ds, record, index, isUpdate){
7013         if(isUpdate !== true){
7014             this.fireEvent("beforerowremoved", this, index, record);
7015         }
7016         var bt = this.mainBody.dom;
7017         
7018         var rows = this.el.select('tbody > tr', true).elements;
7019         
7020         if(typeof(rows[index]) != 'undefined'){
7021             bt.removeChild(rows[index].dom);
7022         }
7023         
7024 //        if(bt.rows[index]){
7025 //            bt.removeChild(bt.rows[index]);
7026 //        }
7027         
7028         if(isUpdate !== true){
7029             //this.stripeRows(index);
7030             //this.syncRowHeights(index, index);
7031             //this.layout();
7032             this.fireEvent("rowremoved", this, index, record);
7033         }
7034     },
7035     
7036     onAdd : function(ds, records, rowIndex)
7037     {
7038         //Roo.log('on Add called');
7039         // - note this does not handle multiple adding very well..
7040         var bt = this.mainBody.dom;
7041         for (var i =0 ; i < records.length;i++) {
7042             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
7043             //Roo.log(records[i]);
7044             //Roo.log(this.store.getAt(rowIndex+i));
7045             this.insertRow(this.store, rowIndex + i, false);
7046             return;
7047         }
7048         
7049     },
7050     
7051     
7052     refreshRow : function(record){
7053         var ds = this.store, index;
7054         if(typeof record == 'number'){
7055             index = record;
7056             record = ds.getAt(index);
7057         }else{
7058             index = ds.indexOf(record);
7059         }
7060         this.insertRow(ds, index, true);
7061         this.autoSize();
7062         this.onRemove(ds, record, index+1, true);
7063         this.autoSize();
7064         //this.syncRowHeights(index, index);
7065         //this.layout();
7066         this.fireEvent("rowupdated", this, index, record);
7067     },
7068     
7069     insertRow : function(dm, rowIndex, isUpdate){
7070         
7071         if(!isUpdate){
7072             this.fireEvent("beforerowsinserted", this, rowIndex);
7073         }
7074             //var s = this.getScrollState();
7075         var row = this.renderRow(this.cm, this.store, rowIndex);
7076         // insert before rowIndex..
7077         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
7078         
7079         var _this = this;
7080                 
7081         if(row.cellObjects.length){
7082             Roo.each(row.cellObjects, function(r){
7083                 _this.renderCellObject(r);
7084             })
7085         }
7086             
7087         if(!isUpdate){
7088             this.fireEvent("rowsinserted", this, rowIndex);
7089             //this.syncRowHeights(firstRow, lastRow);
7090             //this.stripeRows(firstRow);
7091             //this.layout();
7092         }
7093         
7094     },
7095     
7096     
7097     getRowDom : function(rowIndex)
7098     {
7099         var rows = this.el.select('tbody > tr', true).elements;
7100         
7101         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
7102         
7103     },
7104     // returns the object tree for a tr..
7105   
7106     
7107     renderRow : function(cm, ds, rowIndex) 
7108     {
7109         var d = ds.getAt(rowIndex);
7110         
7111         var row = {
7112             tag : 'tr',
7113             cls : 'x-row-' + rowIndex,
7114             cn : []
7115         };
7116             
7117         var cellObjects = [];
7118         
7119         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7120             var config = cm.config[i];
7121             
7122             var renderer = cm.getRenderer(i);
7123             var value = '';
7124             var id = false;
7125             
7126             if(typeof(renderer) !== 'undefined'){
7127                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
7128             }
7129             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
7130             // and are rendered into the cells after the row is rendered - using the id for the element.
7131             
7132             if(typeof(value) === 'object'){
7133                 id = Roo.id();
7134                 cellObjects.push({
7135                     container : id,
7136                     cfg : value 
7137                 })
7138             }
7139             
7140             var rowcfg = {
7141                 record: d,
7142                 rowIndex : rowIndex,
7143                 colIndex : i,
7144                 rowClass : ''
7145             };
7146
7147             this.fireEvent('rowclass', this, rowcfg);
7148             
7149             var td = {
7150                 tag: 'td',
7151                 cls : rowcfg.rowClass + ' x-col-' + i,
7152                 style: '',
7153                 html: (typeof(value) === 'object') ? '' : value
7154             };
7155             
7156             if (id) {
7157                 td.id = id;
7158             }
7159             
7160             if(typeof(config.colspan) != 'undefined'){
7161                 td.colspan = config.colspan;
7162             }
7163             
7164             if(typeof(config.hidden) != 'undefined' && config.hidden){
7165                 td.style += ' display:none;';
7166             }
7167             
7168             if(typeof(config.align) != 'undefined' && config.align.length){
7169                 td.style += ' text-align:' + config.align + ';';
7170             }
7171             if(typeof(config.valign) != 'undefined' && config.valign.length){
7172                 td.style += ' vertical-align:' + config.valign + ';';
7173             }
7174             
7175             if(typeof(config.width) != 'undefined'){
7176                 td.style += ' width:' +  config.width + 'px;';
7177             }
7178             
7179             if(typeof(config.cursor) != 'undefined'){
7180                 td.style += ' cursor:' +  config.cursor + ';';
7181             }
7182             
7183             if(typeof(config.cls) != 'undefined'){
7184                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7185             }
7186             
7187             ['xs','sm','md','lg'].map(function(size){
7188                 
7189                 if(typeof(config[size]) == 'undefined'){
7190                     return;
7191                 }
7192                 
7193                 
7194                   
7195                 if (!config[size]) { // 0 = hidden
7196                     // BS 4 '0' is treated as hide that column and below.
7197                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
7198                     return;
7199                 }
7200                 
7201                 td.cls += ' col-' + size + '-' + config[size] + (
7202                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
7203                 );
7204                  
7205
7206             });
7207             
7208             row.cn.push(td);
7209            
7210         }
7211         
7212         row.cellObjects = cellObjects;
7213         
7214         return row;
7215           
7216     },
7217     
7218     
7219     
7220     onBeforeLoad : function()
7221     {
7222         
7223     },
7224      /**
7225      * Remove all rows
7226      */
7227     clear : function()
7228     {
7229         this.el.select('tbody', true).first().dom.innerHTML = '';
7230     },
7231     /**
7232      * Show or hide a row.
7233      * @param {Number} rowIndex to show or hide
7234      * @param {Boolean} state hide
7235      */
7236     setRowVisibility : function(rowIndex, state)
7237     {
7238         var bt = this.mainBody.dom;
7239         
7240         var rows = this.el.select('tbody > tr', true).elements;
7241         
7242         if(typeof(rows[rowIndex]) == 'undefined'){
7243             return;
7244         }
7245         rows[rowIndex].dom.style.display = state ? '' : 'none';
7246     },
7247     
7248     
7249     getSelectionModel : function(){
7250         if(!this.selModel){
7251             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7252         }
7253         return this.selModel;
7254     },
7255     /*
7256      * Render the Roo.bootstrap object from renderder
7257      */
7258     renderCellObject : function(r)
7259     {
7260         var _this = this;
7261         
7262         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7263         
7264         var t = r.cfg.render(r.container);
7265         
7266         if(r.cfg.cn){
7267             Roo.each(r.cfg.cn, function(c){
7268                 var child = {
7269                     container: t.getChildContainer(),
7270                     cfg: c
7271                 };
7272                 _this.renderCellObject(child);
7273             })
7274         }
7275     },
7276     
7277     getRowIndex : function(row)
7278     {
7279         var rowIndex = -1;
7280         
7281         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7282             if(el != row){
7283                 return;
7284             }
7285             
7286             rowIndex = index;
7287         });
7288         
7289         return rowIndex;
7290     },
7291      /**
7292      * Returns the grid's underlying element = used by panel.Grid
7293      * @return {Element} The element
7294      */
7295     getGridEl : function(){
7296         return this.el;
7297     },
7298      /**
7299      * Forces a resize - used by panel.Grid
7300      * @return {Element} The element
7301      */
7302     autoSize : function()
7303     {
7304         //var ctr = Roo.get(this.container.dom.parentElement);
7305         var ctr = Roo.get(this.el.dom);
7306         
7307         var thd = this.getGridEl().select('thead',true).first();
7308         var tbd = this.getGridEl().select('tbody', true).first();
7309         var tfd = this.getGridEl().select('tfoot', true).first();
7310         
7311         var cw = ctr.getWidth();
7312         
7313         if (tbd) {
7314             
7315             tbd.setSize(ctr.getWidth(),
7316                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7317             );
7318             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7319             cw -= barsize;
7320         }
7321         cw = Math.max(cw, this.totalWidth);
7322         this.getGridEl().select('tr',true).setWidth(cw);
7323         // resize 'expandable coloumn?
7324         
7325         return; // we doe not have a view in this design..
7326         
7327     },
7328     onBodyScroll: function()
7329     {
7330         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7331         if(this.mainHead){
7332             this.mainHead.setStyle({
7333                 'position' : 'relative',
7334                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7335             });
7336         }
7337         
7338         if(this.lazyLoad){
7339             
7340             var scrollHeight = this.mainBody.dom.scrollHeight;
7341             
7342             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7343             
7344             var height = this.mainBody.getHeight();
7345             
7346             if(scrollHeight - height == scrollTop) {
7347                 
7348                 var total = this.ds.getTotalCount();
7349                 
7350                 if(this.footer.cursor + this.footer.pageSize < total){
7351                     
7352                     this.footer.ds.load({
7353                         params : {
7354                             start : this.footer.cursor + this.footer.pageSize,
7355                             limit : this.footer.pageSize
7356                         },
7357                         add : true
7358                     });
7359                 }
7360             }
7361             
7362         }
7363     },
7364     
7365     onHeaderChange : function()
7366     {
7367         var header = this.renderHeader();
7368         var table = this.el.select('table', true).first();
7369         
7370         this.mainHead.remove();
7371         this.mainHead = table.createChild(header, this.mainBody, false);
7372     },
7373     
7374     onHiddenChange : function(colModel, colIndex, hidden)
7375     {
7376         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7377         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7378         
7379         this.CSS.updateRule(thSelector, "display", "");
7380         this.CSS.updateRule(tdSelector, "display", "");
7381         
7382         if(hidden){
7383             this.CSS.updateRule(thSelector, "display", "none");
7384             this.CSS.updateRule(tdSelector, "display", "none");
7385         }
7386         
7387         this.onHeaderChange();
7388         this.onLoad();
7389     },
7390     
7391     setColumnWidth: function(col_index, width)
7392     {
7393         // width = "md-2 xs-2..."
7394         if(!this.colModel.config[col_index]) {
7395             return;
7396         }
7397         
7398         var w = width.split(" ");
7399         
7400         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7401         
7402         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7403         
7404         
7405         for(var j = 0; j < w.length; j++) {
7406             
7407             if(!w[j]) {
7408                 continue;
7409             }
7410             
7411             var size_cls = w[j].split("-");
7412             
7413             if(!Number.isInteger(size_cls[1] * 1)) {
7414                 continue;
7415             }
7416             
7417             if(!this.colModel.config[col_index][size_cls[0]]) {
7418                 continue;
7419             }
7420             
7421             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7422                 continue;
7423             }
7424             
7425             h_row[0].classList.replace(
7426                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7427                 "col-"+size_cls[0]+"-"+size_cls[1]
7428             );
7429             
7430             for(var i = 0; i < rows.length; i++) {
7431                 
7432                 var size_cls = w[j].split("-");
7433                 
7434                 if(!Number.isInteger(size_cls[1] * 1)) {
7435                     continue;
7436                 }
7437                 
7438                 if(!this.colModel.config[col_index][size_cls[0]]) {
7439                     continue;
7440                 }
7441                 
7442                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7443                     continue;
7444                 }
7445                 
7446                 rows[i].classList.replace(
7447                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7448                     "col-"+size_cls[0]+"-"+size_cls[1]
7449                 );
7450             }
7451             
7452             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7453         }
7454     }
7455 });
7456
7457  
7458
7459  /*
7460  * - LGPL
7461  *
7462  * table cell
7463  * 
7464  */
7465
7466 /**
7467  * @class Roo.bootstrap.TableCell
7468  * @extends Roo.bootstrap.Component
7469  * Bootstrap TableCell class
7470  * @cfg {String} html cell contain text
7471  * @cfg {String} cls cell class
7472  * @cfg {String} tag cell tag (td|th) default td
7473  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7474  * @cfg {String} align Aligns the content in a cell
7475  * @cfg {String} axis Categorizes cells
7476  * @cfg {String} bgcolor Specifies the background color of a cell
7477  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7478  * @cfg {Number} colspan Specifies the number of columns a cell should span
7479  * @cfg {String} headers Specifies one or more header cells a cell is related to
7480  * @cfg {Number} height Sets the height of a cell
7481  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7482  * @cfg {Number} rowspan Sets the number of rows a cell should span
7483  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7484  * @cfg {String} valign Vertical aligns the content in a cell
7485  * @cfg {Number} width Specifies the width of a cell
7486  * 
7487  * @constructor
7488  * Create a new TableCell
7489  * @param {Object} config The config object
7490  */
7491
7492 Roo.bootstrap.TableCell = function(config){
7493     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7494 };
7495
7496 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7497     
7498     html: false,
7499     cls: false,
7500     tag: false,
7501     abbr: false,
7502     align: false,
7503     axis: false,
7504     bgcolor: false,
7505     charoff: false,
7506     colspan: false,
7507     headers: false,
7508     height: false,
7509     nowrap: false,
7510     rowspan: false,
7511     scope: false,
7512     valign: false,
7513     width: false,
7514     
7515     
7516     getAutoCreate : function(){
7517         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7518         
7519         cfg = {
7520             tag: 'td'
7521         };
7522         
7523         if(this.tag){
7524             cfg.tag = this.tag;
7525         }
7526         
7527         if (this.html) {
7528             cfg.html=this.html
7529         }
7530         if (this.cls) {
7531             cfg.cls=this.cls
7532         }
7533         if (this.abbr) {
7534             cfg.abbr=this.abbr
7535         }
7536         if (this.align) {
7537             cfg.align=this.align
7538         }
7539         if (this.axis) {
7540             cfg.axis=this.axis
7541         }
7542         if (this.bgcolor) {
7543             cfg.bgcolor=this.bgcolor
7544         }
7545         if (this.charoff) {
7546             cfg.charoff=this.charoff
7547         }
7548         if (this.colspan) {
7549             cfg.colspan=this.colspan
7550         }
7551         if (this.headers) {
7552             cfg.headers=this.headers
7553         }
7554         if (this.height) {
7555             cfg.height=this.height
7556         }
7557         if (this.nowrap) {
7558             cfg.nowrap=this.nowrap
7559         }
7560         if (this.rowspan) {
7561             cfg.rowspan=this.rowspan
7562         }
7563         if (this.scope) {
7564             cfg.scope=this.scope
7565         }
7566         if (this.valign) {
7567             cfg.valign=this.valign
7568         }
7569         if (this.width) {
7570             cfg.width=this.width
7571         }
7572         
7573         
7574         return cfg;
7575     }
7576    
7577 });
7578
7579  
7580
7581  /*
7582  * - LGPL
7583  *
7584  * table row
7585  * 
7586  */
7587
7588 /**
7589  * @class Roo.bootstrap.TableRow
7590  * @extends Roo.bootstrap.Component
7591  * Bootstrap TableRow class
7592  * @cfg {String} cls row class
7593  * @cfg {String} align Aligns the content in a table row
7594  * @cfg {String} bgcolor Specifies a background color for a table row
7595  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7596  * @cfg {String} valign Vertical aligns the content in a table row
7597  * 
7598  * @constructor
7599  * Create a new TableRow
7600  * @param {Object} config The config object
7601  */
7602
7603 Roo.bootstrap.TableRow = function(config){
7604     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7605 };
7606
7607 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7608     
7609     cls: false,
7610     align: false,
7611     bgcolor: false,
7612     charoff: false,
7613     valign: false,
7614     
7615     getAutoCreate : function(){
7616         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7617         
7618         cfg = {
7619             tag: 'tr'
7620         };
7621             
7622         if(this.cls){
7623             cfg.cls = this.cls;
7624         }
7625         if(this.align){
7626             cfg.align = this.align;
7627         }
7628         if(this.bgcolor){
7629             cfg.bgcolor = this.bgcolor;
7630         }
7631         if(this.charoff){
7632             cfg.charoff = this.charoff;
7633         }
7634         if(this.valign){
7635             cfg.valign = this.valign;
7636         }
7637         
7638         return cfg;
7639     }
7640    
7641 });
7642
7643  
7644
7645  /*
7646  * - LGPL
7647  *
7648  * table body
7649  * 
7650  */
7651
7652 /**
7653  * @class Roo.bootstrap.TableBody
7654  * @extends Roo.bootstrap.Component
7655  * Bootstrap TableBody class
7656  * @cfg {String} cls element class
7657  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7658  * @cfg {String} align Aligns the content inside the element
7659  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7660  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7661  * 
7662  * @constructor
7663  * Create a new TableBody
7664  * @param {Object} config The config object
7665  */
7666
7667 Roo.bootstrap.TableBody = function(config){
7668     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7669 };
7670
7671 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7672     
7673     cls: false,
7674     tag: false,
7675     align: false,
7676     charoff: false,
7677     valign: false,
7678     
7679     getAutoCreate : function(){
7680         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7681         
7682         cfg = {
7683             tag: 'tbody'
7684         };
7685             
7686         if (this.cls) {
7687             cfg.cls=this.cls
7688         }
7689         if(this.tag){
7690             cfg.tag = this.tag;
7691         }
7692         
7693         if(this.align){
7694             cfg.align = this.align;
7695         }
7696         if(this.charoff){
7697             cfg.charoff = this.charoff;
7698         }
7699         if(this.valign){
7700             cfg.valign = this.valign;
7701         }
7702         
7703         return cfg;
7704     }
7705     
7706     
7707 //    initEvents : function()
7708 //    {
7709 //        
7710 //        if(!this.store){
7711 //            return;
7712 //        }
7713 //        
7714 //        this.store = Roo.factory(this.store, Roo.data);
7715 //        this.store.on('load', this.onLoad, this);
7716 //        
7717 //        this.store.load();
7718 //        
7719 //    },
7720 //    
7721 //    onLoad: function () 
7722 //    {   
7723 //        this.fireEvent('load', this);
7724 //    }
7725 //    
7726 //   
7727 });
7728
7729  
7730
7731  /*
7732  * Based on:
7733  * Ext JS Library 1.1.1
7734  * Copyright(c) 2006-2007, Ext JS, LLC.
7735  *
7736  * Originally Released Under LGPL - original licence link has changed is not relivant.
7737  *
7738  * Fork - LGPL
7739  * <script type="text/javascript">
7740  */
7741
7742 // as we use this in bootstrap.
7743 Roo.namespace('Roo.form');
7744  /**
7745  * @class Roo.form.Action
7746  * Internal Class used to handle form actions
7747  * @constructor
7748  * @param {Roo.form.BasicForm} el The form element or its id
7749  * @param {Object} config Configuration options
7750  */
7751
7752  
7753  
7754 // define the action interface
7755 Roo.form.Action = function(form, options){
7756     this.form = form;
7757     this.options = options || {};
7758 };
7759 /**
7760  * Client Validation Failed
7761  * @const 
7762  */
7763 Roo.form.Action.CLIENT_INVALID = 'client';
7764 /**
7765  * Server Validation Failed
7766  * @const 
7767  */
7768 Roo.form.Action.SERVER_INVALID = 'server';
7769  /**
7770  * Connect to Server Failed
7771  * @const 
7772  */
7773 Roo.form.Action.CONNECT_FAILURE = 'connect';
7774 /**
7775  * Reading Data from Server Failed
7776  * @const 
7777  */
7778 Roo.form.Action.LOAD_FAILURE = 'load';
7779
7780 Roo.form.Action.prototype = {
7781     type : 'default',
7782     failureType : undefined,
7783     response : undefined,
7784     result : undefined,
7785
7786     // interface method
7787     run : function(options){
7788
7789     },
7790
7791     // interface method
7792     success : function(response){
7793
7794     },
7795
7796     // interface method
7797     handleResponse : function(response){
7798
7799     },
7800
7801     // default connection failure
7802     failure : function(response){
7803         
7804         this.response = response;
7805         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7806         this.form.afterAction(this, false);
7807     },
7808
7809     processResponse : function(response){
7810         this.response = response;
7811         if(!response.responseText){
7812             return true;
7813         }
7814         this.result = this.handleResponse(response);
7815         return this.result;
7816     },
7817
7818     // utility functions used internally
7819     getUrl : function(appendParams){
7820         var url = this.options.url || this.form.url || this.form.el.dom.action;
7821         if(appendParams){
7822             var p = this.getParams();
7823             if(p){
7824                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7825             }
7826         }
7827         return url;
7828     },
7829
7830     getMethod : function(){
7831         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7832     },
7833
7834     getParams : function(){
7835         var bp = this.form.baseParams;
7836         var p = this.options.params;
7837         if(p){
7838             if(typeof p == "object"){
7839                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7840             }else if(typeof p == 'string' && bp){
7841                 p += '&' + Roo.urlEncode(bp);
7842             }
7843         }else if(bp){
7844             p = Roo.urlEncode(bp);
7845         }
7846         return p;
7847     },
7848
7849     createCallback : function(){
7850         return {
7851             success: this.success,
7852             failure: this.failure,
7853             scope: this,
7854             timeout: (this.form.timeout*1000),
7855             upload: this.form.fileUpload ? this.success : undefined
7856         };
7857     }
7858 };
7859
7860 Roo.form.Action.Submit = function(form, options){
7861     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7862 };
7863
7864 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7865     type : 'submit',
7866
7867     haveProgress : false,
7868     uploadComplete : false,
7869     
7870     // uploadProgress indicator.
7871     uploadProgress : function()
7872     {
7873         if (!this.form.progressUrl) {
7874             return;
7875         }
7876         
7877         if (!this.haveProgress) {
7878             Roo.MessageBox.progress("Uploading", "Uploading");
7879         }
7880         if (this.uploadComplete) {
7881            Roo.MessageBox.hide();
7882            return;
7883         }
7884         
7885         this.haveProgress = true;
7886    
7887         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7888         
7889         var c = new Roo.data.Connection();
7890         c.request({
7891             url : this.form.progressUrl,
7892             params: {
7893                 id : uid
7894             },
7895             method: 'GET',
7896             success : function(req){
7897                //console.log(data);
7898                 var rdata = false;
7899                 var edata;
7900                 try  {
7901                    rdata = Roo.decode(req.responseText)
7902                 } catch (e) {
7903                     Roo.log("Invalid data from server..");
7904                     Roo.log(edata);
7905                     return;
7906                 }
7907                 if (!rdata || !rdata.success) {
7908                     Roo.log(rdata);
7909                     Roo.MessageBox.alert(Roo.encode(rdata));
7910                     return;
7911                 }
7912                 var data = rdata.data;
7913                 
7914                 if (this.uploadComplete) {
7915                    Roo.MessageBox.hide();
7916                    return;
7917                 }
7918                    
7919                 if (data){
7920                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7921                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7922                     );
7923                 }
7924                 this.uploadProgress.defer(2000,this);
7925             },
7926        
7927             failure: function(data) {
7928                 Roo.log('progress url failed ');
7929                 Roo.log(data);
7930             },
7931             scope : this
7932         });
7933            
7934     },
7935     
7936     
7937     run : function()
7938     {
7939         // run get Values on the form, so it syncs any secondary forms.
7940         this.form.getValues();
7941         
7942         var o = this.options;
7943         var method = this.getMethod();
7944         var isPost = method == 'POST';
7945         if(o.clientValidation === false || this.form.isValid()){
7946             
7947             if (this.form.progressUrl) {
7948                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7949                     (new Date() * 1) + '' + Math.random());
7950                     
7951             } 
7952             
7953             
7954             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7955                 form:this.form.el.dom,
7956                 url:this.getUrl(!isPost),
7957                 method: method,
7958                 params:isPost ? this.getParams() : null,
7959                 isUpload: this.form.fileUpload,
7960                 formData : this.form.formData
7961             }));
7962             
7963             this.uploadProgress();
7964
7965         }else if (o.clientValidation !== false){ // client validation failed
7966             this.failureType = Roo.form.Action.CLIENT_INVALID;
7967             this.form.afterAction(this, false);
7968         }
7969     },
7970
7971     success : function(response)
7972     {
7973         this.uploadComplete= true;
7974         if (this.haveProgress) {
7975             Roo.MessageBox.hide();
7976         }
7977         
7978         
7979         var result = this.processResponse(response);
7980         if(result === true || result.success){
7981             this.form.afterAction(this, true);
7982             return;
7983         }
7984         if(result.errors){
7985             this.form.markInvalid(result.errors);
7986             this.failureType = Roo.form.Action.SERVER_INVALID;
7987         }
7988         this.form.afterAction(this, false);
7989     },
7990     failure : function(response)
7991     {
7992         this.uploadComplete= true;
7993         if (this.haveProgress) {
7994             Roo.MessageBox.hide();
7995         }
7996         
7997         this.response = response;
7998         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7999         this.form.afterAction(this, false);
8000     },
8001     
8002     handleResponse : function(response){
8003         if(this.form.errorReader){
8004             var rs = this.form.errorReader.read(response);
8005             var errors = [];
8006             if(rs.records){
8007                 for(var i = 0, len = rs.records.length; i < len; i++) {
8008                     var r = rs.records[i];
8009                     errors[i] = r.data;
8010                 }
8011             }
8012             if(errors.length < 1){
8013                 errors = null;
8014             }
8015             return {
8016                 success : rs.success,
8017                 errors : errors
8018             };
8019         }
8020         var ret = false;
8021         try {
8022             ret = Roo.decode(response.responseText);
8023         } catch (e) {
8024             ret = {
8025                 success: false,
8026                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
8027                 errors : []
8028             };
8029         }
8030         return ret;
8031         
8032     }
8033 });
8034
8035
8036 Roo.form.Action.Load = function(form, options){
8037     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
8038     this.reader = this.form.reader;
8039 };
8040
8041 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
8042     type : 'load',
8043
8044     run : function(){
8045         
8046         Roo.Ajax.request(Roo.apply(
8047                 this.createCallback(), {
8048                     method:this.getMethod(),
8049                     url:this.getUrl(false),
8050                     params:this.getParams()
8051         }));
8052     },
8053
8054     success : function(response){
8055         
8056         var result = this.processResponse(response);
8057         if(result === true || !result.success || !result.data){
8058             this.failureType = Roo.form.Action.LOAD_FAILURE;
8059             this.form.afterAction(this, false);
8060             return;
8061         }
8062         this.form.clearInvalid();
8063         this.form.setValues(result.data);
8064         this.form.afterAction(this, true);
8065     },
8066
8067     handleResponse : function(response){
8068         if(this.form.reader){
8069             var rs = this.form.reader.read(response);
8070             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
8071             return {
8072                 success : rs.success,
8073                 data : data
8074             };
8075         }
8076         return Roo.decode(response.responseText);
8077     }
8078 });
8079
8080 Roo.form.Action.ACTION_TYPES = {
8081     'load' : Roo.form.Action.Load,
8082     'submit' : Roo.form.Action.Submit
8083 };/*
8084  * - LGPL
8085  *
8086  * form
8087  *
8088  */
8089
8090 /**
8091  * @class Roo.bootstrap.Form
8092  * @extends Roo.bootstrap.Component
8093  * Bootstrap Form class
8094  * @cfg {String} method  GET | POST (default POST)
8095  * @cfg {String} labelAlign top | left (default top)
8096  * @cfg {String} align left  | right - for navbars
8097  * @cfg {Boolean} loadMask load mask when submit (default true)
8098
8099  *
8100  * @constructor
8101  * Create a new Form
8102  * @param {Object} config The config object
8103  */
8104
8105
8106 Roo.bootstrap.Form = function(config){
8107     
8108     Roo.bootstrap.Form.superclass.constructor.call(this, config);
8109     
8110     Roo.bootstrap.Form.popover.apply();
8111     
8112     this.addEvents({
8113         /**
8114          * @event clientvalidation
8115          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
8116          * @param {Form} this
8117          * @param {Boolean} valid true if the form has passed client-side validation
8118          */
8119         clientvalidation: true,
8120         /**
8121          * @event beforeaction
8122          * Fires before any action is performed. Return false to cancel the action.
8123          * @param {Form} this
8124          * @param {Action} action The action to be performed
8125          */
8126         beforeaction: true,
8127         /**
8128          * @event actionfailed
8129          * Fires when an action fails.
8130          * @param {Form} this
8131          * @param {Action} action The action that failed
8132          */
8133         actionfailed : true,
8134         /**
8135          * @event actioncomplete
8136          * Fires when an action is completed.
8137          * @param {Form} this
8138          * @param {Action} action The action that completed
8139          */
8140         actioncomplete : true
8141     });
8142 };
8143
8144 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
8145
8146      /**
8147      * @cfg {String} method
8148      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8149      */
8150     method : 'POST',
8151     /**
8152      * @cfg {String} url
8153      * The URL to use for form actions if one isn't supplied in the action options.
8154      */
8155     /**
8156      * @cfg {Boolean} fileUpload
8157      * Set to true if this form is a file upload.
8158      */
8159
8160     /**
8161      * @cfg {Object} baseParams
8162      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8163      */
8164
8165     /**
8166      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8167      */
8168     timeout: 30,
8169     /**
8170      * @cfg {Sting} align (left|right) for navbar forms
8171      */
8172     align : 'left',
8173
8174     // private
8175     activeAction : null,
8176
8177     /**
8178      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8179      * element by passing it or its id or mask the form itself by passing in true.
8180      * @type Mixed
8181      */
8182     waitMsgTarget : false,
8183
8184     loadMask : true,
8185     
8186     /**
8187      * @cfg {Boolean} errorMask (true|false) default false
8188      */
8189     errorMask : false,
8190     
8191     /**
8192      * @cfg {Number} maskOffset Default 100
8193      */
8194     maskOffset : 100,
8195     
8196     /**
8197      * @cfg {Boolean} maskBody
8198      */
8199     maskBody : false,
8200
8201     getAutoCreate : function(){
8202
8203         var cfg = {
8204             tag: 'form',
8205             method : this.method || 'POST',
8206             id : this.id || Roo.id(),
8207             cls : ''
8208         };
8209         if (this.parent().xtype.match(/^Nav/)) {
8210             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8211
8212         }
8213
8214         if (this.labelAlign == 'left' ) {
8215             cfg.cls += ' form-horizontal';
8216         }
8217
8218
8219         return cfg;
8220     },
8221     initEvents : function()
8222     {
8223         this.el.on('submit', this.onSubmit, this);
8224         // this was added as random key presses on the form where triggering form submit.
8225         this.el.on('keypress', function(e) {
8226             if (e.getCharCode() != 13) {
8227                 return true;
8228             }
8229             // we might need to allow it for textareas.. and some other items.
8230             // check e.getTarget().
8231
8232             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8233                 return true;
8234             }
8235
8236             Roo.log("keypress blocked");
8237
8238             e.preventDefault();
8239             return false;
8240         });
8241         
8242     },
8243     // private
8244     onSubmit : function(e){
8245         e.stopEvent();
8246     },
8247
8248      /**
8249      * Returns true if client-side validation on the form is successful.
8250      * @return Boolean
8251      */
8252     isValid : function(){
8253         var items = this.getItems();
8254         var valid = true;
8255         var target = false;
8256         
8257         items.each(function(f){
8258             
8259             if(f.validate()){
8260                 return;
8261             }
8262             
8263             Roo.log('invalid field: ' + f.name);
8264             
8265             valid = false;
8266
8267             if(!target && f.el.isVisible(true)){
8268                 target = f;
8269             }
8270            
8271         });
8272         
8273         if(this.errorMask && !valid){
8274             Roo.bootstrap.Form.popover.mask(this, target);
8275         }
8276         
8277         return valid;
8278     },
8279     
8280     /**
8281      * Returns true if any fields in this form have changed since their original load.
8282      * @return Boolean
8283      */
8284     isDirty : function(){
8285         var dirty = false;
8286         var items = this.getItems();
8287         items.each(function(f){
8288            if(f.isDirty()){
8289                dirty = true;
8290                return false;
8291            }
8292            return true;
8293         });
8294         return dirty;
8295     },
8296      /**
8297      * Performs a predefined action (submit or load) or custom actions you define on this form.
8298      * @param {String} actionName The name of the action type
8299      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8300      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8301      * accept other config options):
8302      * <pre>
8303 Property          Type             Description
8304 ----------------  ---------------  ----------------------------------------------------------------------------------
8305 url               String           The url for the action (defaults to the form's url)
8306 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8307 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8308 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8309                                    validate the form on the client (defaults to false)
8310      * </pre>
8311      * @return {BasicForm} this
8312      */
8313     doAction : function(action, options){
8314         if(typeof action == 'string'){
8315             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8316         }
8317         if(this.fireEvent('beforeaction', this, action) !== false){
8318             this.beforeAction(action);
8319             action.run.defer(100, action);
8320         }
8321         return this;
8322     },
8323
8324     // private
8325     beforeAction : function(action){
8326         var o = action.options;
8327         
8328         if(this.loadMask){
8329             
8330             if(this.maskBody){
8331                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8332             } else {
8333                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8334             }
8335         }
8336         // not really supported yet.. ??
8337
8338         //if(this.waitMsgTarget === true){
8339         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8340         //}else if(this.waitMsgTarget){
8341         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8342         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8343         //}else {
8344         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8345        // }
8346
8347     },
8348
8349     // private
8350     afterAction : function(action, success){
8351         this.activeAction = null;
8352         var o = action.options;
8353
8354         if(this.loadMask){
8355             
8356             if(this.maskBody){
8357                 Roo.get(document.body).unmask();
8358             } else {
8359                 this.el.unmask();
8360             }
8361         }
8362         
8363         //if(this.waitMsgTarget === true){
8364 //            this.el.unmask();
8365         //}else if(this.waitMsgTarget){
8366         //    this.waitMsgTarget.unmask();
8367         //}else{
8368         //    Roo.MessageBox.updateProgress(1);
8369         //    Roo.MessageBox.hide();
8370        // }
8371         //
8372         if(success){
8373             if(o.reset){
8374                 this.reset();
8375             }
8376             Roo.callback(o.success, o.scope, [this, action]);
8377             this.fireEvent('actioncomplete', this, action);
8378
8379         }else{
8380
8381             // failure condition..
8382             // we have a scenario where updates need confirming.
8383             // eg. if a locking scenario exists..
8384             // we look for { errors : { needs_confirm : true }} in the response.
8385             if (
8386                 (typeof(action.result) != 'undefined')  &&
8387                 (typeof(action.result.errors) != 'undefined')  &&
8388                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8389            ){
8390                 var _t = this;
8391                 Roo.log("not supported yet");
8392                  /*
8393
8394                 Roo.MessageBox.confirm(
8395                     "Change requires confirmation",
8396                     action.result.errorMsg,
8397                     function(r) {
8398                         if (r != 'yes') {
8399                             return;
8400                         }
8401                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8402                     }
8403
8404                 );
8405                 */
8406
8407
8408                 return;
8409             }
8410
8411             Roo.callback(o.failure, o.scope, [this, action]);
8412             // show an error message if no failed handler is set..
8413             if (!this.hasListener('actionfailed')) {
8414                 Roo.log("need to add dialog support");
8415                 /*
8416                 Roo.MessageBox.alert("Error",
8417                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8418                         action.result.errorMsg :
8419                         "Saving Failed, please check your entries or try again"
8420                 );
8421                 */
8422             }
8423
8424             this.fireEvent('actionfailed', this, action);
8425         }
8426
8427     },
8428     /**
8429      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8430      * @param {String} id The value to search for
8431      * @return Field
8432      */
8433     findField : function(id){
8434         var items = this.getItems();
8435         var field = items.get(id);
8436         if(!field){
8437              items.each(function(f){
8438                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8439                     field = f;
8440                     return false;
8441                 }
8442                 return true;
8443             });
8444         }
8445         return field || null;
8446     },
8447      /**
8448      * Mark fields in this form invalid in bulk.
8449      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8450      * @return {BasicForm} this
8451      */
8452     markInvalid : function(errors){
8453         if(errors instanceof Array){
8454             for(var i = 0, len = errors.length; i < len; i++){
8455                 var fieldError = errors[i];
8456                 var f = this.findField(fieldError.id);
8457                 if(f){
8458                     f.markInvalid(fieldError.msg);
8459                 }
8460             }
8461         }else{
8462             var field, id;
8463             for(id in errors){
8464                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8465                     field.markInvalid(errors[id]);
8466                 }
8467             }
8468         }
8469         //Roo.each(this.childForms || [], function (f) {
8470         //    f.markInvalid(errors);
8471         //});
8472
8473         return this;
8474     },
8475
8476     /**
8477      * Set values for fields in this form in bulk.
8478      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8479      * @return {BasicForm} this
8480      */
8481     setValues : function(values){
8482         if(values instanceof Array){ // array of objects
8483             for(var i = 0, len = values.length; i < len; i++){
8484                 var v = values[i];
8485                 var f = this.findField(v.id);
8486                 if(f){
8487                     f.setValue(v.value);
8488                     if(this.trackResetOnLoad){
8489                         f.originalValue = f.getValue();
8490                     }
8491                 }
8492             }
8493         }else{ // object hash
8494             var field, id;
8495             for(id in values){
8496                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8497
8498                     if (field.setFromData &&
8499                         field.valueField &&
8500                         field.displayField &&
8501                         // combos' with local stores can
8502                         // be queried via setValue()
8503                         // to set their value..
8504                         (field.store && !field.store.isLocal)
8505                         ) {
8506                         // it's a combo
8507                         var sd = { };
8508                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8509                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8510                         field.setFromData(sd);
8511
8512                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8513                         
8514                         field.setFromData(values);
8515                         
8516                     } else {
8517                         field.setValue(values[id]);
8518                     }
8519
8520
8521                     if(this.trackResetOnLoad){
8522                         field.originalValue = field.getValue();
8523                     }
8524                 }
8525             }
8526         }
8527
8528         //Roo.each(this.childForms || [], function (f) {
8529         //    f.setValues(values);
8530         //});
8531
8532         return this;
8533     },
8534
8535     /**
8536      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8537      * they are returned as an array.
8538      * @param {Boolean} asString
8539      * @return {Object}
8540      */
8541     getValues : function(asString){
8542         //if (this.childForms) {
8543             // copy values from the child forms
8544         //    Roo.each(this.childForms, function (f) {
8545         //        this.setValues(f.getValues());
8546         //    }, this);
8547         //}
8548
8549
8550
8551         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8552         if(asString === true){
8553             return fs;
8554         }
8555         return Roo.urlDecode(fs);
8556     },
8557
8558     /**
8559      * Returns the fields in this form as an object with key/value pairs.
8560      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8561      * @return {Object}
8562      */
8563     getFieldValues : function(with_hidden)
8564     {
8565         var items = this.getItems();
8566         var ret = {};
8567         items.each(function(f){
8568             
8569             if (!f.getName()) {
8570                 return;
8571             }
8572             
8573             var v = f.getValue();
8574             
8575             if (f.inputType =='radio') {
8576                 if (typeof(ret[f.getName()]) == 'undefined') {
8577                     ret[f.getName()] = ''; // empty..
8578                 }
8579
8580                 if (!f.el.dom.checked) {
8581                     return;
8582
8583                 }
8584                 v = f.el.dom.value;
8585
8586             }
8587             
8588             if(f.xtype == 'MoneyField'){
8589                 ret[f.currencyName] = f.getCurrency();
8590             }
8591
8592             // not sure if this supported any more..
8593             if ((typeof(v) == 'object') && f.getRawValue) {
8594                 v = f.getRawValue() ; // dates..
8595             }
8596             // combo boxes where name != hiddenName...
8597             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8598                 ret[f.name] = f.getRawValue();
8599             }
8600             ret[f.getName()] = v;
8601         });
8602
8603         return ret;
8604     },
8605
8606     /**
8607      * Clears all invalid messages in this form.
8608      * @return {BasicForm} this
8609      */
8610     clearInvalid : function(){
8611         var items = this.getItems();
8612
8613         items.each(function(f){
8614            f.clearInvalid();
8615         });
8616
8617         return this;
8618     },
8619
8620     /**
8621      * Resets this form.
8622      * @return {BasicForm} this
8623      */
8624     reset : function(){
8625         var items = this.getItems();
8626         items.each(function(f){
8627             f.reset();
8628         });
8629
8630         Roo.each(this.childForms || [], function (f) {
8631             f.reset();
8632         });
8633
8634
8635         return this;
8636     },
8637     
8638     getItems : function()
8639     {
8640         var r=new Roo.util.MixedCollection(false, function(o){
8641             return o.id || (o.id = Roo.id());
8642         });
8643         var iter = function(el) {
8644             if (el.inputEl) {
8645                 r.add(el);
8646             }
8647             if (!el.items) {
8648                 return;
8649             }
8650             Roo.each(el.items,function(e) {
8651                 iter(e);
8652             });
8653         };
8654
8655         iter(this);
8656         return r;
8657     },
8658     
8659     hideFields : function(items)
8660     {
8661         Roo.each(items, function(i){
8662             
8663             var f = this.findField(i);
8664             
8665             if(!f){
8666                 return;
8667             }
8668             
8669             f.hide();
8670             
8671         }, this);
8672     },
8673     
8674     showFields : function(items)
8675     {
8676         Roo.each(items, function(i){
8677             
8678             var f = this.findField(i);
8679             
8680             if(!f){
8681                 return;
8682             }
8683             
8684             f.show();
8685             
8686         }, this);
8687     }
8688
8689 });
8690
8691 Roo.apply(Roo.bootstrap.Form, {
8692     
8693     popover : {
8694         
8695         padding : 5,
8696         
8697         isApplied : false,
8698         
8699         isMasked : false,
8700         
8701         form : false,
8702         
8703         target : false,
8704         
8705         toolTip : false,
8706         
8707         intervalID : false,
8708         
8709         maskEl : false,
8710         
8711         apply : function()
8712         {
8713             if(this.isApplied){
8714                 return;
8715             }
8716             
8717             this.maskEl = {
8718                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8719                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8720                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8721                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8722             };
8723             
8724             this.maskEl.top.enableDisplayMode("block");
8725             this.maskEl.left.enableDisplayMode("block");
8726             this.maskEl.bottom.enableDisplayMode("block");
8727             this.maskEl.right.enableDisplayMode("block");
8728             
8729             this.toolTip = new Roo.bootstrap.Tooltip({
8730                 cls : 'roo-form-error-popover',
8731                 alignment : {
8732                     'left' : ['r-l', [-2,0], 'right'],
8733                     'right' : ['l-r', [2,0], 'left'],
8734                     'bottom' : ['tl-bl', [0,2], 'top'],
8735                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8736                 }
8737             });
8738             
8739             this.toolTip.render(Roo.get(document.body));
8740
8741             this.toolTip.el.enableDisplayMode("block");
8742             
8743             Roo.get(document.body).on('click', function(){
8744                 this.unmask();
8745             }, this);
8746             
8747             Roo.get(document.body).on('touchstart', function(){
8748                 this.unmask();
8749             }, this);
8750             
8751             this.isApplied = true
8752         },
8753         
8754         mask : function(form, target)
8755         {
8756             this.form = form;
8757             
8758             this.target = target;
8759             
8760             if(!this.form.errorMask || !target.el){
8761                 return;
8762             }
8763             
8764             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8765             
8766             Roo.log(scrollable);
8767             
8768             var ot = this.target.el.calcOffsetsTo(scrollable);
8769             
8770             var scrollTo = ot[1] - this.form.maskOffset;
8771             
8772             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8773             
8774             scrollable.scrollTo('top', scrollTo);
8775             
8776             var box = this.target.el.getBox();
8777             Roo.log(box);
8778             var zIndex = Roo.bootstrap.Modal.zIndex++;
8779
8780             
8781             this.maskEl.top.setStyle('position', 'absolute');
8782             this.maskEl.top.setStyle('z-index', zIndex);
8783             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8784             this.maskEl.top.setLeft(0);
8785             this.maskEl.top.setTop(0);
8786             this.maskEl.top.show();
8787             
8788             this.maskEl.left.setStyle('position', 'absolute');
8789             this.maskEl.left.setStyle('z-index', zIndex);
8790             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8791             this.maskEl.left.setLeft(0);
8792             this.maskEl.left.setTop(box.y - this.padding);
8793             this.maskEl.left.show();
8794
8795             this.maskEl.bottom.setStyle('position', 'absolute');
8796             this.maskEl.bottom.setStyle('z-index', zIndex);
8797             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8798             this.maskEl.bottom.setLeft(0);
8799             this.maskEl.bottom.setTop(box.bottom + this.padding);
8800             this.maskEl.bottom.show();
8801
8802             this.maskEl.right.setStyle('position', 'absolute');
8803             this.maskEl.right.setStyle('z-index', zIndex);
8804             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8805             this.maskEl.right.setLeft(box.right + this.padding);
8806             this.maskEl.right.setTop(box.y - this.padding);
8807             this.maskEl.right.show();
8808
8809             this.toolTip.bindEl = this.target.el;
8810
8811             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8812
8813             var tip = this.target.blankText;
8814
8815             if(this.target.getValue() !== '' ) {
8816                 
8817                 if (this.target.invalidText.length) {
8818                     tip = this.target.invalidText;
8819                 } else if (this.target.regexText.length){
8820                     tip = this.target.regexText;
8821                 }
8822             }
8823
8824             this.toolTip.show(tip);
8825
8826             this.intervalID = window.setInterval(function() {
8827                 Roo.bootstrap.Form.popover.unmask();
8828             }, 10000);
8829
8830             window.onwheel = function(){ return false;};
8831             
8832             (function(){ this.isMasked = true; }).defer(500, this);
8833             
8834         },
8835         
8836         unmask : function()
8837         {
8838             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8839                 return;
8840             }
8841             
8842             this.maskEl.top.setStyle('position', 'absolute');
8843             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8844             this.maskEl.top.hide();
8845
8846             this.maskEl.left.setStyle('position', 'absolute');
8847             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8848             this.maskEl.left.hide();
8849
8850             this.maskEl.bottom.setStyle('position', 'absolute');
8851             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8852             this.maskEl.bottom.hide();
8853
8854             this.maskEl.right.setStyle('position', 'absolute');
8855             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8856             this.maskEl.right.hide();
8857             
8858             this.toolTip.hide();
8859             
8860             this.toolTip.el.hide();
8861             
8862             window.onwheel = function(){ return true;};
8863             
8864             if(this.intervalID){
8865                 window.clearInterval(this.intervalID);
8866                 this.intervalID = false;
8867             }
8868             
8869             this.isMasked = false;
8870             
8871         }
8872         
8873     }
8874     
8875 });
8876
8877 /*
8878  * Based on:
8879  * Ext JS Library 1.1.1
8880  * Copyright(c) 2006-2007, Ext JS, LLC.
8881  *
8882  * Originally Released Under LGPL - original licence link has changed is not relivant.
8883  *
8884  * Fork - LGPL
8885  * <script type="text/javascript">
8886  */
8887 /**
8888  * @class Roo.form.VTypes
8889  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8890  * @singleton
8891  */
8892 Roo.form.VTypes = function(){
8893     // closure these in so they are only created once.
8894     var alpha = /^[a-zA-Z_]+$/;
8895     var alphanum = /^[a-zA-Z0-9_]+$/;
8896     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8897     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8898
8899     // All these messages and functions are configurable
8900     return {
8901         /**
8902          * The function used to validate email addresses
8903          * @param {String} value The email address
8904          */
8905         'email' : function(v){
8906             return email.test(v);
8907         },
8908         /**
8909          * The error text to display when the email validation function returns false
8910          * @type String
8911          */
8912         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8913         /**
8914          * The keystroke filter mask to be applied on email input
8915          * @type RegExp
8916          */
8917         'emailMask' : /[a-z0-9_\.\-@]/i,
8918
8919         /**
8920          * The function used to validate URLs
8921          * @param {String} value The URL
8922          */
8923         'url' : function(v){
8924             return url.test(v);
8925         },
8926         /**
8927          * The error text to display when the url validation function returns false
8928          * @type String
8929          */
8930         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8931         
8932         /**
8933          * The function used to validate alpha values
8934          * @param {String} value The value
8935          */
8936         'alpha' : function(v){
8937             return alpha.test(v);
8938         },
8939         /**
8940          * The error text to display when the alpha validation function returns false
8941          * @type String
8942          */
8943         'alphaText' : 'This field should only contain letters and _',
8944         /**
8945          * The keystroke filter mask to be applied on alpha input
8946          * @type RegExp
8947          */
8948         'alphaMask' : /[a-z_]/i,
8949
8950         /**
8951          * The function used to validate alphanumeric values
8952          * @param {String} value The value
8953          */
8954         'alphanum' : function(v){
8955             return alphanum.test(v);
8956         },
8957         /**
8958          * The error text to display when the alphanumeric validation function returns false
8959          * @type String
8960          */
8961         'alphanumText' : 'This field should only contain letters, numbers and _',
8962         /**
8963          * The keystroke filter mask to be applied on alphanumeric input
8964          * @type RegExp
8965          */
8966         'alphanumMask' : /[a-z0-9_]/i
8967     };
8968 }();/*
8969  * - LGPL
8970  *
8971  * Input
8972  * 
8973  */
8974
8975 /**
8976  * @class Roo.bootstrap.Input
8977  * @extends Roo.bootstrap.Component
8978  * Bootstrap Input class
8979  * @cfg {Boolean} disabled is it disabled
8980  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8981  * @cfg {String} name name of the input
8982  * @cfg {string} fieldLabel - the label associated
8983  * @cfg {string} placeholder - placeholder to put in text.
8984  * @cfg {string}  before - input group add on before
8985  * @cfg {string} after - input group add on after
8986  * @cfg {string} size - (lg|sm) or leave empty..
8987  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8988  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8989  * @cfg {Number} md colspan out of 12 for computer-sized screens
8990  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8991  * @cfg {string} value default value of the input
8992  * @cfg {Number} labelWidth set the width of label 
8993  * @cfg {Number} labellg set the width of label (1-12)
8994  * @cfg {Number} labelmd set the width of label (1-12)
8995  * @cfg {Number} labelsm set the width of label (1-12)
8996  * @cfg {Number} labelxs set the width of label (1-12)
8997  * @cfg {String} labelAlign (top|left)
8998  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8999  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
9000  * @cfg {String} indicatorpos (left|right) default left
9001  * @cfg {String} capture (user|camera) use for file input only. (default empty)
9002  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
9003
9004  * @cfg {String} align (left|center|right) Default left
9005  * @cfg {Boolean} forceFeedback (true|false) Default false
9006  * 
9007  * @constructor
9008  * Create a new Input
9009  * @param {Object} config The config object
9010  */
9011
9012 Roo.bootstrap.Input = function(config){
9013     
9014     Roo.bootstrap.Input.superclass.constructor.call(this, config);
9015     
9016     this.addEvents({
9017         /**
9018          * @event focus
9019          * Fires when this field receives input focus.
9020          * @param {Roo.form.Field} this
9021          */
9022         focus : true,
9023         /**
9024          * @event blur
9025          * Fires when this field loses input focus.
9026          * @param {Roo.form.Field} this
9027          */
9028         blur : true,
9029         /**
9030          * @event specialkey
9031          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
9032          * {@link Roo.EventObject#getKey} to determine which key was pressed.
9033          * @param {Roo.form.Field} this
9034          * @param {Roo.EventObject} e The event object
9035          */
9036         specialkey : true,
9037         /**
9038          * @event change
9039          * Fires just before the field blurs if the field value has changed.
9040          * @param {Roo.form.Field} this
9041          * @param {Mixed} newValue The new value
9042          * @param {Mixed} oldValue The original value
9043          */
9044         change : true,
9045         /**
9046          * @event invalid
9047          * Fires after the field has been marked as invalid.
9048          * @param {Roo.form.Field} this
9049          * @param {String} msg The validation message
9050          */
9051         invalid : true,
9052         /**
9053          * @event valid
9054          * Fires after the field has been validated with no errors.
9055          * @param {Roo.form.Field} this
9056          */
9057         valid : true,
9058          /**
9059          * @event keyup
9060          * Fires after the key up
9061          * @param {Roo.form.Field} this
9062          * @param {Roo.EventObject}  e The event Object
9063          */
9064         keyup : true
9065     });
9066 };
9067
9068 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
9069      /**
9070      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
9071       automatic validation (defaults to "keyup").
9072      */
9073     validationEvent : "keyup",
9074      /**
9075      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
9076      */
9077     validateOnBlur : true,
9078     /**
9079      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
9080      */
9081     validationDelay : 250,
9082      /**
9083      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
9084      */
9085     focusClass : "x-form-focus",  // not needed???
9086     
9087        
9088     /**
9089      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9090      */
9091     invalidClass : "has-warning",
9092     
9093     /**
9094      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9095      */
9096     validClass : "has-success",
9097     
9098     /**
9099      * @cfg {Boolean} hasFeedback (true|false) default true
9100      */
9101     hasFeedback : true,
9102     
9103     /**
9104      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9105      */
9106     invalidFeedbackClass : "glyphicon-warning-sign",
9107     
9108     /**
9109      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9110      */
9111     validFeedbackClass : "glyphicon-ok",
9112     
9113     /**
9114      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
9115      */
9116     selectOnFocus : false,
9117     
9118      /**
9119      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
9120      */
9121     maskRe : null,
9122        /**
9123      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
9124      */
9125     vtype : null,
9126     
9127       /**
9128      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
9129      */
9130     disableKeyFilter : false,
9131     
9132        /**
9133      * @cfg {Boolean} disabled True to disable the field (defaults to false).
9134      */
9135     disabled : false,
9136      /**
9137      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
9138      */
9139     allowBlank : true,
9140     /**
9141      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9142      */
9143     blankText : "Please complete this mandatory field",
9144     
9145      /**
9146      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9147      */
9148     minLength : 0,
9149     /**
9150      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9151      */
9152     maxLength : Number.MAX_VALUE,
9153     /**
9154      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9155      */
9156     minLengthText : "The minimum length for this field is {0}",
9157     /**
9158      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9159      */
9160     maxLengthText : "The maximum length for this field is {0}",
9161   
9162     
9163     /**
9164      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9165      * If available, this function will be called only after the basic validators all return true, and will be passed the
9166      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9167      */
9168     validator : null,
9169     /**
9170      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9171      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9172      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
9173      */
9174     regex : null,
9175     /**
9176      * @cfg {String} regexText -- Depricated - use Invalid Text
9177      */
9178     regexText : "",
9179     
9180     /**
9181      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9182      */
9183     invalidText : "",
9184     
9185     
9186     
9187     autocomplete: false,
9188     
9189     
9190     fieldLabel : '',
9191     inputType : 'text',
9192     
9193     name : false,
9194     placeholder: false,
9195     before : false,
9196     after : false,
9197     size : false,
9198     hasFocus : false,
9199     preventMark: false,
9200     isFormField : true,
9201     value : '',
9202     labelWidth : 2,
9203     labelAlign : false,
9204     readOnly : false,
9205     align : false,
9206     formatedValue : false,
9207     forceFeedback : false,
9208     
9209     indicatorpos : 'left',
9210     
9211     labellg : 0,
9212     labelmd : 0,
9213     labelsm : 0,
9214     labelxs : 0,
9215     
9216     capture : '',
9217     accept : '',
9218     
9219     parentLabelAlign : function()
9220     {
9221         var parent = this;
9222         while (parent.parent()) {
9223             parent = parent.parent();
9224             if (typeof(parent.labelAlign) !='undefined') {
9225                 return parent.labelAlign;
9226             }
9227         }
9228         return 'left';
9229         
9230     },
9231     
9232     getAutoCreate : function()
9233     {
9234         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9235         
9236         var id = Roo.id();
9237         
9238         var cfg = {};
9239         
9240         if(this.inputType != 'hidden'){
9241             cfg.cls = 'form-group' //input-group
9242         }
9243         
9244         var input =  {
9245             tag: 'input',
9246             id : id,
9247             type : this.inputType,
9248             value : this.value,
9249             cls : 'form-control',
9250             placeholder : this.placeholder || '',
9251             autocomplete : this.autocomplete || 'new-password'
9252         };
9253         
9254         if(this.capture.length){
9255             input.capture = this.capture;
9256         }
9257         
9258         if(this.accept.length){
9259             input.accept = this.accept + "/*";
9260         }
9261         
9262         if(this.align){
9263             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9264         }
9265         
9266         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9267             input.maxLength = this.maxLength;
9268         }
9269         
9270         if (this.disabled) {
9271             input.disabled=true;
9272         }
9273         
9274         if (this.readOnly) {
9275             input.readonly=true;
9276         }
9277         
9278         if (this.name) {
9279             input.name = this.name;
9280         }
9281         
9282         if (this.size) {
9283             input.cls += ' input-' + this.size;
9284         }
9285         
9286         var settings=this;
9287         ['xs','sm','md','lg'].map(function(size){
9288             if (settings[size]) {
9289                 cfg.cls += ' col-' + size + '-' + settings[size];
9290             }
9291         });
9292         
9293         var inputblock = input;
9294         
9295         var feedback = {
9296             tag: 'span',
9297             cls: 'glyphicon form-control-feedback'
9298         };
9299             
9300         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9301             
9302             inputblock = {
9303                 cls : 'has-feedback',
9304                 cn :  [
9305                     input,
9306                     feedback
9307                 ] 
9308             };  
9309         }
9310         
9311         if (this.before || this.after) {
9312             
9313             inputblock = {
9314                 cls : 'input-group',
9315                 cn :  [] 
9316             };
9317             
9318             if (this.before && typeof(this.before) == 'string') {
9319                 
9320                 inputblock.cn.push({
9321                     tag :'span',
9322                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9323                     html : this.before
9324                 });
9325             }
9326             if (this.before && typeof(this.before) == 'object') {
9327                 this.before = Roo.factory(this.before);
9328                 
9329                 inputblock.cn.push({
9330                     tag :'span',
9331                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9332                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9333                 });
9334             }
9335             
9336             inputblock.cn.push(input);
9337             
9338             if (this.after && typeof(this.after) == 'string') {
9339                 inputblock.cn.push({
9340                     tag :'span',
9341                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9342                     html : this.after
9343                 });
9344             }
9345             if (this.after && typeof(this.after) == 'object') {
9346                 this.after = Roo.factory(this.after);
9347                 
9348                 inputblock.cn.push({
9349                     tag :'span',
9350                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
9351                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9352                 });
9353             }
9354             
9355             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9356                 inputblock.cls += ' has-feedback';
9357                 inputblock.cn.push(feedback);
9358             }
9359         };
9360         var indicator = {
9361             tag : 'i',
9362             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9363             tooltip : 'This field is required'
9364         };
9365         if (Roo.bootstrap.version == 4) {
9366             indicator = {
9367                 tag : 'i',
9368                 style : 'display-none'
9369             };
9370         }
9371         if (align ==='left' && this.fieldLabel.length) {
9372             
9373             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
9374             
9375             cfg.cn = [
9376                 indicator,
9377                 {
9378                     tag: 'label',
9379                     'for' :  id,
9380                     cls : 'control-label col-form-label',
9381                     html : this.fieldLabel
9382
9383                 },
9384                 {
9385                     cls : "", 
9386                     cn: [
9387                         inputblock
9388                     ]
9389                 }
9390             ];
9391             
9392             var labelCfg = cfg.cn[1];
9393             var contentCfg = cfg.cn[2];
9394             
9395             if(this.indicatorpos == 'right'){
9396                 cfg.cn = [
9397                     {
9398                         tag: 'label',
9399                         'for' :  id,
9400                         cls : 'control-label col-form-label',
9401                         cn : [
9402                             {
9403                                 tag : 'span',
9404                                 html : this.fieldLabel
9405                             },
9406                             indicator
9407                         ]
9408                     },
9409                     {
9410                         cls : "",
9411                         cn: [
9412                             inputblock
9413                         ]
9414                     }
9415
9416                 ];
9417                 
9418                 labelCfg = cfg.cn[0];
9419                 contentCfg = cfg.cn[1];
9420             
9421             }
9422             
9423             if(this.labelWidth > 12){
9424                 labelCfg.style = "width: " + this.labelWidth + 'px';
9425             }
9426             
9427             if(this.labelWidth < 13 && this.labelmd == 0){
9428                 this.labelmd = this.labelWidth;
9429             }
9430             
9431             if(this.labellg > 0){
9432                 labelCfg.cls += ' col-lg-' + this.labellg;
9433                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9434             }
9435             
9436             if(this.labelmd > 0){
9437                 labelCfg.cls += ' col-md-' + this.labelmd;
9438                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9439             }
9440             
9441             if(this.labelsm > 0){
9442                 labelCfg.cls += ' col-sm-' + this.labelsm;
9443                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9444             }
9445             
9446             if(this.labelxs > 0){
9447                 labelCfg.cls += ' col-xs-' + this.labelxs;
9448                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9449             }
9450             
9451             
9452         } else if ( this.fieldLabel.length) {
9453                 
9454             cfg.cn = [
9455                 {
9456                     tag : 'i',
9457                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9458                     tooltip : 'This field is required'
9459                 },
9460                 {
9461                     tag: 'label',
9462                    //cls : 'input-group-addon',
9463                     html : this.fieldLabel
9464
9465                 },
9466
9467                inputblock
9468
9469            ];
9470            
9471            if(this.indicatorpos == 'right'){
9472                 
9473                 cfg.cn = [
9474                     {
9475                         tag: 'label',
9476                        //cls : 'input-group-addon',
9477                         html : this.fieldLabel
9478
9479                     },
9480                     {
9481                         tag : 'i',
9482                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9483                         tooltip : 'This field is required'
9484                     },
9485
9486                    inputblock
9487
9488                ];
9489
9490             }
9491
9492         } else {
9493             
9494             cfg.cn = [
9495
9496                     inputblock
9497
9498             ];
9499                 
9500                 
9501         };
9502         
9503         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9504            cfg.cls += ' navbar-form';
9505         }
9506         
9507         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
9508             // on BS4 we do this only if not form 
9509             cfg.cls += ' navbar-form';
9510             cfg.tag = 'li';
9511         }
9512         
9513         return cfg;
9514         
9515     },
9516     /**
9517      * return the real input element.
9518      */
9519     inputEl: function ()
9520     {
9521         return this.el.select('input.form-control',true).first();
9522     },
9523     
9524     tooltipEl : function()
9525     {
9526         return this.inputEl();
9527     },
9528     
9529     indicatorEl : function()
9530     {
9531         if (Roo.bootstrap.version == 4) {
9532             return false; // not enabled in v4 yet.
9533         }
9534         
9535         var indicator = this.el.select('i.roo-required-indicator',true).first();
9536         
9537         if(!indicator){
9538             return false;
9539         }
9540         
9541         return indicator;
9542         
9543     },
9544     
9545     setDisabled : function(v)
9546     {
9547         var i  = this.inputEl().dom;
9548         if (!v) {
9549             i.removeAttribute('disabled');
9550             return;
9551             
9552         }
9553         i.setAttribute('disabled','true');
9554     },
9555     initEvents : function()
9556     {
9557           
9558         this.inputEl().on("keydown" , this.fireKey,  this);
9559         this.inputEl().on("focus", this.onFocus,  this);
9560         this.inputEl().on("blur", this.onBlur,  this);
9561         
9562         this.inputEl().relayEvent('keyup', this);
9563         
9564         this.indicator = this.indicatorEl();
9565         
9566         if(this.indicator){
9567             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9568         }
9569  
9570         // reference to original value for reset
9571         this.originalValue = this.getValue();
9572         //Roo.form.TextField.superclass.initEvents.call(this);
9573         if(this.validationEvent == 'keyup'){
9574             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9575             this.inputEl().on('keyup', this.filterValidation, this);
9576         }
9577         else if(this.validationEvent !== false){
9578             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9579         }
9580         
9581         if(this.selectOnFocus){
9582             this.on("focus", this.preFocus, this);
9583             
9584         }
9585         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9586             this.inputEl().on("keypress", this.filterKeys, this);
9587         } else {
9588             this.inputEl().relayEvent('keypress', this);
9589         }
9590        /* if(this.grow){
9591             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9592             this.el.on("click", this.autoSize,  this);
9593         }
9594         */
9595         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9596             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9597         }
9598         
9599         if (typeof(this.before) == 'object') {
9600             this.before.render(this.el.select('.roo-input-before',true).first());
9601         }
9602         if (typeof(this.after) == 'object') {
9603             this.after.render(this.el.select('.roo-input-after',true).first());
9604         }
9605         
9606         this.inputEl().on('change', this.onChange, this);
9607         
9608     },
9609     filterValidation : function(e){
9610         if(!e.isNavKeyPress()){
9611             this.validationTask.delay(this.validationDelay);
9612         }
9613     },
9614      /**
9615      * Validates the field value
9616      * @return {Boolean} True if the value is valid, else false
9617      */
9618     validate : function(){
9619         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9620         if(this.disabled || this.validateValue(this.getRawValue())){
9621             this.markValid();
9622             return true;
9623         }
9624         
9625         this.markInvalid();
9626         return false;
9627     },
9628     
9629     
9630     /**
9631      * Validates a value according to the field's validation rules and marks the field as invalid
9632      * if the validation fails
9633      * @param {Mixed} value The value to validate
9634      * @return {Boolean} True if the value is valid, else false
9635      */
9636     validateValue : function(value)
9637     {
9638         if(this.getVisibilityEl().hasClass('hidden')){
9639             return true;
9640         }
9641         
9642         if(value.length < 1)  { // if it's blank
9643             if(this.allowBlank){
9644                 return true;
9645             }
9646             return false;
9647         }
9648         
9649         if(value.length < this.minLength){
9650             return false;
9651         }
9652         if(value.length > this.maxLength){
9653             return false;
9654         }
9655         if(this.vtype){
9656             var vt = Roo.form.VTypes;
9657             if(!vt[this.vtype](value, this)){
9658                 return false;
9659             }
9660         }
9661         if(typeof this.validator == "function"){
9662             var msg = this.validator(value);
9663             if(msg !== true){
9664                 return false;
9665             }
9666             if (typeof(msg) == 'string') {
9667                 this.invalidText = msg;
9668             }
9669         }
9670         
9671         if(this.regex && !this.regex.test(value)){
9672             return false;
9673         }
9674         
9675         return true;
9676     },
9677     
9678      // private
9679     fireKey : function(e){
9680         //Roo.log('field ' + e.getKey());
9681         if(e.isNavKeyPress()){
9682             this.fireEvent("specialkey", this, e);
9683         }
9684     },
9685     focus : function (selectText){
9686         if(this.rendered){
9687             this.inputEl().focus();
9688             if(selectText === true){
9689                 this.inputEl().dom.select();
9690             }
9691         }
9692         return this;
9693     } ,
9694     
9695     onFocus : function(){
9696         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9697            // this.el.addClass(this.focusClass);
9698         }
9699         if(!this.hasFocus){
9700             this.hasFocus = true;
9701             this.startValue = this.getValue();
9702             this.fireEvent("focus", this);
9703         }
9704     },
9705     
9706     beforeBlur : Roo.emptyFn,
9707
9708     
9709     // private
9710     onBlur : function(){
9711         this.beforeBlur();
9712         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9713             //this.el.removeClass(this.focusClass);
9714         }
9715         this.hasFocus = false;
9716         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9717             this.validate();
9718         }
9719         var v = this.getValue();
9720         if(String(v) !== String(this.startValue)){
9721             this.fireEvent('change', this, v, this.startValue);
9722         }
9723         this.fireEvent("blur", this);
9724     },
9725     
9726     onChange : function(e)
9727     {
9728         var v = this.getValue();
9729         if(String(v) !== String(this.startValue)){
9730             this.fireEvent('change', this, v, this.startValue);
9731         }
9732         
9733     },
9734     
9735     /**
9736      * Resets the current field value to the originally loaded value and clears any validation messages
9737      */
9738     reset : function(){
9739         this.setValue(this.originalValue);
9740         this.validate();
9741     },
9742      /**
9743      * Returns the name of the field
9744      * @return {Mixed} name The name field
9745      */
9746     getName: function(){
9747         return this.name;
9748     },
9749      /**
9750      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9751      * @return {Mixed} value The field value
9752      */
9753     getValue : function(){
9754         
9755         var v = this.inputEl().getValue();
9756         
9757         return v;
9758     },
9759     /**
9760      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9761      * @return {Mixed} value The field value
9762      */
9763     getRawValue : function(){
9764         var v = this.inputEl().getValue();
9765         
9766         return v;
9767     },
9768     
9769     /**
9770      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9771      * @param {Mixed} value The value to set
9772      */
9773     setRawValue : function(v){
9774         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9775     },
9776     
9777     selectText : function(start, end){
9778         var v = this.getRawValue();
9779         if(v.length > 0){
9780             start = start === undefined ? 0 : start;
9781             end = end === undefined ? v.length : end;
9782             var d = this.inputEl().dom;
9783             if(d.setSelectionRange){
9784                 d.setSelectionRange(start, end);
9785             }else if(d.createTextRange){
9786                 var range = d.createTextRange();
9787                 range.moveStart("character", start);
9788                 range.moveEnd("character", v.length-end);
9789                 range.select();
9790             }
9791         }
9792     },
9793     
9794     /**
9795      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9796      * @param {Mixed} value The value to set
9797      */
9798     setValue : function(v){
9799         this.value = v;
9800         if(this.rendered){
9801             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9802             this.validate();
9803         }
9804     },
9805     
9806     /*
9807     processValue : function(value){
9808         if(this.stripCharsRe){
9809             var newValue = value.replace(this.stripCharsRe, '');
9810             if(newValue !== value){
9811                 this.setRawValue(newValue);
9812                 return newValue;
9813             }
9814         }
9815         return value;
9816     },
9817   */
9818     preFocus : function(){
9819         
9820         if(this.selectOnFocus){
9821             this.inputEl().dom.select();
9822         }
9823     },
9824     filterKeys : function(e){
9825         var k = e.getKey();
9826         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9827             return;
9828         }
9829         var c = e.getCharCode(), cc = String.fromCharCode(c);
9830         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9831             return;
9832         }
9833         if(!this.maskRe.test(cc)){
9834             e.stopEvent();
9835         }
9836     },
9837      /**
9838      * Clear any invalid styles/messages for this field
9839      */
9840     clearInvalid : function(){
9841         
9842         if(!this.el || this.preventMark){ // not rendered
9843             return;
9844         }
9845         
9846         
9847         this.el.removeClass([this.invalidClass, 'is-invalid']);
9848         
9849         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9850             
9851             var feedback = this.el.select('.form-control-feedback', true).first();
9852             
9853             if(feedback){
9854                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9855             }
9856             
9857         }
9858         
9859         if(this.indicator){
9860             this.indicator.removeClass('visible');
9861             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9862         }
9863         
9864         this.fireEvent('valid', this);
9865     },
9866     
9867      /**
9868      * Mark this field as valid
9869      */
9870     markValid : function()
9871     {
9872         if(!this.el  || this.preventMark){ // not rendered...
9873             return;
9874         }
9875         
9876         this.el.removeClass([this.invalidClass, this.validClass]);
9877         this.inputEl().removeClass(['is-valid', 'is-invalid']);
9878
9879         var feedback = this.el.select('.form-control-feedback', true).first();
9880             
9881         if(feedback){
9882             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9883         }
9884         
9885         if(this.indicator){
9886             this.indicator.removeClass('visible');
9887             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9888         }
9889         
9890         if(this.disabled){
9891             return;
9892         }
9893         
9894         if(this.allowBlank && !this.getRawValue().length){
9895             return;
9896         }
9897         if (Roo.bootstrap.version == 3) {
9898             this.el.addClass(this.validClass);
9899         } else {
9900             this.inputEl().addClass('is-valid');
9901         }
9902
9903         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9904             
9905             var feedback = this.el.select('.form-control-feedback', true).first();
9906             
9907             if(feedback){
9908                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9909                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9910             }
9911             
9912         }
9913         
9914         this.fireEvent('valid', this);
9915     },
9916     
9917      /**
9918      * Mark this field as invalid
9919      * @param {String} msg The validation message
9920      */
9921     markInvalid : function(msg)
9922     {
9923         if(!this.el  || this.preventMark){ // not rendered
9924             return;
9925         }
9926         
9927         this.el.removeClass([this.invalidClass, this.validClass]);
9928         this.inputEl().removeClass(['is-valid', 'is-invalid']);
9929         
9930         var feedback = this.el.select('.form-control-feedback', true).first();
9931             
9932         if(feedback){
9933             this.el.select('.form-control-feedback', true).first().removeClass(
9934                     [this.invalidFeedbackClass, this.validFeedbackClass]);
9935         }
9936
9937         if(this.disabled){
9938             return;
9939         }
9940         
9941         if(this.allowBlank && !this.getRawValue().length){
9942             return;
9943         }
9944         
9945         if(this.indicator){
9946             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9947             this.indicator.addClass('visible');
9948         }
9949         if (Roo.bootstrap.version == 3) {
9950             this.el.addClass(this.invalidClass);
9951         } else {
9952             this.inputEl().addClass('is-invalid');
9953         }
9954         
9955         
9956         
9957         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9958             
9959             var feedback = this.el.select('.form-control-feedback', true).first();
9960             
9961             if(feedback){
9962                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9963                 
9964                 if(this.getValue().length || this.forceFeedback){
9965                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9966                 }
9967                 
9968             }
9969             
9970         }
9971         
9972         this.fireEvent('invalid', this, msg);
9973     },
9974     // private
9975     SafariOnKeyDown : function(event)
9976     {
9977         // this is a workaround for a password hang bug on chrome/ webkit.
9978         if (this.inputEl().dom.type != 'password') {
9979             return;
9980         }
9981         
9982         var isSelectAll = false;
9983         
9984         if(this.inputEl().dom.selectionEnd > 0){
9985             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9986         }
9987         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9988             event.preventDefault();
9989             this.setValue('');
9990             return;
9991         }
9992         
9993         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9994             
9995             event.preventDefault();
9996             // this is very hacky as keydown always get's upper case.
9997             //
9998             var cc = String.fromCharCode(event.getCharCode());
9999             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
10000             
10001         }
10002     },
10003     adjustWidth : function(tag, w){
10004         tag = tag.toLowerCase();
10005         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
10006             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
10007                 if(tag == 'input'){
10008                     return w + 2;
10009                 }
10010                 if(tag == 'textarea'){
10011                     return w-2;
10012                 }
10013             }else if(Roo.isOpera){
10014                 if(tag == 'input'){
10015                     return w + 2;
10016                 }
10017                 if(tag == 'textarea'){
10018                     return w-2;
10019                 }
10020             }
10021         }
10022         return w;
10023     },
10024     
10025     setFieldLabel : function(v)
10026     {
10027         if(!this.rendered){
10028             return;
10029         }
10030         
10031         if(this.indicatorEl()){
10032             var ar = this.el.select('label > span',true);
10033             
10034             if (ar.elements.length) {
10035                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10036                 this.fieldLabel = v;
10037                 return;
10038             }
10039             
10040             var br = this.el.select('label',true);
10041             
10042             if(br.elements.length) {
10043                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10044                 this.fieldLabel = v;
10045                 return;
10046             }
10047             
10048             Roo.log('Cannot Found any of label > span || label in input');
10049             return;
10050         }
10051         
10052         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10053         this.fieldLabel = v;
10054         
10055         
10056     }
10057 });
10058
10059  
10060 /*
10061  * - LGPL
10062  *
10063  * Input
10064  * 
10065  */
10066
10067 /**
10068  * @class Roo.bootstrap.TextArea
10069  * @extends Roo.bootstrap.Input
10070  * Bootstrap TextArea class
10071  * @cfg {Number} cols Specifies the visible width of a text area
10072  * @cfg {Number} rows Specifies the visible number of lines in a text area
10073  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
10074  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
10075  * @cfg {string} html text
10076  * 
10077  * @constructor
10078  * Create a new TextArea
10079  * @param {Object} config The config object
10080  */
10081
10082 Roo.bootstrap.TextArea = function(config){
10083     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
10084    
10085 };
10086
10087 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
10088      
10089     cols : false,
10090     rows : 5,
10091     readOnly : false,
10092     warp : 'soft',
10093     resize : false,
10094     value: false,
10095     html: false,
10096     
10097     getAutoCreate : function(){
10098         
10099         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10100         
10101         var id = Roo.id();
10102         
10103         var cfg = {};
10104         
10105         if(this.inputType != 'hidden'){
10106             cfg.cls = 'form-group' //input-group
10107         }
10108         
10109         var input =  {
10110             tag: 'textarea',
10111             id : id,
10112             warp : this.warp,
10113             rows : this.rows,
10114             value : this.value || '',
10115             html: this.html || '',
10116             cls : 'form-control',
10117             placeholder : this.placeholder || '' 
10118             
10119         };
10120         
10121         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10122             input.maxLength = this.maxLength;
10123         }
10124         
10125         if(this.resize){
10126             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
10127         }
10128         
10129         if(this.cols){
10130             input.cols = this.cols;
10131         }
10132         
10133         if (this.readOnly) {
10134             input.readonly = true;
10135         }
10136         
10137         if (this.name) {
10138             input.name = this.name;
10139         }
10140         
10141         if (this.size) {
10142             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
10143         }
10144         
10145         var settings=this;
10146         ['xs','sm','md','lg'].map(function(size){
10147             if (settings[size]) {
10148                 cfg.cls += ' col-' + size + '-' + settings[size];
10149             }
10150         });
10151         
10152         var inputblock = input;
10153         
10154         if(this.hasFeedback && !this.allowBlank){
10155             
10156             var feedback = {
10157                 tag: 'span',
10158                 cls: 'glyphicon form-control-feedback'
10159             };
10160
10161             inputblock = {
10162                 cls : 'has-feedback',
10163                 cn :  [
10164                     input,
10165                     feedback
10166                 ] 
10167             };  
10168         }
10169         
10170         
10171         if (this.before || this.after) {
10172             
10173             inputblock = {
10174                 cls : 'input-group',
10175                 cn :  [] 
10176             };
10177             if (this.before) {
10178                 inputblock.cn.push({
10179                     tag :'span',
10180                     cls : 'input-group-addon',
10181                     html : this.before
10182                 });
10183             }
10184             
10185             inputblock.cn.push(input);
10186             
10187             if(this.hasFeedback && !this.allowBlank){
10188                 inputblock.cls += ' has-feedback';
10189                 inputblock.cn.push(feedback);
10190             }
10191             
10192             if (this.after) {
10193                 inputblock.cn.push({
10194                     tag :'span',
10195                     cls : 'input-group-addon',
10196                     html : this.after
10197                 });
10198             }
10199             
10200         }
10201         
10202         if (align ==='left' && this.fieldLabel.length) {
10203             cfg.cn = [
10204                 {
10205                     tag: 'label',
10206                     'for' :  id,
10207                     cls : 'control-label',
10208                     html : this.fieldLabel
10209                 },
10210                 {
10211                     cls : "",
10212                     cn: [
10213                         inputblock
10214                     ]
10215                 }
10216
10217             ];
10218             
10219             if(this.labelWidth > 12){
10220                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10221             }
10222
10223             if(this.labelWidth < 13 && this.labelmd == 0){
10224                 this.labelmd = this.labelWidth;
10225             }
10226
10227             if(this.labellg > 0){
10228                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10229                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10230             }
10231
10232             if(this.labelmd > 0){
10233                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10234                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10235             }
10236
10237             if(this.labelsm > 0){
10238                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10239                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10240             }
10241
10242             if(this.labelxs > 0){
10243                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10244                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10245             }
10246             
10247         } else if ( this.fieldLabel.length) {
10248             cfg.cn = [
10249
10250                {
10251                    tag: 'label',
10252                    //cls : 'input-group-addon',
10253                    html : this.fieldLabel
10254
10255                },
10256
10257                inputblock
10258
10259            ];
10260
10261         } else {
10262
10263             cfg.cn = [
10264
10265                 inputblock
10266
10267             ];
10268                 
10269         }
10270         
10271         if (this.disabled) {
10272             input.disabled=true;
10273         }
10274         
10275         return cfg;
10276         
10277     },
10278     /**
10279      * return the real textarea element.
10280      */
10281     inputEl: function ()
10282     {
10283         return this.el.select('textarea.form-control',true).first();
10284     },
10285     
10286     /**
10287      * Clear any invalid styles/messages for this field
10288      */
10289     clearInvalid : function()
10290     {
10291         
10292         if(!this.el || this.preventMark){ // not rendered
10293             return;
10294         }
10295         
10296         var label = this.el.select('label', true).first();
10297         var icon = this.el.select('i.fa-star', true).first();
10298         
10299         if(label && icon){
10300             icon.remove();
10301         }
10302         this.el.removeClass( this.validClass);
10303         this.inputEl().removeClass('is-invalid');
10304          
10305         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10306             
10307             var feedback = this.el.select('.form-control-feedback', true).first();
10308             
10309             if(feedback){
10310                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10311             }
10312             
10313         }
10314         
10315         this.fireEvent('valid', this);
10316     },
10317     
10318      /**
10319      * Mark this field as valid
10320      */
10321     markValid : function()
10322     {
10323         if(!this.el  || this.preventMark){ // not rendered
10324             return;
10325         }
10326         
10327         this.el.removeClass([this.invalidClass, this.validClass]);
10328         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10329         
10330         var feedback = this.el.select('.form-control-feedback', true).first();
10331             
10332         if(feedback){
10333             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10334         }
10335
10336         if(this.disabled || this.allowBlank){
10337             return;
10338         }
10339         
10340         var label = this.el.select('label', true).first();
10341         var icon = this.el.select('i.fa-star', true).first();
10342         
10343         if(label && icon){
10344             icon.remove();
10345         }
10346         if (Roo.bootstrap.version == 3) {
10347             this.el.addClass(this.validClass);
10348         } else {
10349             this.inputEl().addClass('is-valid');
10350         }
10351         
10352         
10353         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10354             
10355             var feedback = this.el.select('.form-control-feedback', true).first();
10356             
10357             if(feedback){
10358                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10359                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10360             }
10361             
10362         }
10363         
10364         this.fireEvent('valid', this);
10365     },
10366     
10367      /**
10368      * Mark this field as invalid
10369      * @param {String} msg The validation message
10370      */
10371     markInvalid : function(msg)
10372     {
10373         if(!this.el  || this.preventMark){ // not rendered
10374             return;
10375         }
10376         
10377         this.el.removeClass([this.invalidClass, this.validClass]);
10378         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10379         
10380         var feedback = this.el.select('.form-control-feedback', true).first();
10381             
10382         if(feedback){
10383             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10384         }
10385
10386         if(this.disabled || this.allowBlank){
10387             return;
10388         }
10389         
10390         var label = this.el.select('label', true).first();
10391         var icon = this.el.select('i.fa-star', true).first();
10392         
10393         if(!this.getValue().length && label && !icon){
10394             this.el.createChild({
10395                 tag : 'i',
10396                 cls : 'text-danger fa fa-lg fa-star',
10397                 tooltip : 'This field is required',
10398                 style : 'margin-right:5px;'
10399             }, label, true);
10400         }
10401         
10402         if (Roo.bootstrap.version == 3) {
10403             this.el.addClass(this.invalidClass);
10404         } else {
10405             this.inputEl().addClass('is-invalid');
10406         }
10407         
10408         // fixme ... this may be depricated need to test..
10409         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10410             
10411             var feedback = this.el.select('.form-control-feedback', true).first();
10412             
10413             if(feedback){
10414                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10415                 
10416                 if(this.getValue().length || this.forceFeedback){
10417                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10418                 }
10419                 
10420             }
10421             
10422         }
10423         
10424         this.fireEvent('invalid', this, msg);
10425     }
10426 });
10427
10428  
10429 /*
10430  * - LGPL
10431  *
10432  * trigger field - base class for combo..
10433  * 
10434  */
10435  
10436 /**
10437  * @class Roo.bootstrap.TriggerField
10438  * @extends Roo.bootstrap.Input
10439  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10440  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10441  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10442  * for which you can provide a custom implementation.  For example:
10443  * <pre><code>
10444 var trigger = new Roo.bootstrap.TriggerField();
10445 trigger.onTriggerClick = myTriggerFn;
10446 trigger.applyTo('my-field');
10447 </code></pre>
10448  *
10449  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10450  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10451  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10452  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10453  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10454
10455  * @constructor
10456  * Create a new TriggerField.
10457  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10458  * to the base TextField)
10459  */
10460 Roo.bootstrap.TriggerField = function(config){
10461     this.mimicing = false;
10462     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10463 };
10464
10465 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10466     /**
10467      * @cfg {String} triggerClass A CSS class to apply to the trigger
10468      */
10469      /**
10470      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10471      */
10472     hideTrigger:false,
10473
10474     /**
10475      * @cfg {Boolean} removable (true|false) special filter default false
10476      */
10477     removable : false,
10478     
10479     /** @cfg {Boolean} grow @hide */
10480     /** @cfg {Number} growMin @hide */
10481     /** @cfg {Number} growMax @hide */
10482
10483     /**
10484      * @hide 
10485      * @method
10486      */
10487     autoSize: Roo.emptyFn,
10488     // private
10489     monitorTab : true,
10490     // private
10491     deferHeight : true,
10492
10493     
10494     actionMode : 'wrap',
10495     
10496     caret : false,
10497     
10498     
10499     getAutoCreate : function(){
10500        
10501         var align = this.labelAlign || this.parentLabelAlign();
10502         
10503         var id = Roo.id();
10504         
10505         var cfg = {
10506             cls: 'form-group' //input-group
10507         };
10508         
10509         
10510         var input =  {
10511             tag: 'input',
10512             id : id,
10513             type : this.inputType,
10514             cls : 'form-control',
10515             autocomplete: 'new-password',
10516             placeholder : this.placeholder || '' 
10517             
10518         };
10519         if (this.name) {
10520             input.name = this.name;
10521         }
10522         if (this.size) {
10523             input.cls += ' input-' + this.size;
10524         }
10525         
10526         if (this.disabled) {
10527             input.disabled=true;
10528         }
10529         
10530         var inputblock = input;
10531         
10532         if(this.hasFeedback && !this.allowBlank){
10533             
10534             var feedback = {
10535                 tag: 'span',
10536                 cls: 'glyphicon form-control-feedback'
10537             };
10538             
10539             if(this.removable && !this.editable && !this.tickable){
10540                 inputblock = {
10541                     cls : 'has-feedback',
10542                     cn :  [
10543                         inputblock,
10544                         {
10545                             tag: 'button',
10546                             html : 'x',
10547                             cls : 'roo-combo-removable-btn close'
10548                         },
10549                         feedback
10550                     ] 
10551                 };
10552             } else {
10553                 inputblock = {
10554                     cls : 'has-feedback',
10555                     cn :  [
10556                         inputblock,
10557                         feedback
10558                     ] 
10559                 };
10560             }
10561
10562         } else {
10563             if(this.removable && !this.editable && !this.tickable){
10564                 inputblock = {
10565                     cls : 'roo-removable',
10566                     cn :  [
10567                         inputblock,
10568                         {
10569                             tag: 'button',
10570                             html : 'x',
10571                             cls : 'roo-combo-removable-btn close'
10572                         }
10573                     ] 
10574                 };
10575             }
10576         }
10577         
10578         if (this.before || this.after) {
10579             
10580             inputblock = {
10581                 cls : 'input-group',
10582                 cn :  [] 
10583             };
10584             if (this.before) {
10585                 inputblock.cn.push({
10586                     tag :'span',
10587                     cls : 'input-group-addon input-group-prepend input-group-text',
10588                     html : this.before
10589                 });
10590             }
10591             
10592             inputblock.cn.push(input);
10593             
10594             if(this.hasFeedback && !this.allowBlank){
10595                 inputblock.cls += ' has-feedback';
10596                 inputblock.cn.push(feedback);
10597             }
10598             
10599             if (this.after) {
10600                 inputblock.cn.push({
10601                     tag :'span',
10602                     cls : 'input-group-addon input-group-append input-group-text',
10603                     html : this.after
10604                 });
10605             }
10606             
10607         };
10608         
10609       
10610         
10611         var ibwrap = inputblock;
10612         
10613         if(this.multiple){
10614             ibwrap = {
10615                 tag: 'ul',
10616                 cls: 'roo-select2-choices',
10617                 cn:[
10618                     {
10619                         tag: 'li',
10620                         cls: 'roo-select2-search-field',
10621                         cn: [
10622
10623                             inputblock
10624                         ]
10625                     }
10626                 ]
10627             };
10628                 
10629         }
10630         
10631         var combobox = {
10632             cls: 'roo-select2-container input-group',
10633             cn: [
10634                  {
10635                     tag: 'input',
10636                     type : 'hidden',
10637                     cls: 'form-hidden-field'
10638                 },
10639                 ibwrap
10640             ]
10641         };
10642         
10643         if(!this.multiple && this.showToggleBtn){
10644             
10645             var caret = {
10646                         tag: 'span',
10647                         cls: 'caret'
10648              };
10649             if (this.caret != false) {
10650                 caret = {
10651                      tag: 'i',
10652                      cls: 'fa fa-' + this.caret
10653                 };
10654                 
10655             }
10656             
10657             combobox.cn.push({
10658                 tag :'span',
10659                 cls : 'input-group-addon input-group-append input-group-text btn'  +
10660                     (Roo.bootstrap.version == 3 ? ' dropdown-toggle' : ''),
10661                 cn : [
10662                     caret,
10663                     {
10664                         tag: 'span',
10665                         cls: 'combobox-clear',
10666                         cn  : [
10667                             {
10668                                 tag : 'i',
10669                                 cls: 'icon-remove'
10670                             }
10671                         ]
10672                     }
10673                 ]
10674
10675             })
10676         }
10677         
10678         if(this.multiple){
10679             combobox.cls += ' roo-select2-container-multi';
10680         }
10681          var indicator = {
10682             tag : 'i',
10683             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10684             tooltip : 'This field is required'
10685         };
10686         if (Roo.bootstrap.version == 4) {
10687             indicator = {
10688                 tag : 'i',
10689                 style : 'display:none'
10690             };
10691         }
10692         
10693         
10694         if (align ==='left' && this.fieldLabel.length) {
10695             
10696             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10697
10698             cfg.cn = [
10699                 indicator,
10700                 {
10701                     tag: 'label',
10702                     'for' :  id,
10703                     cls : 'control-label',
10704                     html : this.fieldLabel
10705
10706                 },
10707                 {
10708                     cls : "", 
10709                     cn: [
10710                         combobox
10711                     ]
10712                 }
10713
10714             ];
10715             
10716             var labelCfg = cfg.cn[1];
10717             var contentCfg = cfg.cn[2];
10718             
10719             if(this.indicatorpos == 'right'){
10720                 cfg.cn = [
10721                     {
10722                         tag: 'label',
10723                         'for' :  id,
10724                         cls : 'control-label',
10725                         cn : [
10726                             {
10727                                 tag : 'span',
10728                                 html : this.fieldLabel
10729                             },
10730                             indicator
10731                         ]
10732                     },
10733                     {
10734                         cls : "", 
10735                         cn: [
10736                             combobox
10737                         ]
10738                     }
10739
10740                 ];
10741                 
10742                 labelCfg = cfg.cn[0];
10743                 contentCfg = cfg.cn[1];
10744             }
10745             
10746             if(this.labelWidth > 12){
10747                 labelCfg.style = "width: " + this.labelWidth + 'px';
10748             }
10749             
10750             if(this.labelWidth < 13 && this.labelmd == 0){
10751                 this.labelmd = this.labelWidth;
10752             }
10753             
10754             if(this.labellg > 0){
10755                 labelCfg.cls += ' col-lg-' + this.labellg;
10756                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10757             }
10758             
10759             if(this.labelmd > 0){
10760                 labelCfg.cls += ' col-md-' + this.labelmd;
10761                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10762             }
10763             
10764             if(this.labelsm > 0){
10765                 labelCfg.cls += ' col-sm-' + this.labelsm;
10766                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10767             }
10768             
10769             if(this.labelxs > 0){
10770                 labelCfg.cls += ' col-xs-' + this.labelxs;
10771                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10772             }
10773             
10774         } else if ( this.fieldLabel.length) {
10775 //                Roo.log(" label");
10776             cfg.cn = [
10777                 indicator,
10778                {
10779                    tag: 'label',
10780                    //cls : 'input-group-addon',
10781                    html : this.fieldLabel
10782
10783                },
10784
10785                combobox
10786
10787             ];
10788             
10789             if(this.indicatorpos == 'right'){
10790                 
10791                 cfg.cn = [
10792                     {
10793                        tag: 'label',
10794                        cn : [
10795                            {
10796                                tag : 'span',
10797                                html : this.fieldLabel
10798                            },
10799                            indicator
10800                        ]
10801
10802                     },
10803                     combobox
10804
10805                 ];
10806
10807             }
10808
10809         } else {
10810             
10811 //                Roo.log(" no label && no align");
10812                 cfg = combobox
10813                      
10814                 
10815         }
10816         
10817         var settings=this;
10818         ['xs','sm','md','lg'].map(function(size){
10819             if (settings[size]) {
10820                 cfg.cls += ' col-' + size + '-' + settings[size];
10821             }
10822         });
10823         
10824         return cfg;
10825         
10826     },
10827     
10828     
10829     
10830     // private
10831     onResize : function(w, h){
10832 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10833 //        if(typeof w == 'number'){
10834 //            var x = w - this.trigger.getWidth();
10835 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10836 //            this.trigger.setStyle('left', x+'px');
10837 //        }
10838     },
10839
10840     // private
10841     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10842
10843     // private
10844     getResizeEl : function(){
10845         return this.inputEl();
10846     },
10847
10848     // private
10849     getPositionEl : function(){
10850         return this.inputEl();
10851     },
10852
10853     // private
10854     alignErrorIcon : function(){
10855         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10856     },
10857
10858     // private
10859     initEvents : function(){
10860         
10861         this.createList();
10862         
10863         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10864         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10865         if(!this.multiple && this.showToggleBtn){
10866             this.trigger = this.el.select('span.input-group-append',true).first();
10867             if(this.hideTrigger){
10868                 this.trigger.setDisplayed(false);
10869             }
10870             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10871         }
10872         
10873         if(this.multiple){
10874             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10875         }
10876         
10877         if(this.removable && !this.editable && !this.tickable){
10878             var close = this.closeTriggerEl();
10879             
10880             if(close){
10881                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10882                 close.on('click', this.removeBtnClick, this, close);
10883             }
10884         }
10885         
10886         //this.trigger.addClassOnOver('x-form-trigger-over');
10887         //this.trigger.addClassOnClick('x-form-trigger-click');
10888         
10889         //if(!this.width){
10890         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10891         //}
10892     },
10893     
10894     closeTriggerEl : function()
10895     {
10896         var close = this.el.select('.roo-combo-removable-btn', true).first();
10897         return close ? close : false;
10898     },
10899     
10900     removeBtnClick : function(e, h, el)
10901     {
10902         e.preventDefault();
10903         
10904         if(this.fireEvent("remove", this) !== false){
10905             this.reset();
10906             this.fireEvent("afterremove", this)
10907         }
10908     },
10909     
10910     createList : function()
10911     {
10912         this.list = Roo.get(document.body).createChild({
10913             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10914             cls: 'typeahead typeahead-long dropdown-menu',
10915             style: 'display:none'
10916         });
10917         
10918         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10919         
10920     },
10921
10922     // private
10923     initTrigger : function(){
10924        
10925     },
10926
10927     // private
10928     onDestroy : function(){
10929         if(this.trigger){
10930             this.trigger.removeAllListeners();
10931           //  this.trigger.remove();
10932         }
10933         //if(this.wrap){
10934         //    this.wrap.remove();
10935         //}
10936         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10937     },
10938
10939     // private
10940     onFocus : function(){
10941         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10942         /*
10943         if(!this.mimicing){
10944             this.wrap.addClass('x-trigger-wrap-focus');
10945             this.mimicing = true;
10946             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10947             if(this.monitorTab){
10948                 this.el.on("keydown", this.checkTab, this);
10949             }
10950         }
10951         */
10952     },
10953
10954     // private
10955     checkTab : function(e){
10956         if(e.getKey() == e.TAB){
10957             this.triggerBlur();
10958         }
10959     },
10960
10961     // private
10962     onBlur : function(){
10963         // do nothing
10964     },
10965
10966     // private
10967     mimicBlur : function(e, t){
10968         /*
10969         if(!this.wrap.contains(t) && this.validateBlur()){
10970             this.triggerBlur();
10971         }
10972         */
10973     },
10974
10975     // private
10976     triggerBlur : function(){
10977         this.mimicing = false;
10978         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10979         if(this.monitorTab){
10980             this.el.un("keydown", this.checkTab, this);
10981         }
10982         //this.wrap.removeClass('x-trigger-wrap-focus');
10983         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10984     },
10985
10986     // private
10987     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10988     validateBlur : function(e, t){
10989         return true;
10990     },
10991
10992     // private
10993     onDisable : function(){
10994         this.inputEl().dom.disabled = true;
10995         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10996         //if(this.wrap){
10997         //    this.wrap.addClass('x-item-disabled');
10998         //}
10999     },
11000
11001     // private
11002     onEnable : function(){
11003         this.inputEl().dom.disabled = false;
11004         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
11005         //if(this.wrap){
11006         //    this.el.removeClass('x-item-disabled');
11007         //}
11008     },
11009
11010     // private
11011     onShow : function(){
11012         var ae = this.getActionEl();
11013         
11014         if(ae){
11015             ae.dom.style.display = '';
11016             ae.dom.style.visibility = 'visible';
11017         }
11018     },
11019
11020     // private
11021     
11022     onHide : function(){
11023         var ae = this.getActionEl();
11024         ae.dom.style.display = 'none';
11025     },
11026
11027     /**
11028      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
11029      * by an implementing function.
11030      * @method
11031      * @param {EventObject} e
11032      */
11033     onTriggerClick : Roo.emptyFn
11034 });
11035  /*
11036  * Based on:
11037  * Ext JS Library 1.1.1
11038  * Copyright(c) 2006-2007, Ext JS, LLC.
11039  *
11040  * Originally Released Under LGPL - original licence link has changed is not relivant.
11041  *
11042  * Fork - LGPL
11043  * <script type="text/javascript">
11044  */
11045
11046
11047 /**
11048  * @class Roo.data.SortTypes
11049  * @singleton
11050  * Defines the default sorting (casting?) comparison functions used when sorting data.
11051  */
11052 Roo.data.SortTypes = {
11053     /**
11054      * Default sort that does nothing
11055      * @param {Mixed} s The value being converted
11056      * @return {Mixed} The comparison value
11057      */
11058     none : function(s){
11059         return s;
11060     },
11061     
11062     /**
11063      * The regular expression used to strip tags
11064      * @type {RegExp}
11065      * @property
11066      */
11067     stripTagsRE : /<\/?[^>]+>/gi,
11068     
11069     /**
11070      * Strips all HTML tags to sort on text only
11071      * @param {Mixed} s The value being converted
11072      * @return {String} The comparison value
11073      */
11074     asText : function(s){
11075         return String(s).replace(this.stripTagsRE, "");
11076     },
11077     
11078     /**
11079      * Strips all HTML tags to sort on text only - Case insensitive
11080      * @param {Mixed} s The value being converted
11081      * @return {String} The comparison value
11082      */
11083     asUCText : function(s){
11084         return String(s).toUpperCase().replace(this.stripTagsRE, "");
11085     },
11086     
11087     /**
11088      * Case insensitive string
11089      * @param {Mixed} s The value being converted
11090      * @return {String} The comparison value
11091      */
11092     asUCString : function(s) {
11093         return String(s).toUpperCase();
11094     },
11095     
11096     /**
11097      * Date sorting
11098      * @param {Mixed} s The value being converted
11099      * @return {Number} The comparison value
11100      */
11101     asDate : function(s) {
11102         if(!s){
11103             return 0;
11104         }
11105         if(s instanceof Date){
11106             return s.getTime();
11107         }
11108         return Date.parse(String(s));
11109     },
11110     
11111     /**
11112      * Float sorting
11113      * @param {Mixed} s The value being converted
11114      * @return {Float} The comparison value
11115      */
11116     asFloat : function(s) {
11117         var val = parseFloat(String(s).replace(/,/g, ""));
11118         if(isNaN(val)) {
11119             val = 0;
11120         }
11121         return val;
11122     },
11123     
11124     /**
11125      * Integer sorting
11126      * @param {Mixed} s The value being converted
11127      * @return {Number} The comparison value
11128      */
11129     asInt : function(s) {
11130         var val = parseInt(String(s).replace(/,/g, ""));
11131         if(isNaN(val)) {
11132             val = 0;
11133         }
11134         return val;
11135     }
11136 };/*
11137  * Based on:
11138  * Ext JS Library 1.1.1
11139  * Copyright(c) 2006-2007, Ext JS, LLC.
11140  *
11141  * Originally Released Under LGPL - original licence link has changed is not relivant.
11142  *
11143  * Fork - LGPL
11144  * <script type="text/javascript">
11145  */
11146
11147 /**
11148 * @class Roo.data.Record
11149  * Instances of this class encapsulate both record <em>definition</em> information, and record
11150  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
11151  * to access Records cached in an {@link Roo.data.Store} object.<br>
11152  * <p>
11153  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
11154  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
11155  * objects.<br>
11156  * <p>
11157  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
11158  * @constructor
11159  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
11160  * {@link #create}. The parameters are the same.
11161  * @param {Array} data An associative Array of data values keyed by the field name.
11162  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
11163  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
11164  * not specified an integer id is generated.
11165  */
11166 Roo.data.Record = function(data, id){
11167     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11168     this.data = data;
11169 };
11170
11171 /**
11172  * Generate a constructor for a specific record layout.
11173  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11174  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11175  * Each field definition object may contain the following properties: <ul>
11176  * <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,
11177  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11178  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11179  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11180  * is being used, then this is a string containing the javascript expression to reference the data relative to 
11181  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11182  * to the data item relative to the record element. If the mapping expression is the same as the field name,
11183  * this may be omitted.</p></li>
11184  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11185  * <ul><li>auto (Default, implies no conversion)</li>
11186  * <li>string</li>
11187  * <li>int</li>
11188  * <li>float</li>
11189  * <li>boolean</li>
11190  * <li>date</li></ul></p></li>
11191  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11192  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11193  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11194  * by the Reader into an object that will be stored in the Record. It is passed the
11195  * following parameters:<ul>
11196  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11197  * </ul></p></li>
11198  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11199  * </ul>
11200  * <br>usage:<br><pre><code>
11201 var TopicRecord = Roo.data.Record.create(
11202     {name: 'title', mapping: 'topic_title'},
11203     {name: 'author', mapping: 'username'},
11204     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11205     {name: 'lastPost', mapping: 'post_time', type: 'date'},
11206     {name: 'lastPoster', mapping: 'user2'},
11207     {name: 'excerpt', mapping: 'post_text'}
11208 );
11209
11210 var myNewRecord = new TopicRecord({
11211     title: 'Do my job please',
11212     author: 'noobie',
11213     totalPosts: 1,
11214     lastPost: new Date(),
11215     lastPoster: 'Animal',
11216     excerpt: 'No way dude!'
11217 });
11218 myStore.add(myNewRecord);
11219 </code></pre>
11220  * @method create
11221  * @static
11222  */
11223 Roo.data.Record.create = function(o){
11224     var f = function(){
11225         f.superclass.constructor.apply(this, arguments);
11226     };
11227     Roo.extend(f, Roo.data.Record);
11228     var p = f.prototype;
11229     p.fields = new Roo.util.MixedCollection(false, function(field){
11230         return field.name;
11231     });
11232     for(var i = 0, len = o.length; i < len; i++){
11233         p.fields.add(new Roo.data.Field(o[i]));
11234     }
11235     f.getField = function(name){
11236         return p.fields.get(name);  
11237     };
11238     return f;
11239 };
11240
11241 Roo.data.Record.AUTO_ID = 1000;
11242 Roo.data.Record.EDIT = 'edit';
11243 Roo.data.Record.REJECT = 'reject';
11244 Roo.data.Record.COMMIT = 'commit';
11245
11246 Roo.data.Record.prototype = {
11247     /**
11248      * Readonly flag - true if this record has been modified.
11249      * @type Boolean
11250      */
11251     dirty : false,
11252     editing : false,
11253     error: null,
11254     modified: null,
11255
11256     // private
11257     join : function(store){
11258         this.store = store;
11259     },
11260
11261     /**
11262      * Set the named field to the specified value.
11263      * @param {String} name The name of the field to set.
11264      * @param {Object} value The value to set the field to.
11265      */
11266     set : function(name, value){
11267         if(this.data[name] == value){
11268             return;
11269         }
11270         this.dirty = true;
11271         if(!this.modified){
11272             this.modified = {};
11273         }
11274         if(typeof this.modified[name] == 'undefined'){
11275             this.modified[name] = this.data[name];
11276         }
11277         this.data[name] = value;
11278         if(!this.editing && this.store){
11279             this.store.afterEdit(this);
11280         }       
11281     },
11282
11283     /**
11284      * Get the value of the named field.
11285      * @param {String} name The name of the field to get the value of.
11286      * @return {Object} The value of the field.
11287      */
11288     get : function(name){
11289         return this.data[name]; 
11290     },
11291
11292     // private
11293     beginEdit : function(){
11294         this.editing = true;
11295         this.modified = {}; 
11296     },
11297
11298     // private
11299     cancelEdit : function(){
11300         this.editing = false;
11301         delete this.modified;
11302     },
11303
11304     // private
11305     endEdit : function(){
11306         this.editing = false;
11307         if(this.dirty && this.store){
11308             this.store.afterEdit(this);
11309         }
11310     },
11311
11312     /**
11313      * Usually called by the {@link Roo.data.Store} which owns the Record.
11314      * Rejects all changes made to the Record since either creation, or the last commit operation.
11315      * Modified fields are reverted to their original values.
11316      * <p>
11317      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11318      * of reject operations.
11319      */
11320     reject : function(){
11321         var m = this.modified;
11322         for(var n in m){
11323             if(typeof m[n] != "function"){
11324                 this.data[n] = m[n];
11325             }
11326         }
11327         this.dirty = false;
11328         delete this.modified;
11329         this.editing = false;
11330         if(this.store){
11331             this.store.afterReject(this);
11332         }
11333     },
11334
11335     /**
11336      * Usually called by the {@link Roo.data.Store} which owns the Record.
11337      * Commits all changes made to the Record since either creation, or the last commit operation.
11338      * <p>
11339      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11340      * of commit operations.
11341      */
11342     commit : function(){
11343         this.dirty = false;
11344         delete this.modified;
11345         this.editing = false;
11346         if(this.store){
11347             this.store.afterCommit(this);
11348         }
11349     },
11350
11351     // private
11352     hasError : function(){
11353         return this.error != null;
11354     },
11355
11356     // private
11357     clearError : function(){
11358         this.error = null;
11359     },
11360
11361     /**
11362      * Creates a copy of this record.
11363      * @param {String} id (optional) A new record id if you don't want to use this record's id
11364      * @return {Record}
11365      */
11366     copy : function(newId) {
11367         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11368     }
11369 };/*
11370  * Based on:
11371  * Ext JS Library 1.1.1
11372  * Copyright(c) 2006-2007, Ext JS, LLC.
11373  *
11374  * Originally Released Under LGPL - original licence link has changed is not relivant.
11375  *
11376  * Fork - LGPL
11377  * <script type="text/javascript">
11378  */
11379
11380
11381
11382 /**
11383  * @class Roo.data.Store
11384  * @extends Roo.util.Observable
11385  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11386  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11387  * <p>
11388  * 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
11389  * has no knowledge of the format of the data returned by the Proxy.<br>
11390  * <p>
11391  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11392  * instances from the data object. These records are cached and made available through accessor functions.
11393  * @constructor
11394  * Creates a new Store.
11395  * @param {Object} config A config object containing the objects needed for the Store to access data,
11396  * and read the data into Records.
11397  */
11398 Roo.data.Store = function(config){
11399     this.data = new Roo.util.MixedCollection(false);
11400     this.data.getKey = function(o){
11401         return o.id;
11402     };
11403     this.baseParams = {};
11404     // private
11405     this.paramNames = {
11406         "start" : "start",
11407         "limit" : "limit",
11408         "sort" : "sort",
11409         "dir" : "dir",
11410         "multisort" : "_multisort"
11411     };
11412
11413     if(config && config.data){
11414         this.inlineData = config.data;
11415         delete config.data;
11416     }
11417
11418     Roo.apply(this, config);
11419     
11420     if(this.reader){ // reader passed
11421         this.reader = Roo.factory(this.reader, Roo.data);
11422         this.reader.xmodule = this.xmodule || false;
11423         if(!this.recordType){
11424             this.recordType = this.reader.recordType;
11425         }
11426         if(this.reader.onMetaChange){
11427             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11428         }
11429     }
11430
11431     if(this.recordType){
11432         this.fields = this.recordType.prototype.fields;
11433     }
11434     this.modified = [];
11435
11436     this.addEvents({
11437         /**
11438          * @event datachanged
11439          * Fires when the data cache has changed, and a widget which is using this Store
11440          * as a Record cache should refresh its view.
11441          * @param {Store} this
11442          */
11443         datachanged : true,
11444         /**
11445          * @event metachange
11446          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11447          * @param {Store} this
11448          * @param {Object} meta The JSON metadata
11449          */
11450         metachange : true,
11451         /**
11452          * @event add
11453          * Fires when Records have been added to the Store
11454          * @param {Store} this
11455          * @param {Roo.data.Record[]} records The array of Records added
11456          * @param {Number} index The index at which the record(s) were added
11457          */
11458         add : true,
11459         /**
11460          * @event remove
11461          * Fires when a Record has been removed from the Store
11462          * @param {Store} this
11463          * @param {Roo.data.Record} record The Record that was removed
11464          * @param {Number} index The index at which the record was removed
11465          */
11466         remove : true,
11467         /**
11468          * @event update
11469          * Fires when a Record has been updated
11470          * @param {Store} this
11471          * @param {Roo.data.Record} record The Record that was updated
11472          * @param {String} operation The update operation being performed.  Value may be one of:
11473          * <pre><code>
11474  Roo.data.Record.EDIT
11475  Roo.data.Record.REJECT
11476  Roo.data.Record.COMMIT
11477          * </code></pre>
11478          */
11479         update : true,
11480         /**
11481          * @event clear
11482          * Fires when the data cache has been cleared.
11483          * @param {Store} this
11484          */
11485         clear : true,
11486         /**
11487          * @event beforeload
11488          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11489          * the load action will be canceled.
11490          * @param {Store} this
11491          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11492          */
11493         beforeload : true,
11494         /**
11495          * @event beforeloadadd
11496          * Fires after a new set of Records has been loaded.
11497          * @param {Store} this
11498          * @param {Roo.data.Record[]} records The Records that were loaded
11499          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11500          */
11501         beforeloadadd : true,
11502         /**
11503          * @event load
11504          * Fires after a new set of Records has been loaded, before they are added to the store.
11505          * @param {Store} this
11506          * @param {Roo.data.Record[]} records The Records that were loaded
11507          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11508          * @params {Object} return from reader
11509          */
11510         load : true,
11511         /**
11512          * @event loadexception
11513          * Fires if an exception occurs in the Proxy during loading.
11514          * Called with the signature of the Proxy's "loadexception" event.
11515          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11516          * 
11517          * @param {Proxy} 
11518          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11519          * @param {Object} load options 
11520          * @param {Object} jsonData from your request (normally this contains the Exception)
11521          */
11522         loadexception : true
11523     });
11524     
11525     if(this.proxy){
11526         this.proxy = Roo.factory(this.proxy, Roo.data);
11527         this.proxy.xmodule = this.xmodule || false;
11528         this.relayEvents(this.proxy,  ["loadexception"]);
11529     }
11530     this.sortToggle = {};
11531     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11532
11533     Roo.data.Store.superclass.constructor.call(this);
11534
11535     if(this.inlineData){
11536         this.loadData(this.inlineData);
11537         delete this.inlineData;
11538     }
11539 };
11540
11541 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11542      /**
11543     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11544     * without a remote query - used by combo/forms at present.
11545     */
11546     
11547     /**
11548     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11549     */
11550     /**
11551     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11552     */
11553     /**
11554     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11555     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11556     */
11557     /**
11558     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11559     * on any HTTP request
11560     */
11561     /**
11562     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11563     */
11564     /**
11565     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11566     */
11567     multiSort: false,
11568     /**
11569     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11570     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11571     */
11572     remoteSort : false,
11573
11574     /**
11575     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11576      * loaded or when a record is removed. (defaults to false).
11577     */
11578     pruneModifiedRecords : false,
11579
11580     // private
11581     lastOptions : null,
11582
11583     /**
11584      * Add Records to the Store and fires the add event.
11585      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11586      */
11587     add : function(records){
11588         records = [].concat(records);
11589         for(var i = 0, len = records.length; i < len; i++){
11590             records[i].join(this);
11591         }
11592         var index = this.data.length;
11593         this.data.addAll(records);
11594         this.fireEvent("add", this, records, index);
11595     },
11596
11597     /**
11598      * Remove a Record from the Store and fires the remove event.
11599      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11600      */
11601     remove : function(record){
11602         var index = this.data.indexOf(record);
11603         this.data.removeAt(index);
11604  
11605         if(this.pruneModifiedRecords){
11606             this.modified.remove(record);
11607         }
11608         this.fireEvent("remove", this, record, index);
11609     },
11610
11611     /**
11612      * Remove all Records from the Store and fires the clear event.
11613      */
11614     removeAll : function(){
11615         this.data.clear();
11616         if(this.pruneModifiedRecords){
11617             this.modified = [];
11618         }
11619         this.fireEvent("clear", this);
11620     },
11621
11622     /**
11623      * Inserts Records to the Store at the given index and fires the add event.
11624      * @param {Number} index The start index at which to insert the passed Records.
11625      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11626      */
11627     insert : function(index, records){
11628         records = [].concat(records);
11629         for(var i = 0, len = records.length; i < len; i++){
11630             this.data.insert(index, records[i]);
11631             records[i].join(this);
11632         }
11633         this.fireEvent("add", this, records, index);
11634     },
11635
11636     /**
11637      * Get the index within the cache of the passed Record.
11638      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11639      * @return {Number} The index of the passed Record. Returns -1 if not found.
11640      */
11641     indexOf : function(record){
11642         return this.data.indexOf(record);
11643     },
11644
11645     /**
11646      * Get the index within the cache of the Record with the passed id.
11647      * @param {String} id The id of the Record to find.
11648      * @return {Number} The index of the Record. Returns -1 if not found.
11649      */
11650     indexOfId : function(id){
11651         return this.data.indexOfKey(id);
11652     },
11653
11654     /**
11655      * Get the Record with the specified id.
11656      * @param {String} id The id of the Record to find.
11657      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11658      */
11659     getById : function(id){
11660         return this.data.key(id);
11661     },
11662
11663     /**
11664      * Get the Record at the specified index.
11665      * @param {Number} index The index of the Record to find.
11666      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11667      */
11668     getAt : function(index){
11669         return this.data.itemAt(index);
11670     },
11671
11672     /**
11673      * Returns a range of Records between specified indices.
11674      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11675      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11676      * @return {Roo.data.Record[]} An array of Records
11677      */
11678     getRange : function(start, end){
11679         return this.data.getRange(start, end);
11680     },
11681
11682     // private
11683     storeOptions : function(o){
11684         o = Roo.apply({}, o);
11685         delete o.callback;
11686         delete o.scope;
11687         this.lastOptions = o;
11688     },
11689
11690     /**
11691      * Loads the Record cache from the configured Proxy using the configured Reader.
11692      * <p>
11693      * If using remote paging, then the first load call must specify the <em>start</em>
11694      * and <em>limit</em> properties in the options.params property to establish the initial
11695      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11696      * <p>
11697      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11698      * and this call will return before the new data has been loaded. Perform any post-processing
11699      * in a callback function, or in a "load" event handler.</strong>
11700      * <p>
11701      * @param {Object} options An object containing properties which control loading options:<ul>
11702      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11703      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11704      * passed the following arguments:<ul>
11705      * <li>r : Roo.data.Record[]</li>
11706      * <li>options: Options object from the load call</li>
11707      * <li>success: Boolean success indicator</li></ul></li>
11708      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11709      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11710      * </ul>
11711      */
11712     load : function(options){
11713         options = options || {};
11714         if(this.fireEvent("beforeload", this, options) !== false){
11715             this.storeOptions(options);
11716             var p = Roo.apply(options.params || {}, this.baseParams);
11717             // if meta was not loaded from remote source.. try requesting it.
11718             if (!this.reader.metaFromRemote) {
11719                 p._requestMeta = 1;
11720             }
11721             if(this.sortInfo && this.remoteSort){
11722                 var pn = this.paramNames;
11723                 p[pn["sort"]] = this.sortInfo.field;
11724                 p[pn["dir"]] = this.sortInfo.direction;
11725             }
11726             if (this.multiSort) {
11727                 var pn = this.paramNames;
11728                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11729             }
11730             
11731             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11732         }
11733     },
11734
11735     /**
11736      * Reloads the Record cache from the configured Proxy using the configured Reader and
11737      * the options from the last load operation performed.
11738      * @param {Object} options (optional) An object containing properties which may override the options
11739      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11740      * the most recently used options are reused).
11741      */
11742     reload : function(options){
11743         this.load(Roo.applyIf(options||{}, this.lastOptions));
11744     },
11745
11746     // private
11747     // Called as a callback by the Reader during a load operation.
11748     loadRecords : function(o, options, success){
11749         if(!o || success === false){
11750             if(success !== false){
11751                 this.fireEvent("load", this, [], options, o);
11752             }
11753             if(options.callback){
11754                 options.callback.call(options.scope || this, [], options, false);
11755             }
11756             return;
11757         }
11758         // if data returned failure - throw an exception.
11759         if (o.success === false) {
11760             // show a message if no listener is registered.
11761             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11762                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11763             }
11764             // loadmask wil be hooked into this..
11765             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11766             return;
11767         }
11768         var r = o.records, t = o.totalRecords || r.length;
11769         
11770         this.fireEvent("beforeloadadd", this, r, options, o);
11771         
11772         if(!options || options.add !== true){
11773             if(this.pruneModifiedRecords){
11774                 this.modified = [];
11775             }
11776             for(var i = 0, len = r.length; i < len; i++){
11777                 r[i].join(this);
11778             }
11779             if(this.snapshot){
11780                 this.data = this.snapshot;
11781                 delete this.snapshot;
11782             }
11783             this.data.clear();
11784             this.data.addAll(r);
11785             this.totalLength = t;
11786             this.applySort();
11787             this.fireEvent("datachanged", this);
11788         }else{
11789             this.totalLength = Math.max(t, this.data.length+r.length);
11790             this.add(r);
11791         }
11792         
11793         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11794                 
11795             var e = new Roo.data.Record({});
11796
11797             e.set(this.parent.displayField, this.parent.emptyTitle);
11798             e.set(this.parent.valueField, '');
11799
11800             this.insert(0, e);
11801         }
11802             
11803         this.fireEvent("load", this, r, options, o);
11804         if(options.callback){
11805             options.callback.call(options.scope || this, r, options, true);
11806         }
11807     },
11808
11809
11810     /**
11811      * Loads data from a passed data block. A Reader which understands the format of the data
11812      * must have been configured in the constructor.
11813      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11814      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11815      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11816      */
11817     loadData : function(o, append){
11818         var r = this.reader.readRecords(o);
11819         this.loadRecords(r, {add: append}, true);
11820     },
11821
11822     /**
11823      * Gets the number of cached records.
11824      * <p>
11825      * <em>If using paging, this may not be the total size of the dataset. If the data object
11826      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11827      * the data set size</em>
11828      */
11829     getCount : function(){
11830         return this.data.length || 0;
11831     },
11832
11833     /**
11834      * Gets the total number of records in the dataset as returned by the server.
11835      * <p>
11836      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11837      * the dataset size</em>
11838      */
11839     getTotalCount : function(){
11840         return this.totalLength || 0;
11841     },
11842
11843     /**
11844      * Returns the sort state of the Store as an object with two properties:
11845      * <pre><code>
11846  field {String} The name of the field by which the Records are sorted
11847  direction {String} The sort order, "ASC" or "DESC"
11848      * </code></pre>
11849      */
11850     getSortState : function(){
11851         return this.sortInfo;
11852     },
11853
11854     // private
11855     applySort : function(){
11856         if(this.sortInfo && !this.remoteSort){
11857             var s = this.sortInfo, f = s.field;
11858             var st = this.fields.get(f).sortType;
11859             var fn = function(r1, r2){
11860                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11861                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11862             };
11863             this.data.sort(s.direction, fn);
11864             if(this.snapshot && this.snapshot != this.data){
11865                 this.snapshot.sort(s.direction, fn);
11866             }
11867         }
11868     },
11869
11870     /**
11871      * Sets the default sort column and order to be used by the next load operation.
11872      * @param {String} fieldName The name of the field to sort by.
11873      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11874      */
11875     setDefaultSort : function(field, dir){
11876         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11877     },
11878
11879     /**
11880      * Sort the Records.
11881      * If remote sorting is used, the sort is performed on the server, and the cache is
11882      * reloaded. If local sorting is used, the cache is sorted internally.
11883      * @param {String} fieldName The name of the field to sort by.
11884      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11885      */
11886     sort : function(fieldName, dir){
11887         var f = this.fields.get(fieldName);
11888         if(!dir){
11889             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11890             
11891             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11892                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11893             }else{
11894                 dir = f.sortDir;
11895             }
11896         }
11897         this.sortToggle[f.name] = dir;
11898         this.sortInfo = {field: f.name, direction: dir};
11899         if(!this.remoteSort){
11900             this.applySort();
11901             this.fireEvent("datachanged", this);
11902         }else{
11903             this.load(this.lastOptions);
11904         }
11905     },
11906
11907     /**
11908      * Calls the specified function for each of the Records in the cache.
11909      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11910      * Returning <em>false</em> aborts and exits the iteration.
11911      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11912      */
11913     each : function(fn, scope){
11914         this.data.each(fn, scope);
11915     },
11916
11917     /**
11918      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11919      * (e.g., during paging).
11920      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11921      */
11922     getModifiedRecords : function(){
11923         return this.modified;
11924     },
11925
11926     // private
11927     createFilterFn : function(property, value, anyMatch){
11928         if(!value.exec){ // not a regex
11929             value = String(value);
11930             if(value.length == 0){
11931                 return false;
11932             }
11933             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11934         }
11935         return function(r){
11936             return value.test(r.data[property]);
11937         };
11938     },
11939
11940     /**
11941      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11942      * @param {String} property A field on your records
11943      * @param {Number} start The record index to start at (defaults to 0)
11944      * @param {Number} end The last record index to include (defaults to length - 1)
11945      * @return {Number} The sum
11946      */
11947     sum : function(property, start, end){
11948         var rs = this.data.items, v = 0;
11949         start = start || 0;
11950         end = (end || end === 0) ? end : rs.length-1;
11951
11952         for(var i = start; i <= end; i++){
11953             v += (rs[i].data[property] || 0);
11954         }
11955         return v;
11956     },
11957
11958     /**
11959      * Filter the records by a specified property.
11960      * @param {String} field A field on your records
11961      * @param {String/RegExp} value Either a string that the field
11962      * should start with or a RegExp to test against the field
11963      * @param {Boolean} anyMatch True to match any part not just the beginning
11964      */
11965     filter : function(property, value, anyMatch){
11966         var fn = this.createFilterFn(property, value, anyMatch);
11967         return fn ? this.filterBy(fn) : this.clearFilter();
11968     },
11969
11970     /**
11971      * Filter by a function. The specified function will be called with each
11972      * record in this data source. If the function returns true the record is included,
11973      * otherwise it is filtered.
11974      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11975      * @param {Object} scope (optional) The scope of the function (defaults to this)
11976      */
11977     filterBy : function(fn, scope){
11978         this.snapshot = this.snapshot || this.data;
11979         this.data = this.queryBy(fn, scope||this);
11980         this.fireEvent("datachanged", this);
11981     },
11982
11983     /**
11984      * Query the records by a specified property.
11985      * @param {String} field A field on your records
11986      * @param {String/RegExp} value Either a string that the field
11987      * should start with or a RegExp to test against the field
11988      * @param {Boolean} anyMatch True to match any part not just the beginning
11989      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11990      */
11991     query : function(property, value, anyMatch){
11992         var fn = this.createFilterFn(property, value, anyMatch);
11993         return fn ? this.queryBy(fn) : this.data.clone();
11994     },
11995
11996     /**
11997      * Query by a function. The specified function will be called with each
11998      * record in this data source. If the function returns true the record is included
11999      * in the results.
12000      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
12001      * @param {Object} scope (optional) The scope of the function (defaults to this)
12002       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12003      **/
12004     queryBy : function(fn, scope){
12005         var data = this.snapshot || this.data;
12006         return data.filterBy(fn, scope||this);
12007     },
12008
12009     /**
12010      * Collects unique values for a particular dataIndex from this store.
12011      * @param {String} dataIndex The property to collect
12012      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
12013      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
12014      * @return {Array} An array of the unique values
12015      **/
12016     collect : function(dataIndex, allowNull, bypassFilter){
12017         var d = (bypassFilter === true && this.snapshot) ?
12018                 this.snapshot.items : this.data.items;
12019         var v, sv, r = [], l = {};
12020         for(var i = 0, len = d.length; i < len; i++){
12021             v = d[i].data[dataIndex];
12022             sv = String(v);
12023             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
12024                 l[sv] = true;
12025                 r[r.length] = v;
12026             }
12027         }
12028         return r;
12029     },
12030
12031     /**
12032      * Revert to a view of the Record cache with no filtering applied.
12033      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
12034      */
12035     clearFilter : function(suppressEvent){
12036         if(this.snapshot && this.snapshot != this.data){
12037             this.data = this.snapshot;
12038             delete this.snapshot;
12039             if(suppressEvent !== true){
12040                 this.fireEvent("datachanged", this);
12041             }
12042         }
12043     },
12044
12045     // private
12046     afterEdit : function(record){
12047         if(this.modified.indexOf(record) == -1){
12048             this.modified.push(record);
12049         }
12050         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
12051     },
12052     
12053     // private
12054     afterReject : function(record){
12055         this.modified.remove(record);
12056         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
12057     },
12058
12059     // private
12060     afterCommit : function(record){
12061         this.modified.remove(record);
12062         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
12063     },
12064
12065     /**
12066      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
12067      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
12068      */
12069     commitChanges : function(){
12070         var m = this.modified.slice(0);
12071         this.modified = [];
12072         for(var i = 0, len = m.length; i < len; i++){
12073             m[i].commit();
12074         }
12075     },
12076
12077     /**
12078      * Cancel outstanding changes on all changed records.
12079      */
12080     rejectChanges : function(){
12081         var m = this.modified.slice(0);
12082         this.modified = [];
12083         for(var i = 0, len = m.length; i < len; i++){
12084             m[i].reject();
12085         }
12086     },
12087
12088     onMetaChange : function(meta, rtype, o){
12089         this.recordType = rtype;
12090         this.fields = rtype.prototype.fields;
12091         delete this.snapshot;
12092         this.sortInfo = meta.sortInfo || this.sortInfo;
12093         this.modified = [];
12094         this.fireEvent('metachange', this, this.reader.meta);
12095     },
12096     
12097     moveIndex : function(data, type)
12098     {
12099         var index = this.indexOf(data);
12100         
12101         var newIndex = index + type;
12102         
12103         this.remove(data);
12104         
12105         this.insert(newIndex, data);
12106         
12107     }
12108 });/*
12109  * Based on:
12110  * Ext JS Library 1.1.1
12111  * Copyright(c) 2006-2007, Ext JS, LLC.
12112  *
12113  * Originally Released Under LGPL - original licence link has changed is not relivant.
12114  *
12115  * Fork - LGPL
12116  * <script type="text/javascript">
12117  */
12118
12119 /**
12120  * @class Roo.data.SimpleStore
12121  * @extends Roo.data.Store
12122  * Small helper class to make creating Stores from Array data easier.
12123  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
12124  * @cfg {Array} fields An array of field definition objects, or field name strings.
12125  * @cfg {Array} data The multi-dimensional array of data
12126  * @constructor
12127  * @param {Object} config
12128  */
12129 Roo.data.SimpleStore = function(config){
12130     Roo.data.SimpleStore.superclass.constructor.call(this, {
12131         isLocal : true,
12132         reader: new Roo.data.ArrayReader({
12133                 id: config.id
12134             },
12135             Roo.data.Record.create(config.fields)
12136         ),
12137         proxy : new Roo.data.MemoryProxy(config.data)
12138     });
12139     this.load();
12140 };
12141 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
12142  * Based on:
12143  * Ext JS Library 1.1.1
12144  * Copyright(c) 2006-2007, Ext JS, LLC.
12145  *
12146  * Originally Released Under LGPL - original licence link has changed is not relivant.
12147  *
12148  * Fork - LGPL
12149  * <script type="text/javascript">
12150  */
12151
12152 /**
12153 /**
12154  * @extends Roo.data.Store
12155  * @class Roo.data.JsonStore
12156  * Small helper class to make creating Stores for JSON data easier. <br/>
12157 <pre><code>
12158 var store = new Roo.data.JsonStore({
12159     url: 'get-images.php',
12160     root: 'images',
12161     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
12162 });
12163 </code></pre>
12164  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12165  * JsonReader and HttpProxy (unless inline data is provided).</b>
12166  * @cfg {Array} fields An array of field definition objects, or field name strings.
12167  * @constructor
12168  * @param {Object} config
12169  */
12170 Roo.data.JsonStore = function(c){
12171     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12172         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12173         reader: new Roo.data.JsonReader(c, c.fields)
12174     }));
12175 };
12176 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12177  * Based on:
12178  * Ext JS Library 1.1.1
12179  * Copyright(c) 2006-2007, Ext JS, LLC.
12180  *
12181  * Originally Released Under LGPL - original licence link has changed is not relivant.
12182  *
12183  * Fork - LGPL
12184  * <script type="text/javascript">
12185  */
12186
12187  
12188 Roo.data.Field = function(config){
12189     if(typeof config == "string"){
12190         config = {name: config};
12191     }
12192     Roo.apply(this, config);
12193     
12194     if(!this.type){
12195         this.type = "auto";
12196     }
12197     
12198     var st = Roo.data.SortTypes;
12199     // named sortTypes are supported, here we look them up
12200     if(typeof this.sortType == "string"){
12201         this.sortType = st[this.sortType];
12202     }
12203     
12204     // set default sortType for strings and dates
12205     if(!this.sortType){
12206         switch(this.type){
12207             case "string":
12208                 this.sortType = st.asUCString;
12209                 break;
12210             case "date":
12211                 this.sortType = st.asDate;
12212                 break;
12213             default:
12214                 this.sortType = st.none;
12215         }
12216     }
12217
12218     // define once
12219     var stripRe = /[\$,%]/g;
12220
12221     // prebuilt conversion function for this field, instead of
12222     // switching every time we're reading a value
12223     if(!this.convert){
12224         var cv, dateFormat = this.dateFormat;
12225         switch(this.type){
12226             case "":
12227             case "auto":
12228             case undefined:
12229                 cv = function(v){ return v; };
12230                 break;
12231             case "string":
12232                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12233                 break;
12234             case "int":
12235                 cv = function(v){
12236                     return v !== undefined && v !== null && v !== '' ?
12237                            parseInt(String(v).replace(stripRe, ""), 10) : '';
12238                     };
12239                 break;
12240             case "float":
12241                 cv = function(v){
12242                     return v !== undefined && v !== null && v !== '' ?
12243                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
12244                     };
12245                 break;
12246             case "bool":
12247             case "boolean":
12248                 cv = function(v){ return v === true || v === "true" || v == 1; };
12249                 break;
12250             case "date":
12251                 cv = function(v){
12252                     if(!v){
12253                         return '';
12254                     }
12255                     if(v instanceof Date){
12256                         return v;
12257                     }
12258                     if(dateFormat){
12259                         if(dateFormat == "timestamp"){
12260                             return new Date(v*1000);
12261                         }
12262                         return Date.parseDate(v, dateFormat);
12263                     }
12264                     var parsed = Date.parse(v);
12265                     return parsed ? new Date(parsed) : null;
12266                 };
12267              break;
12268             
12269         }
12270         this.convert = cv;
12271     }
12272 };
12273
12274 Roo.data.Field.prototype = {
12275     dateFormat: null,
12276     defaultValue: "",
12277     mapping: null,
12278     sortType : null,
12279     sortDir : "ASC"
12280 };/*
12281  * Based on:
12282  * Ext JS Library 1.1.1
12283  * Copyright(c) 2006-2007, Ext JS, LLC.
12284  *
12285  * Originally Released Under LGPL - original licence link has changed is not relivant.
12286  *
12287  * Fork - LGPL
12288  * <script type="text/javascript">
12289  */
12290  
12291 // Base class for reading structured data from a data source.  This class is intended to be
12292 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12293
12294 /**
12295  * @class Roo.data.DataReader
12296  * Base class for reading structured data from a data source.  This class is intended to be
12297  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12298  */
12299
12300 Roo.data.DataReader = function(meta, recordType){
12301     
12302     this.meta = meta;
12303     
12304     this.recordType = recordType instanceof Array ? 
12305         Roo.data.Record.create(recordType) : recordType;
12306 };
12307
12308 Roo.data.DataReader.prototype = {
12309      /**
12310      * Create an empty record
12311      * @param {Object} data (optional) - overlay some values
12312      * @return {Roo.data.Record} record created.
12313      */
12314     newRow :  function(d) {
12315         var da =  {};
12316         this.recordType.prototype.fields.each(function(c) {
12317             switch( c.type) {
12318                 case 'int' : da[c.name] = 0; break;
12319                 case 'date' : da[c.name] = new Date(); break;
12320                 case 'float' : da[c.name] = 0.0; break;
12321                 case 'boolean' : da[c.name] = false; break;
12322                 default : da[c.name] = ""; break;
12323             }
12324             
12325         });
12326         return new this.recordType(Roo.apply(da, d));
12327     }
12328     
12329 };/*
12330  * Based on:
12331  * Ext JS Library 1.1.1
12332  * Copyright(c) 2006-2007, Ext JS, LLC.
12333  *
12334  * Originally Released Under LGPL - original licence link has changed is not relivant.
12335  *
12336  * Fork - LGPL
12337  * <script type="text/javascript">
12338  */
12339
12340 /**
12341  * @class Roo.data.DataProxy
12342  * @extends Roo.data.Observable
12343  * This class is an abstract base class for implementations which provide retrieval of
12344  * unformatted data objects.<br>
12345  * <p>
12346  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12347  * (of the appropriate type which knows how to parse the data object) to provide a block of
12348  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12349  * <p>
12350  * Custom implementations must implement the load method as described in
12351  * {@link Roo.data.HttpProxy#load}.
12352  */
12353 Roo.data.DataProxy = function(){
12354     this.addEvents({
12355         /**
12356          * @event beforeload
12357          * Fires before a network request is made to retrieve a data object.
12358          * @param {Object} This DataProxy object.
12359          * @param {Object} params The params parameter to the load function.
12360          */
12361         beforeload : true,
12362         /**
12363          * @event load
12364          * Fires before the load method's callback is called.
12365          * @param {Object} This DataProxy object.
12366          * @param {Object} o The data object.
12367          * @param {Object} arg The callback argument object passed to the load function.
12368          */
12369         load : true,
12370         /**
12371          * @event loadexception
12372          * Fires if an Exception occurs during data retrieval.
12373          * @param {Object} This DataProxy object.
12374          * @param {Object} o The data object.
12375          * @param {Object} arg The callback argument object passed to the load function.
12376          * @param {Object} e The Exception.
12377          */
12378         loadexception : true
12379     });
12380     Roo.data.DataProxy.superclass.constructor.call(this);
12381 };
12382
12383 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12384
12385     /**
12386      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12387      */
12388 /*
12389  * Based on:
12390  * Ext JS Library 1.1.1
12391  * Copyright(c) 2006-2007, Ext JS, LLC.
12392  *
12393  * Originally Released Under LGPL - original licence link has changed is not relivant.
12394  *
12395  * Fork - LGPL
12396  * <script type="text/javascript">
12397  */
12398 /**
12399  * @class Roo.data.MemoryProxy
12400  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12401  * to the Reader when its load method is called.
12402  * @constructor
12403  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12404  */
12405 Roo.data.MemoryProxy = function(data){
12406     if (data.data) {
12407         data = data.data;
12408     }
12409     Roo.data.MemoryProxy.superclass.constructor.call(this);
12410     this.data = data;
12411 };
12412
12413 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12414     
12415     /**
12416      * Load data from the requested source (in this case an in-memory
12417      * data object passed to the constructor), read the data object into
12418      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12419      * process that block using the passed callback.
12420      * @param {Object} params This parameter is not used by the MemoryProxy class.
12421      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12422      * object into a block of Roo.data.Records.
12423      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12424      * The function must be passed <ul>
12425      * <li>The Record block object</li>
12426      * <li>The "arg" argument from the load function</li>
12427      * <li>A boolean success indicator</li>
12428      * </ul>
12429      * @param {Object} scope The scope in which to call the callback
12430      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12431      */
12432     load : function(params, reader, callback, scope, arg){
12433         params = params || {};
12434         var result;
12435         try {
12436             result = reader.readRecords(params.data ? params.data :this.data);
12437         }catch(e){
12438             this.fireEvent("loadexception", this, arg, null, e);
12439             callback.call(scope, null, arg, false);
12440             return;
12441         }
12442         callback.call(scope, result, arg, true);
12443     },
12444     
12445     // private
12446     update : function(params, records){
12447         
12448     }
12449 });/*
12450  * Based on:
12451  * Ext JS Library 1.1.1
12452  * Copyright(c) 2006-2007, Ext JS, LLC.
12453  *
12454  * Originally Released Under LGPL - original licence link has changed is not relivant.
12455  *
12456  * Fork - LGPL
12457  * <script type="text/javascript">
12458  */
12459 /**
12460  * @class Roo.data.HttpProxy
12461  * @extends Roo.data.DataProxy
12462  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12463  * configured to reference a certain URL.<br><br>
12464  * <p>
12465  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12466  * from which the running page was served.<br><br>
12467  * <p>
12468  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12469  * <p>
12470  * Be aware that to enable the browser to parse an XML document, the server must set
12471  * the Content-Type header in the HTTP response to "text/xml".
12472  * @constructor
12473  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12474  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12475  * will be used to make the request.
12476  */
12477 Roo.data.HttpProxy = function(conn){
12478     Roo.data.HttpProxy.superclass.constructor.call(this);
12479     // is conn a conn config or a real conn?
12480     this.conn = conn;
12481     this.useAjax = !conn || !conn.events;
12482   
12483 };
12484
12485 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12486     // thse are take from connection...
12487     
12488     /**
12489      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12490      */
12491     /**
12492      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12493      * extra parameters to each request made by this object. (defaults to undefined)
12494      */
12495     /**
12496      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12497      *  to each request made by this object. (defaults to undefined)
12498      */
12499     /**
12500      * @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)
12501      */
12502     /**
12503      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12504      */
12505      /**
12506      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12507      * @type Boolean
12508      */
12509   
12510
12511     /**
12512      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12513      * @type Boolean
12514      */
12515     /**
12516      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12517      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12518      * a finer-grained basis than the DataProxy events.
12519      */
12520     getConnection : function(){
12521         return this.useAjax ? Roo.Ajax : this.conn;
12522     },
12523
12524     /**
12525      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12526      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12527      * process that block using the passed callback.
12528      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12529      * for the request to the remote server.
12530      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12531      * object into a block of Roo.data.Records.
12532      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12533      * The function must be passed <ul>
12534      * <li>The Record block object</li>
12535      * <li>The "arg" argument from the load function</li>
12536      * <li>A boolean success indicator</li>
12537      * </ul>
12538      * @param {Object} scope The scope in which to call the callback
12539      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12540      */
12541     load : function(params, reader, callback, scope, arg){
12542         if(this.fireEvent("beforeload", this, params) !== false){
12543             var  o = {
12544                 params : params || {},
12545                 request: {
12546                     callback : callback,
12547                     scope : scope,
12548                     arg : arg
12549                 },
12550                 reader: reader,
12551                 callback : this.loadResponse,
12552                 scope: this
12553             };
12554             if(this.useAjax){
12555                 Roo.applyIf(o, this.conn);
12556                 if(this.activeRequest){
12557                     Roo.Ajax.abort(this.activeRequest);
12558                 }
12559                 this.activeRequest = Roo.Ajax.request(o);
12560             }else{
12561                 this.conn.request(o);
12562             }
12563         }else{
12564             callback.call(scope||this, null, arg, false);
12565         }
12566     },
12567
12568     // private
12569     loadResponse : function(o, success, response){
12570         delete this.activeRequest;
12571         if(!success){
12572             this.fireEvent("loadexception", this, o, response);
12573             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12574             return;
12575         }
12576         var result;
12577         try {
12578             result = o.reader.read(response);
12579         }catch(e){
12580             this.fireEvent("loadexception", this, o, response, e);
12581             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12582             return;
12583         }
12584         
12585         this.fireEvent("load", this, o, o.request.arg);
12586         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12587     },
12588
12589     // private
12590     update : function(dataSet){
12591
12592     },
12593
12594     // private
12595     updateResponse : function(dataSet){
12596
12597     }
12598 });/*
12599  * Based on:
12600  * Ext JS Library 1.1.1
12601  * Copyright(c) 2006-2007, Ext JS, LLC.
12602  *
12603  * Originally Released Under LGPL - original licence link has changed is not relivant.
12604  *
12605  * Fork - LGPL
12606  * <script type="text/javascript">
12607  */
12608
12609 /**
12610  * @class Roo.data.ScriptTagProxy
12611  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12612  * other than the originating domain of the running page.<br><br>
12613  * <p>
12614  * <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
12615  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12616  * <p>
12617  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12618  * source code that is used as the source inside a &lt;script> tag.<br><br>
12619  * <p>
12620  * In order for the browser to process the returned data, the server must wrap the data object
12621  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12622  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12623  * depending on whether the callback name was passed:
12624  * <p>
12625  * <pre><code>
12626 boolean scriptTag = false;
12627 String cb = request.getParameter("callback");
12628 if (cb != null) {
12629     scriptTag = true;
12630     response.setContentType("text/javascript");
12631 } else {
12632     response.setContentType("application/x-json");
12633 }
12634 Writer out = response.getWriter();
12635 if (scriptTag) {
12636     out.write(cb + "(");
12637 }
12638 out.print(dataBlock.toJsonString());
12639 if (scriptTag) {
12640     out.write(");");
12641 }
12642 </pre></code>
12643  *
12644  * @constructor
12645  * @param {Object} config A configuration object.
12646  */
12647 Roo.data.ScriptTagProxy = function(config){
12648     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12649     Roo.apply(this, config);
12650     this.head = document.getElementsByTagName("head")[0];
12651 };
12652
12653 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12654
12655 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12656     /**
12657      * @cfg {String} url The URL from which to request the data object.
12658      */
12659     /**
12660      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12661      */
12662     timeout : 30000,
12663     /**
12664      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12665      * the server the name of the callback function set up by the load call to process the returned data object.
12666      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12667      * javascript output which calls this named function passing the data object as its only parameter.
12668      */
12669     callbackParam : "callback",
12670     /**
12671      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12672      * name to the request.
12673      */
12674     nocache : true,
12675
12676     /**
12677      * Load data from the configured URL, read the data object into
12678      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12679      * process that block using the passed callback.
12680      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12681      * for the request to the remote server.
12682      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12683      * object into a block of Roo.data.Records.
12684      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12685      * The function must be passed <ul>
12686      * <li>The Record block object</li>
12687      * <li>The "arg" argument from the load function</li>
12688      * <li>A boolean success indicator</li>
12689      * </ul>
12690      * @param {Object} scope The scope in which to call the callback
12691      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12692      */
12693     load : function(params, reader, callback, scope, arg){
12694         if(this.fireEvent("beforeload", this, params) !== false){
12695
12696             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12697
12698             var url = this.url;
12699             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12700             if(this.nocache){
12701                 url += "&_dc=" + (new Date().getTime());
12702             }
12703             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12704             var trans = {
12705                 id : transId,
12706                 cb : "stcCallback"+transId,
12707                 scriptId : "stcScript"+transId,
12708                 params : params,
12709                 arg : arg,
12710                 url : url,
12711                 callback : callback,
12712                 scope : scope,
12713                 reader : reader
12714             };
12715             var conn = this;
12716
12717             window[trans.cb] = function(o){
12718                 conn.handleResponse(o, trans);
12719             };
12720
12721             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12722
12723             if(this.autoAbort !== false){
12724                 this.abort();
12725             }
12726
12727             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12728
12729             var script = document.createElement("script");
12730             script.setAttribute("src", url);
12731             script.setAttribute("type", "text/javascript");
12732             script.setAttribute("id", trans.scriptId);
12733             this.head.appendChild(script);
12734
12735             this.trans = trans;
12736         }else{
12737             callback.call(scope||this, null, arg, false);
12738         }
12739     },
12740
12741     // private
12742     isLoading : function(){
12743         return this.trans ? true : false;
12744     },
12745
12746     /**
12747      * Abort the current server request.
12748      */
12749     abort : function(){
12750         if(this.isLoading()){
12751             this.destroyTrans(this.trans);
12752         }
12753     },
12754
12755     // private
12756     destroyTrans : function(trans, isLoaded){
12757         this.head.removeChild(document.getElementById(trans.scriptId));
12758         clearTimeout(trans.timeoutId);
12759         if(isLoaded){
12760             window[trans.cb] = undefined;
12761             try{
12762                 delete window[trans.cb];
12763             }catch(e){}
12764         }else{
12765             // if hasn't been loaded, wait for load to remove it to prevent script error
12766             window[trans.cb] = function(){
12767                 window[trans.cb] = undefined;
12768                 try{
12769                     delete window[trans.cb];
12770                 }catch(e){}
12771             };
12772         }
12773     },
12774
12775     // private
12776     handleResponse : function(o, trans){
12777         this.trans = false;
12778         this.destroyTrans(trans, true);
12779         var result;
12780         try {
12781             result = trans.reader.readRecords(o);
12782         }catch(e){
12783             this.fireEvent("loadexception", this, o, trans.arg, e);
12784             trans.callback.call(trans.scope||window, null, trans.arg, false);
12785             return;
12786         }
12787         this.fireEvent("load", this, o, trans.arg);
12788         trans.callback.call(trans.scope||window, result, trans.arg, true);
12789     },
12790
12791     // private
12792     handleFailure : function(trans){
12793         this.trans = false;
12794         this.destroyTrans(trans, false);
12795         this.fireEvent("loadexception", this, null, trans.arg);
12796         trans.callback.call(trans.scope||window, null, trans.arg, false);
12797     }
12798 });/*
12799  * Based on:
12800  * Ext JS Library 1.1.1
12801  * Copyright(c) 2006-2007, Ext JS, LLC.
12802  *
12803  * Originally Released Under LGPL - original licence link has changed is not relivant.
12804  *
12805  * Fork - LGPL
12806  * <script type="text/javascript">
12807  */
12808
12809 /**
12810  * @class Roo.data.JsonReader
12811  * @extends Roo.data.DataReader
12812  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12813  * based on mappings in a provided Roo.data.Record constructor.
12814  * 
12815  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12816  * in the reply previously. 
12817  * 
12818  * <p>
12819  * Example code:
12820  * <pre><code>
12821 var RecordDef = Roo.data.Record.create([
12822     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12823     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12824 ]);
12825 var myReader = new Roo.data.JsonReader({
12826     totalProperty: "results",    // The property which contains the total dataset size (optional)
12827     root: "rows",                // The property which contains an Array of row objects
12828     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12829 }, RecordDef);
12830 </code></pre>
12831  * <p>
12832  * This would consume a JSON file like this:
12833  * <pre><code>
12834 { 'results': 2, 'rows': [
12835     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12836     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12837 }
12838 </code></pre>
12839  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12840  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12841  * paged from the remote server.
12842  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12843  * @cfg {String} root name of the property which contains the Array of row objects.
12844  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12845  * @cfg {Array} fields Array of field definition objects
12846  * @constructor
12847  * Create a new JsonReader
12848  * @param {Object} meta Metadata configuration options
12849  * @param {Object} recordType Either an Array of field definition objects,
12850  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12851  */
12852 Roo.data.JsonReader = function(meta, recordType){
12853     
12854     meta = meta || {};
12855     // set some defaults:
12856     Roo.applyIf(meta, {
12857         totalProperty: 'total',
12858         successProperty : 'success',
12859         root : 'data',
12860         id : 'id'
12861     });
12862     
12863     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12864 };
12865 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12866     
12867     /**
12868      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12869      * Used by Store query builder to append _requestMeta to params.
12870      * 
12871      */
12872     metaFromRemote : false,
12873     /**
12874      * This method is only used by a DataProxy which has retrieved data from a remote server.
12875      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12876      * @return {Object} data A data block which is used by an Roo.data.Store object as
12877      * a cache of Roo.data.Records.
12878      */
12879     read : function(response){
12880         var json = response.responseText;
12881        
12882         var o = /* eval:var:o */ eval("("+json+")");
12883         if(!o) {
12884             throw {message: "JsonReader.read: Json object not found"};
12885         }
12886         
12887         if(o.metaData){
12888             
12889             delete this.ef;
12890             this.metaFromRemote = true;
12891             this.meta = o.metaData;
12892             this.recordType = Roo.data.Record.create(o.metaData.fields);
12893             this.onMetaChange(this.meta, this.recordType, o);
12894         }
12895         return this.readRecords(o);
12896     },
12897
12898     // private function a store will implement
12899     onMetaChange : function(meta, recordType, o){
12900
12901     },
12902
12903     /**
12904          * @ignore
12905          */
12906     simpleAccess: function(obj, subsc) {
12907         return obj[subsc];
12908     },
12909
12910         /**
12911          * @ignore
12912          */
12913     getJsonAccessor: function(){
12914         var re = /[\[\.]/;
12915         return function(expr) {
12916             try {
12917                 return(re.test(expr))
12918                     ? new Function("obj", "return obj." + expr)
12919                     : function(obj){
12920                         return obj[expr];
12921                     };
12922             } catch(e){}
12923             return Roo.emptyFn;
12924         };
12925     }(),
12926
12927     /**
12928      * Create a data block containing Roo.data.Records from an XML document.
12929      * @param {Object} o An object which contains an Array of row objects in the property specified
12930      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12931      * which contains the total size of the dataset.
12932      * @return {Object} data A data block which is used by an Roo.data.Store object as
12933      * a cache of Roo.data.Records.
12934      */
12935     readRecords : function(o){
12936         /**
12937          * After any data loads, the raw JSON data is available for further custom processing.
12938          * @type Object
12939          */
12940         this.o = o;
12941         var s = this.meta, Record = this.recordType,
12942             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12943
12944 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12945         if (!this.ef) {
12946             if(s.totalProperty) {
12947                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12948                 }
12949                 if(s.successProperty) {
12950                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12951                 }
12952                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12953                 if (s.id) {
12954                         var g = this.getJsonAccessor(s.id);
12955                         this.getId = function(rec) {
12956                                 var r = g(rec);  
12957                                 return (r === undefined || r === "") ? null : r;
12958                         };
12959                 } else {
12960                         this.getId = function(){return null;};
12961                 }
12962             this.ef = [];
12963             for(var jj = 0; jj < fl; jj++){
12964                 f = fi[jj];
12965                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12966                 this.ef[jj] = this.getJsonAccessor(map);
12967             }
12968         }
12969
12970         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12971         if(s.totalProperty){
12972             var vt = parseInt(this.getTotal(o), 10);
12973             if(!isNaN(vt)){
12974                 totalRecords = vt;
12975             }
12976         }
12977         if(s.successProperty){
12978             var vs = this.getSuccess(o);
12979             if(vs === false || vs === 'false'){
12980                 success = false;
12981             }
12982         }
12983         var records = [];
12984         for(var i = 0; i < c; i++){
12985                 var n = root[i];
12986             var values = {};
12987             var id = this.getId(n);
12988             for(var j = 0; j < fl; j++){
12989                 f = fi[j];
12990             var v = this.ef[j](n);
12991             if (!f.convert) {
12992                 Roo.log('missing convert for ' + f.name);
12993                 Roo.log(f);
12994                 continue;
12995             }
12996             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12997             }
12998             var record = new Record(values, id);
12999             record.json = n;
13000             records[i] = record;
13001         }
13002         return {
13003             raw : o,
13004             success : success,
13005             records : records,
13006             totalRecords : totalRecords
13007         };
13008     }
13009 });/*
13010  * Based on:
13011  * Ext JS Library 1.1.1
13012  * Copyright(c) 2006-2007, Ext JS, LLC.
13013  *
13014  * Originally Released Under LGPL - original licence link has changed is not relivant.
13015  *
13016  * Fork - LGPL
13017  * <script type="text/javascript">
13018  */
13019
13020 /**
13021  * @class Roo.data.ArrayReader
13022  * @extends Roo.data.DataReader
13023  * Data reader class to create an Array of Roo.data.Record objects from an Array.
13024  * Each element of that Array represents a row of data fields. The
13025  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
13026  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
13027  * <p>
13028  * Example code:.
13029  * <pre><code>
13030 var RecordDef = Roo.data.Record.create([
13031     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
13032     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
13033 ]);
13034 var myReader = new Roo.data.ArrayReader({
13035     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
13036 }, RecordDef);
13037 </code></pre>
13038  * <p>
13039  * This would consume an Array like this:
13040  * <pre><code>
13041 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
13042   </code></pre>
13043  
13044  * @constructor
13045  * Create a new JsonReader
13046  * @param {Object} meta Metadata configuration options.
13047  * @param {Object|Array} recordType Either an Array of field definition objects
13048  * 
13049  * @cfg {Array} fields Array of field definition objects
13050  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
13051  * as specified to {@link Roo.data.Record#create},
13052  * or an {@link Roo.data.Record} object
13053  *
13054  * 
13055  * created using {@link Roo.data.Record#create}.
13056  */
13057 Roo.data.ArrayReader = function(meta, recordType){
13058     
13059      
13060     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
13061 };
13062
13063 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
13064     /**
13065      * Create a data block containing Roo.data.Records from an XML document.
13066      * @param {Object} o An Array of row objects which represents the dataset.
13067      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
13068      * a cache of Roo.data.Records.
13069      */
13070     readRecords : function(o){
13071         var sid = this.meta ? this.meta.id : null;
13072         var recordType = this.recordType, fields = recordType.prototype.fields;
13073         var records = [];
13074         var root = o;
13075             for(var i = 0; i < root.length; i++){
13076                     var n = root[i];
13077                 var values = {};
13078                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
13079                 for(var j = 0, jlen = fields.length; j < jlen; j++){
13080                 var f = fields.items[j];
13081                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
13082                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
13083                 v = f.convert(v);
13084                 values[f.name] = v;
13085             }
13086                 var record = new recordType(values, id);
13087                 record.json = n;
13088                 records[records.length] = record;
13089             }
13090             return {
13091                 records : records,
13092                 totalRecords : records.length
13093             };
13094     }
13095 });/*
13096  * - LGPL
13097  * * 
13098  */
13099
13100 /**
13101  * @class Roo.bootstrap.ComboBox
13102  * @extends Roo.bootstrap.TriggerField
13103  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
13104  * @cfg {Boolean} append (true|false) default false
13105  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
13106  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
13107  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
13108  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
13109  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
13110  * @cfg {Boolean} animate default true
13111  * @cfg {Boolean} emptyResultText only for touch device
13112  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
13113  * @cfg {String} emptyTitle default ''
13114  * @constructor
13115  * Create a new ComboBox.
13116  * @param {Object} config Configuration options
13117  */
13118 Roo.bootstrap.ComboBox = function(config){
13119     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
13120     this.addEvents({
13121         /**
13122          * @event expand
13123          * Fires when the dropdown list is expanded
13124         * @param {Roo.bootstrap.ComboBox} combo This combo box
13125         */
13126         'expand' : true,
13127         /**
13128          * @event collapse
13129          * Fires when the dropdown list is collapsed
13130         * @param {Roo.bootstrap.ComboBox} combo This combo box
13131         */
13132         'collapse' : true,
13133         /**
13134          * @event beforeselect
13135          * Fires before a list item is selected. Return false to cancel the selection.
13136         * @param {Roo.bootstrap.ComboBox} combo This combo box
13137         * @param {Roo.data.Record} record The data record returned from the underlying store
13138         * @param {Number} index The index of the selected item in the dropdown list
13139         */
13140         'beforeselect' : true,
13141         /**
13142          * @event select
13143          * Fires when a list item is selected
13144         * @param {Roo.bootstrap.ComboBox} combo This combo box
13145         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
13146         * @param {Number} index The index of the selected item in the dropdown list
13147         */
13148         'select' : true,
13149         /**
13150          * @event beforequery
13151          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
13152          * The event object passed has these properties:
13153         * @param {Roo.bootstrap.ComboBox} combo This combo box
13154         * @param {String} query The query
13155         * @param {Boolean} forceAll true to force "all" query
13156         * @param {Boolean} cancel true to cancel the query
13157         * @param {Object} e The query event object
13158         */
13159         'beforequery': true,
13160          /**
13161          * @event add
13162          * Fires when the 'add' icon is pressed (add a listener to enable add button)
13163         * @param {Roo.bootstrap.ComboBox} combo This combo box
13164         */
13165         'add' : true,
13166         /**
13167          * @event edit
13168          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
13169         * @param {Roo.bootstrap.ComboBox} combo This combo box
13170         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13171         */
13172         'edit' : true,
13173         /**
13174          * @event remove
13175          * Fires when the remove value from the combobox array
13176         * @param {Roo.bootstrap.ComboBox} combo This combo box
13177         */
13178         'remove' : true,
13179         /**
13180          * @event afterremove
13181          * Fires when the remove value from the combobox array
13182         * @param {Roo.bootstrap.ComboBox} combo This combo box
13183         */
13184         'afterremove' : true,
13185         /**
13186          * @event specialfilter
13187          * Fires when specialfilter
13188             * @param {Roo.bootstrap.ComboBox} combo This combo box
13189             */
13190         'specialfilter' : true,
13191         /**
13192          * @event tick
13193          * Fires when tick the element
13194             * @param {Roo.bootstrap.ComboBox} combo This combo box
13195             */
13196         'tick' : true,
13197         /**
13198          * @event touchviewdisplay
13199          * Fires when touch view require special display (default is using displayField)
13200             * @param {Roo.bootstrap.ComboBox} combo This combo box
13201             * @param {Object} cfg set html .
13202             */
13203         'touchviewdisplay' : true
13204         
13205     });
13206     
13207     this.item = [];
13208     this.tickItems = [];
13209     
13210     this.selectedIndex = -1;
13211     if(this.mode == 'local'){
13212         if(config.queryDelay === undefined){
13213             this.queryDelay = 10;
13214         }
13215         if(config.minChars === undefined){
13216             this.minChars = 0;
13217         }
13218     }
13219 };
13220
13221 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13222      
13223     /**
13224      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13225      * rendering into an Roo.Editor, defaults to false)
13226      */
13227     /**
13228      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13229      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13230      */
13231     /**
13232      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13233      */
13234     /**
13235      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13236      * the dropdown list (defaults to undefined, with no header element)
13237      */
13238
13239      /**
13240      * @cfg {String/Roo.Template} tpl The template to use to render the output
13241      */
13242      
13243      /**
13244      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13245      */
13246     listWidth: undefined,
13247     /**
13248      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13249      * mode = 'remote' or 'text' if mode = 'local')
13250      */
13251     displayField: undefined,
13252     
13253     /**
13254      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13255      * mode = 'remote' or 'value' if mode = 'local'). 
13256      * Note: use of a valueField requires the user make a selection
13257      * in order for a value to be mapped.
13258      */
13259     valueField: undefined,
13260     /**
13261      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13262      */
13263     modalTitle : '',
13264     
13265     /**
13266      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13267      * field's data value (defaults to the underlying DOM element's name)
13268      */
13269     hiddenName: undefined,
13270     /**
13271      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13272      */
13273     listClass: '',
13274     /**
13275      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13276      */
13277     selectedClass: 'active',
13278     
13279     /**
13280      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13281      */
13282     shadow:'sides',
13283     /**
13284      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13285      * anchor positions (defaults to 'tl-bl')
13286      */
13287     listAlign: 'tl-bl?',
13288     /**
13289      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13290      */
13291     maxHeight: 300,
13292     /**
13293      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13294      * query specified by the allQuery config option (defaults to 'query')
13295      */
13296     triggerAction: 'query',
13297     /**
13298      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13299      * (defaults to 4, does not apply if editable = false)
13300      */
13301     minChars : 4,
13302     /**
13303      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13304      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13305      */
13306     typeAhead: false,
13307     /**
13308      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13309      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13310      */
13311     queryDelay: 500,
13312     /**
13313      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13314      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13315      */
13316     pageSize: 0,
13317     /**
13318      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13319      * when editable = true (defaults to false)
13320      */
13321     selectOnFocus:false,
13322     /**
13323      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13324      */
13325     queryParam: 'query',
13326     /**
13327      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13328      * when mode = 'remote' (defaults to 'Loading...')
13329      */
13330     loadingText: 'Loading...',
13331     /**
13332      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13333      */
13334     resizable: false,
13335     /**
13336      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13337      */
13338     handleHeight : 8,
13339     /**
13340      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13341      * traditional select (defaults to true)
13342      */
13343     editable: true,
13344     /**
13345      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13346      */
13347     allQuery: '',
13348     /**
13349      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13350      */
13351     mode: 'remote',
13352     /**
13353      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13354      * listWidth has a higher value)
13355      */
13356     minListWidth : 70,
13357     /**
13358      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13359      * allow the user to set arbitrary text into the field (defaults to false)
13360      */
13361     forceSelection:false,
13362     /**
13363      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13364      * if typeAhead = true (defaults to 250)
13365      */
13366     typeAheadDelay : 250,
13367     /**
13368      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13369      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13370      */
13371     valueNotFoundText : undefined,
13372     /**
13373      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13374      */
13375     blockFocus : false,
13376     
13377     /**
13378      * @cfg {Boolean} disableClear Disable showing of clear button.
13379      */
13380     disableClear : false,
13381     /**
13382      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13383      */
13384     alwaysQuery : false,
13385     
13386     /**
13387      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13388      */
13389     multiple : false,
13390     
13391     /**
13392      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
13393      */
13394     invalidClass : "has-warning",
13395     
13396     /**
13397      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
13398      */
13399     validClass : "has-success",
13400     
13401     /**
13402      * @cfg {Boolean} specialFilter (true|false) special filter default false
13403      */
13404     specialFilter : false,
13405     
13406     /**
13407      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13408      */
13409     mobileTouchView : true,
13410     
13411     /**
13412      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13413      */
13414     useNativeIOS : false,
13415     
13416     /**
13417      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13418      */
13419     mobile_restrict_height : false,
13420     
13421     ios_options : false,
13422     
13423     //private
13424     addicon : false,
13425     editicon: false,
13426     
13427     page: 0,
13428     hasQuery: false,
13429     append: false,
13430     loadNext: false,
13431     autoFocus : true,
13432     tickable : false,
13433     btnPosition : 'right',
13434     triggerList : true,
13435     showToggleBtn : true,
13436     animate : true,
13437     emptyResultText: 'Empty',
13438     triggerText : 'Select',
13439     emptyTitle : '',
13440     
13441     // element that contains real text value.. (when hidden is used..)
13442     
13443     getAutoCreate : function()
13444     {   
13445         var cfg = false;
13446         //render
13447         /*
13448          * Render classic select for iso
13449          */
13450         
13451         if(Roo.isIOS && this.useNativeIOS){
13452             cfg = this.getAutoCreateNativeIOS();
13453             return cfg;
13454         }
13455         
13456         /*
13457          * Touch Devices
13458          */
13459         
13460         if(Roo.isTouch && this.mobileTouchView){
13461             cfg = this.getAutoCreateTouchView();
13462             return cfg;;
13463         }
13464         
13465         /*
13466          *  Normal ComboBox
13467          */
13468         if(!this.tickable){
13469             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13470             return cfg;
13471         }
13472         
13473         /*
13474          *  ComboBox with tickable selections
13475          */
13476              
13477         var align = this.labelAlign || this.parentLabelAlign();
13478         
13479         cfg = {
13480             cls : 'form-group roo-combobox-tickable' //input-group
13481         };
13482         
13483         var btn_text_select = '';
13484         var btn_text_done = '';
13485         var btn_text_cancel = '';
13486         
13487         if (this.btn_text_show) {
13488             btn_text_select = 'Select';
13489             btn_text_done = 'Done';
13490             btn_text_cancel = 'Cancel'; 
13491         }
13492         
13493         var buttons = {
13494             tag : 'div',
13495             cls : 'tickable-buttons',
13496             cn : [
13497                 {
13498                     tag : 'button',
13499                     type : 'button',
13500                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13501                     //html : this.triggerText
13502                     html: btn_text_select
13503                 },
13504                 {
13505                     tag : 'button',
13506                     type : 'button',
13507                     name : 'ok',
13508                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13509                     //html : 'Done'
13510                     html: btn_text_done
13511                 },
13512                 {
13513                     tag : 'button',
13514                     type : 'button',
13515                     name : 'cancel',
13516                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13517                     //html : 'Cancel'
13518                     html: btn_text_cancel
13519                 }
13520             ]
13521         };
13522         
13523         if(this.editable){
13524             buttons.cn.unshift({
13525                 tag: 'input',
13526                 cls: 'roo-select2-search-field-input'
13527             });
13528         }
13529         
13530         var _this = this;
13531         
13532         Roo.each(buttons.cn, function(c){
13533             if (_this.size) {
13534                 c.cls += ' btn-' + _this.size;
13535             }
13536
13537             if (_this.disabled) {
13538                 c.disabled = true;
13539             }
13540         });
13541         
13542         var box = {
13543             tag: 'div',
13544             style : 'display: contents',
13545             cn: [
13546                 {
13547                     tag: 'input',
13548                     type : 'hidden',
13549                     cls: 'form-hidden-field'
13550                 },
13551                 {
13552                     tag: 'ul',
13553                     cls: 'roo-select2-choices',
13554                     cn:[
13555                         {
13556                             tag: 'li',
13557                             cls: 'roo-select2-search-field',
13558                             cn: [
13559                                 buttons
13560                             ]
13561                         }
13562                     ]
13563                 }
13564             ]
13565         };
13566         
13567         var combobox = {
13568             cls: 'roo-select2-container input-group roo-select2-container-multi',
13569             cn: [
13570                 
13571                 box
13572 //                {
13573 //                    tag: 'ul',
13574 //                    cls: 'typeahead typeahead-long dropdown-menu',
13575 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13576 //                }
13577             ]
13578         };
13579         
13580         if(this.hasFeedback && !this.allowBlank){
13581             
13582             var feedback = {
13583                 tag: 'span',
13584                 cls: 'glyphicon form-control-feedback'
13585             };
13586
13587             combobox.cn.push(feedback);
13588         }
13589         
13590         var indicator = {
13591             tag : 'i',
13592             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13593             tooltip : 'This field is required'
13594         };
13595         if (Roo.bootstrap.version == 4) {
13596             indicator = {
13597                 tag : 'i',
13598                 style : 'display:none'
13599             };
13600         }
13601         if (align ==='left' && this.fieldLabel.length) {
13602             
13603             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13604             
13605             cfg.cn = [
13606                 indicator,
13607                 {
13608                     tag: 'label',
13609                     'for' :  id,
13610                     cls : 'control-label col-form-label',
13611                     html : this.fieldLabel
13612
13613                 },
13614                 {
13615                     cls : "", 
13616                     cn: [
13617                         combobox
13618                     ]
13619                 }
13620
13621             ];
13622             
13623             var labelCfg = cfg.cn[1];
13624             var contentCfg = cfg.cn[2];
13625             
13626
13627             if(this.indicatorpos == 'right'){
13628                 
13629                 cfg.cn = [
13630                     {
13631                         tag: 'label',
13632                         'for' :  id,
13633                         cls : 'control-label col-form-label',
13634                         cn : [
13635                             {
13636                                 tag : 'span',
13637                                 html : this.fieldLabel
13638                             },
13639                             indicator
13640                         ]
13641                     },
13642                     {
13643                         cls : "",
13644                         cn: [
13645                             combobox
13646                         ]
13647                     }
13648
13649                 ];
13650                 
13651                 
13652                 
13653                 labelCfg = cfg.cn[0];
13654                 contentCfg = cfg.cn[1];
13655             
13656             }
13657             
13658             if(this.labelWidth > 12){
13659                 labelCfg.style = "width: " + this.labelWidth + 'px';
13660             }
13661             
13662             if(this.labelWidth < 13 && this.labelmd == 0){
13663                 this.labelmd = this.labelWidth;
13664             }
13665             
13666             if(this.labellg > 0){
13667                 labelCfg.cls += ' col-lg-' + this.labellg;
13668                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13669             }
13670             
13671             if(this.labelmd > 0){
13672                 labelCfg.cls += ' col-md-' + this.labelmd;
13673                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13674             }
13675             
13676             if(this.labelsm > 0){
13677                 labelCfg.cls += ' col-sm-' + this.labelsm;
13678                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13679             }
13680             
13681             if(this.labelxs > 0){
13682                 labelCfg.cls += ' col-xs-' + this.labelxs;
13683                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13684             }
13685                 
13686                 
13687         } else if ( this.fieldLabel.length) {
13688 //                Roo.log(" label");
13689                  cfg.cn = [
13690                    indicator,
13691                     {
13692                         tag: 'label',
13693                         //cls : 'input-group-addon',
13694                         html : this.fieldLabel
13695                     },
13696                     combobox
13697                 ];
13698                 
13699                 if(this.indicatorpos == 'right'){
13700                     cfg.cn = [
13701                         {
13702                             tag: 'label',
13703                             //cls : 'input-group-addon',
13704                             html : this.fieldLabel
13705                         },
13706                         indicator,
13707                         combobox
13708                     ];
13709                     
13710                 }
13711
13712         } else {
13713             
13714 //                Roo.log(" no label && no align");
13715                 cfg = combobox
13716                      
13717                 
13718         }
13719          
13720         var settings=this;
13721         ['xs','sm','md','lg'].map(function(size){
13722             if (settings[size]) {
13723                 cfg.cls += ' col-' + size + '-' + settings[size];
13724             }
13725         });
13726         
13727         return cfg;
13728         
13729     },
13730     
13731     _initEventsCalled : false,
13732     
13733     // private
13734     initEvents: function()
13735     {   
13736         if (this._initEventsCalled) { // as we call render... prevent looping...
13737             return;
13738         }
13739         this._initEventsCalled = true;
13740         
13741         if (!this.store) {
13742             throw "can not find store for combo";
13743         }
13744         
13745         this.indicator = this.indicatorEl();
13746         
13747         this.store = Roo.factory(this.store, Roo.data);
13748         this.store.parent = this;
13749         
13750         // if we are building from html. then this element is so complex, that we can not really
13751         // use the rendered HTML.
13752         // so we have to trash and replace the previous code.
13753         if (Roo.XComponent.build_from_html) {
13754             // remove this element....
13755             var e = this.el.dom, k=0;
13756             while (e ) { e = e.previousSibling;  ++k;}
13757
13758             this.el.remove();
13759             
13760             this.el=false;
13761             this.rendered = false;
13762             
13763             this.render(this.parent().getChildContainer(true), k);
13764         }
13765         
13766         if(Roo.isIOS && this.useNativeIOS){
13767             this.initIOSView();
13768             return;
13769         }
13770         
13771         /*
13772          * Touch Devices
13773          */
13774         
13775         if(Roo.isTouch && this.mobileTouchView){
13776             this.initTouchView();
13777             return;
13778         }
13779         
13780         if(this.tickable){
13781             this.initTickableEvents();
13782             return;
13783         }
13784         
13785         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13786         
13787         if(this.hiddenName){
13788             
13789             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13790             
13791             this.hiddenField.dom.value =
13792                 this.hiddenValue !== undefined ? this.hiddenValue :
13793                 this.value !== undefined ? this.value : '';
13794
13795             // prevent input submission
13796             this.el.dom.removeAttribute('name');
13797             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13798              
13799              
13800         }
13801         //if(Roo.isGecko){
13802         //    this.el.dom.setAttribute('autocomplete', 'off');
13803         //}
13804         
13805         var cls = 'x-combo-list';
13806         
13807         //this.list = new Roo.Layer({
13808         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13809         //});
13810         
13811         var _this = this;
13812         
13813         (function(){
13814             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13815             _this.list.setWidth(lw);
13816         }).defer(100);
13817         
13818         this.list.on('mouseover', this.onViewOver, this);
13819         this.list.on('mousemove', this.onViewMove, this);
13820         this.list.on('scroll', this.onViewScroll, this);
13821         
13822         /*
13823         this.list.swallowEvent('mousewheel');
13824         this.assetHeight = 0;
13825
13826         if(this.title){
13827             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13828             this.assetHeight += this.header.getHeight();
13829         }
13830
13831         this.innerList = this.list.createChild({cls:cls+'-inner'});
13832         this.innerList.on('mouseover', this.onViewOver, this);
13833         this.innerList.on('mousemove', this.onViewMove, this);
13834         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13835         
13836         if(this.allowBlank && !this.pageSize && !this.disableClear){
13837             this.footer = this.list.createChild({cls:cls+'-ft'});
13838             this.pageTb = new Roo.Toolbar(this.footer);
13839            
13840         }
13841         if(this.pageSize){
13842             this.footer = this.list.createChild({cls:cls+'-ft'});
13843             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13844                     {pageSize: this.pageSize});
13845             
13846         }
13847         
13848         if (this.pageTb && this.allowBlank && !this.disableClear) {
13849             var _this = this;
13850             this.pageTb.add(new Roo.Toolbar.Fill(), {
13851                 cls: 'x-btn-icon x-btn-clear',
13852                 text: '&#160;',
13853                 handler: function()
13854                 {
13855                     _this.collapse();
13856                     _this.clearValue();
13857                     _this.onSelect(false, -1);
13858                 }
13859             });
13860         }
13861         if (this.footer) {
13862             this.assetHeight += this.footer.getHeight();
13863         }
13864         */
13865             
13866         if(!this.tpl){
13867             this.tpl = Roo.bootstrap.version == 4 ?
13868                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
13869                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13870         }
13871
13872         this.view = new Roo.View(this.list, this.tpl, {
13873             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13874         });
13875         //this.view.wrapEl.setDisplayed(false);
13876         this.view.on('click', this.onViewClick, this);
13877         
13878         
13879         this.store.on('beforeload', this.onBeforeLoad, this);
13880         this.store.on('load', this.onLoad, this);
13881         this.store.on('loadexception', this.onLoadException, this);
13882         /*
13883         if(this.resizable){
13884             this.resizer = new Roo.Resizable(this.list,  {
13885                pinned:true, handles:'se'
13886             });
13887             this.resizer.on('resize', function(r, w, h){
13888                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13889                 this.listWidth = w;
13890                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13891                 this.restrictHeight();
13892             }, this);
13893             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13894         }
13895         */
13896         if(!this.editable){
13897             this.editable = true;
13898             this.setEditable(false);
13899         }
13900         
13901         /*
13902         
13903         if (typeof(this.events.add.listeners) != 'undefined') {
13904             
13905             this.addicon = this.wrap.createChild(
13906                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13907        
13908             this.addicon.on('click', function(e) {
13909                 this.fireEvent('add', this);
13910             }, this);
13911         }
13912         if (typeof(this.events.edit.listeners) != 'undefined') {
13913             
13914             this.editicon = this.wrap.createChild(
13915                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13916             if (this.addicon) {
13917                 this.editicon.setStyle('margin-left', '40px');
13918             }
13919             this.editicon.on('click', function(e) {
13920                 
13921                 // we fire even  if inothing is selected..
13922                 this.fireEvent('edit', this, this.lastData );
13923                 
13924             }, this);
13925         }
13926         */
13927         
13928         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13929             "up" : function(e){
13930                 this.inKeyMode = true;
13931                 this.selectPrev();
13932             },
13933
13934             "down" : function(e){
13935                 if(!this.isExpanded()){
13936                     this.onTriggerClick();
13937                 }else{
13938                     this.inKeyMode = true;
13939                     this.selectNext();
13940                 }
13941             },
13942
13943             "enter" : function(e){
13944 //                this.onViewClick();
13945                 //return true;
13946                 this.collapse();
13947                 
13948                 if(this.fireEvent("specialkey", this, e)){
13949                     this.onViewClick(false);
13950                 }
13951                 
13952                 return true;
13953             },
13954
13955             "esc" : function(e){
13956                 this.collapse();
13957             },
13958
13959             "tab" : function(e){
13960                 this.collapse();
13961                 
13962                 if(this.fireEvent("specialkey", this, e)){
13963                     this.onViewClick(false);
13964                 }
13965                 
13966                 return true;
13967             },
13968
13969             scope : this,
13970
13971             doRelay : function(foo, bar, hname){
13972                 if(hname == 'down' || this.scope.isExpanded()){
13973                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13974                 }
13975                 return true;
13976             },
13977
13978             forceKeyDown: true
13979         });
13980         
13981         
13982         this.queryDelay = Math.max(this.queryDelay || 10,
13983                 this.mode == 'local' ? 10 : 250);
13984         
13985         
13986         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13987         
13988         if(this.typeAhead){
13989             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13990         }
13991         if(this.editable !== false){
13992             this.inputEl().on("keyup", this.onKeyUp, this);
13993         }
13994         if(this.forceSelection){
13995             this.inputEl().on('blur', this.doForce, this);
13996         }
13997         
13998         if(this.multiple){
13999             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14000             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14001         }
14002     },
14003     
14004     initTickableEvents: function()
14005     {   
14006         this.createList();
14007         
14008         if(this.hiddenName){
14009             
14010             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14011             
14012             this.hiddenField.dom.value =
14013                 this.hiddenValue !== undefined ? this.hiddenValue :
14014                 this.value !== undefined ? this.value : '';
14015
14016             // prevent input submission
14017             this.el.dom.removeAttribute('name');
14018             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14019              
14020              
14021         }
14022         
14023 //        this.list = this.el.select('ul.dropdown-menu',true).first();
14024         
14025         this.choices = this.el.select('ul.roo-select2-choices', true).first();
14026         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14027         if(this.triggerList){
14028             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
14029         }
14030          
14031         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
14032         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
14033         
14034         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
14035         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
14036         
14037         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
14038         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
14039         
14040         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
14041         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
14042         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
14043         
14044         this.okBtn.hide();
14045         this.cancelBtn.hide();
14046         
14047         var _this = this;
14048         
14049         (function(){
14050             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
14051             _this.list.setWidth(lw);
14052         }).defer(100);
14053         
14054         this.list.on('mouseover', this.onViewOver, this);
14055         this.list.on('mousemove', this.onViewMove, this);
14056         
14057         this.list.on('scroll', this.onViewScroll, this);
14058         
14059         if(!this.tpl){
14060             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
14061                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
14062         }
14063
14064         this.view = new Roo.View(this.list, this.tpl, {
14065             singleSelect:true,
14066             tickable:true,
14067             parent:this,
14068             store: this.store,
14069             selectedClass: this.selectedClass
14070         });
14071         
14072         //this.view.wrapEl.setDisplayed(false);
14073         this.view.on('click', this.onViewClick, this);
14074         
14075         
14076         
14077         this.store.on('beforeload', this.onBeforeLoad, this);
14078         this.store.on('load', this.onLoad, this);
14079         this.store.on('loadexception', this.onLoadException, this);
14080         
14081         if(this.editable){
14082             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
14083                 "up" : function(e){
14084                     this.inKeyMode = true;
14085                     this.selectPrev();
14086                 },
14087
14088                 "down" : function(e){
14089                     this.inKeyMode = true;
14090                     this.selectNext();
14091                 },
14092
14093                 "enter" : function(e){
14094                     if(this.fireEvent("specialkey", this, e)){
14095                         this.onViewClick(false);
14096                     }
14097                     
14098                     return true;
14099                 },
14100
14101                 "esc" : function(e){
14102                     this.onTickableFooterButtonClick(e, false, false);
14103                 },
14104
14105                 "tab" : function(e){
14106                     this.fireEvent("specialkey", this, e);
14107                     
14108                     this.onTickableFooterButtonClick(e, false, false);
14109                     
14110                     return true;
14111                 },
14112
14113                 scope : this,
14114
14115                 doRelay : function(e, fn, key){
14116                     if(this.scope.isExpanded()){
14117                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14118                     }
14119                     return true;
14120                 },
14121
14122                 forceKeyDown: true
14123             });
14124         }
14125         
14126         this.queryDelay = Math.max(this.queryDelay || 10,
14127                 this.mode == 'local' ? 10 : 250);
14128         
14129         
14130         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14131         
14132         if(this.typeAhead){
14133             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14134         }
14135         
14136         if(this.editable !== false){
14137             this.tickableInputEl().on("keyup", this.onKeyUp, this);
14138         }
14139         
14140         this.indicator = this.indicatorEl();
14141         
14142         if(this.indicator){
14143             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
14144             this.indicator.hide();
14145         }
14146         
14147     },
14148
14149     onDestroy : function(){
14150         if(this.view){
14151             this.view.setStore(null);
14152             this.view.el.removeAllListeners();
14153             this.view.el.remove();
14154             this.view.purgeListeners();
14155         }
14156         if(this.list){
14157             this.list.dom.innerHTML  = '';
14158         }
14159         
14160         if(this.store){
14161             this.store.un('beforeload', this.onBeforeLoad, this);
14162             this.store.un('load', this.onLoad, this);
14163             this.store.un('loadexception', this.onLoadException, this);
14164         }
14165         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
14166     },
14167
14168     // private
14169     fireKey : function(e){
14170         if(e.isNavKeyPress() && !this.list.isVisible()){
14171             this.fireEvent("specialkey", this, e);
14172         }
14173     },
14174
14175     // private
14176     onResize: function(w, h){
14177 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14178 //        
14179 //        if(typeof w != 'number'){
14180 //            // we do not handle it!?!?
14181 //            return;
14182 //        }
14183 //        var tw = this.trigger.getWidth();
14184 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
14185 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
14186 //        var x = w - tw;
14187 //        this.inputEl().setWidth( this.adjustWidth('input', x));
14188 //            
14189 //        //this.trigger.setStyle('left', x+'px');
14190 //        
14191 //        if(this.list && this.listWidth === undefined){
14192 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14193 //            this.list.setWidth(lw);
14194 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14195 //        }
14196         
14197     
14198         
14199     },
14200
14201     /**
14202      * Allow or prevent the user from directly editing the field text.  If false is passed,
14203      * the user will only be able to select from the items defined in the dropdown list.  This method
14204      * is the runtime equivalent of setting the 'editable' config option at config time.
14205      * @param {Boolean} value True to allow the user to directly edit the field text
14206      */
14207     setEditable : function(value){
14208         if(value == this.editable){
14209             return;
14210         }
14211         this.editable = value;
14212         if(!value){
14213             this.inputEl().dom.setAttribute('readOnly', true);
14214             this.inputEl().on('mousedown', this.onTriggerClick,  this);
14215             this.inputEl().addClass('x-combo-noedit');
14216         }else{
14217             this.inputEl().dom.setAttribute('readOnly', false);
14218             this.inputEl().un('mousedown', this.onTriggerClick,  this);
14219             this.inputEl().removeClass('x-combo-noedit');
14220         }
14221     },
14222
14223     // private
14224     
14225     onBeforeLoad : function(combo,opts){
14226         if(!this.hasFocus){
14227             return;
14228         }
14229          if (!opts.add) {
14230             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14231          }
14232         this.restrictHeight();
14233         this.selectedIndex = -1;
14234     },
14235
14236     // private
14237     onLoad : function(){
14238         
14239         this.hasQuery = false;
14240         
14241         if(!this.hasFocus){
14242             return;
14243         }
14244         
14245         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14246             this.loading.hide();
14247         }
14248         
14249         if(this.store.getCount() > 0){
14250             
14251             this.expand();
14252             this.restrictHeight();
14253             if(this.lastQuery == this.allQuery){
14254                 if(this.editable && !this.tickable){
14255                     this.inputEl().dom.select();
14256                 }
14257                 
14258                 if(
14259                     !this.selectByValue(this.value, true) &&
14260                     this.autoFocus && 
14261                     (
14262                         !this.store.lastOptions ||
14263                         typeof(this.store.lastOptions.add) == 'undefined' || 
14264                         this.store.lastOptions.add != true
14265                     )
14266                 ){
14267                     this.select(0, true);
14268                 }
14269             }else{
14270                 if(this.autoFocus){
14271                     this.selectNext();
14272                 }
14273                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14274                     this.taTask.delay(this.typeAheadDelay);
14275                 }
14276             }
14277         }else{
14278             this.onEmptyResults();
14279         }
14280         
14281         //this.el.focus();
14282     },
14283     // private
14284     onLoadException : function()
14285     {
14286         this.hasQuery = false;
14287         
14288         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14289             this.loading.hide();
14290         }
14291         
14292         if(this.tickable && this.editable){
14293             return;
14294         }
14295         
14296         this.collapse();
14297         // only causes errors at present
14298         //Roo.log(this.store.reader.jsonData);
14299         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14300             // fixme
14301             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14302         //}
14303         
14304         
14305     },
14306     // private
14307     onTypeAhead : function(){
14308         if(this.store.getCount() > 0){
14309             var r = this.store.getAt(0);
14310             var newValue = r.data[this.displayField];
14311             var len = newValue.length;
14312             var selStart = this.getRawValue().length;
14313             
14314             if(selStart != len){
14315                 this.setRawValue(newValue);
14316                 this.selectText(selStart, newValue.length);
14317             }
14318         }
14319     },
14320
14321     // private
14322     onSelect : function(record, index){
14323         
14324         if(this.fireEvent('beforeselect', this, record, index) !== false){
14325         
14326             this.setFromData(index > -1 ? record.data : false);
14327             
14328             this.collapse();
14329             this.fireEvent('select', this, record, index);
14330         }
14331     },
14332
14333     /**
14334      * Returns the currently selected field value or empty string if no value is set.
14335      * @return {String} value The selected value
14336      */
14337     getValue : function()
14338     {
14339         if(Roo.isIOS && this.useNativeIOS){
14340             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14341         }
14342         
14343         if(this.multiple){
14344             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14345         }
14346         
14347         if(this.valueField){
14348             return typeof this.value != 'undefined' ? this.value : '';
14349         }else{
14350             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14351         }
14352     },
14353     
14354     getRawValue : function()
14355     {
14356         if(Roo.isIOS && this.useNativeIOS){
14357             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14358         }
14359         
14360         var v = this.inputEl().getValue();
14361         
14362         return v;
14363     },
14364
14365     /**
14366      * Clears any text/value currently set in the field
14367      */
14368     clearValue : function(){
14369         
14370         if(this.hiddenField){
14371             this.hiddenField.dom.value = '';
14372         }
14373         this.value = '';
14374         this.setRawValue('');
14375         this.lastSelectionText = '';
14376         this.lastData = false;
14377         
14378         var close = this.closeTriggerEl();
14379         
14380         if(close){
14381             close.hide();
14382         }
14383         
14384         this.validate();
14385         
14386     },
14387
14388     /**
14389      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14390      * will be displayed in the field.  If the value does not match the data value of an existing item,
14391      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14392      * Otherwise the field will be blank (although the value will still be set).
14393      * @param {String} value The value to match
14394      */
14395     setValue : function(v)
14396     {
14397         if(Roo.isIOS && this.useNativeIOS){
14398             this.setIOSValue(v);
14399             return;
14400         }
14401         
14402         if(this.multiple){
14403             this.syncValue();
14404             return;
14405         }
14406         
14407         var text = v;
14408         if(this.valueField){
14409             var r = this.findRecord(this.valueField, v);
14410             if(r){
14411                 text = r.data[this.displayField];
14412             }else if(this.valueNotFoundText !== undefined){
14413                 text = this.valueNotFoundText;
14414             }
14415         }
14416         this.lastSelectionText = text;
14417         if(this.hiddenField){
14418             this.hiddenField.dom.value = v;
14419         }
14420         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14421         this.value = v;
14422         
14423         var close = this.closeTriggerEl();
14424         
14425         if(close){
14426             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14427         }
14428         
14429         this.validate();
14430     },
14431     /**
14432      * @property {Object} the last set data for the element
14433      */
14434     
14435     lastData : false,
14436     /**
14437      * Sets the value of the field based on a object which is related to the record format for the store.
14438      * @param {Object} value the value to set as. or false on reset?
14439      */
14440     setFromData : function(o){
14441         
14442         if(this.multiple){
14443             this.addItem(o);
14444             return;
14445         }
14446             
14447         var dv = ''; // display value
14448         var vv = ''; // value value..
14449         this.lastData = o;
14450         if (this.displayField) {
14451             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14452         } else {
14453             // this is an error condition!!!
14454             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14455         }
14456         
14457         if(this.valueField){
14458             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14459         }
14460         
14461         var close = this.closeTriggerEl();
14462         
14463         if(close){
14464             if(dv.length || vv * 1 > 0){
14465                 close.show() ;
14466                 this.blockFocus=true;
14467             } else {
14468                 close.hide();
14469             }             
14470         }
14471         
14472         if(this.hiddenField){
14473             this.hiddenField.dom.value = vv;
14474             
14475             this.lastSelectionText = dv;
14476             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14477             this.value = vv;
14478             return;
14479         }
14480         // no hidden field.. - we store the value in 'value', but still display
14481         // display field!!!!
14482         this.lastSelectionText = dv;
14483         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14484         this.value = vv;
14485         
14486         
14487         
14488     },
14489     // private
14490     reset : function(){
14491         // overridden so that last data is reset..
14492         
14493         if(this.multiple){
14494             this.clearItem();
14495             return;
14496         }
14497         
14498         this.setValue(this.originalValue);
14499         //this.clearInvalid();
14500         this.lastData = false;
14501         if (this.view) {
14502             this.view.clearSelections();
14503         }
14504         
14505         this.validate();
14506     },
14507     // private
14508     findRecord : function(prop, value){
14509         var record;
14510         if(this.store.getCount() > 0){
14511             this.store.each(function(r){
14512                 if(r.data[prop] == value){
14513                     record = r;
14514                     return false;
14515                 }
14516                 return true;
14517             });
14518         }
14519         return record;
14520     },
14521     
14522     getName: function()
14523     {
14524         // returns hidden if it's set..
14525         if (!this.rendered) {return ''};
14526         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14527         
14528     },
14529     // private
14530     onViewMove : function(e, t){
14531         this.inKeyMode = false;
14532     },
14533
14534     // private
14535     onViewOver : function(e, t){
14536         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14537             return;
14538         }
14539         var item = this.view.findItemFromChild(t);
14540         
14541         if(item){
14542             var index = this.view.indexOf(item);
14543             this.select(index, false);
14544         }
14545     },
14546
14547     // private
14548     onViewClick : function(view, doFocus, el, e)
14549     {
14550         var index = this.view.getSelectedIndexes()[0];
14551         
14552         var r = this.store.getAt(index);
14553         
14554         if(this.tickable){
14555             
14556             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14557                 return;
14558             }
14559             
14560             var rm = false;
14561             var _this = this;
14562             
14563             Roo.each(this.tickItems, function(v,k){
14564                 
14565                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14566                     Roo.log(v);
14567                     _this.tickItems.splice(k, 1);
14568                     
14569                     if(typeof(e) == 'undefined' && view == false){
14570                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14571                     }
14572                     
14573                     rm = true;
14574                     return;
14575                 }
14576             });
14577             
14578             if(rm){
14579                 return;
14580             }
14581             
14582             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14583                 this.tickItems.push(r.data);
14584             }
14585             
14586             if(typeof(e) == 'undefined' && view == false){
14587                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14588             }
14589                     
14590             return;
14591         }
14592         
14593         if(r){
14594             this.onSelect(r, index);
14595         }
14596         if(doFocus !== false && !this.blockFocus){
14597             this.inputEl().focus();
14598         }
14599     },
14600
14601     // private
14602     restrictHeight : function(){
14603         //this.innerList.dom.style.height = '';
14604         //var inner = this.innerList.dom;
14605         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14606         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14607         //this.list.beginUpdate();
14608         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14609         this.list.alignTo(this.inputEl(), this.listAlign);
14610         this.list.alignTo(this.inputEl(), this.listAlign);
14611         //this.list.endUpdate();
14612     },
14613
14614     // private
14615     onEmptyResults : function(){
14616         
14617         if(this.tickable && this.editable){
14618             this.hasFocus = false;
14619             this.restrictHeight();
14620             return;
14621         }
14622         
14623         this.collapse();
14624     },
14625
14626     /**
14627      * Returns true if the dropdown list is expanded, else false.
14628      */
14629     isExpanded : function(){
14630         return this.list.isVisible();
14631     },
14632
14633     /**
14634      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14635      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14636      * @param {String} value The data value of the item to select
14637      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14638      * selected item if it is not currently in view (defaults to true)
14639      * @return {Boolean} True if the value matched an item in the list, else false
14640      */
14641     selectByValue : function(v, scrollIntoView){
14642         if(v !== undefined && v !== null){
14643             var r = this.findRecord(this.valueField || this.displayField, v);
14644             if(r){
14645                 this.select(this.store.indexOf(r), scrollIntoView);
14646                 return true;
14647             }
14648         }
14649         return false;
14650     },
14651
14652     /**
14653      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14654      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14655      * @param {Number} index The zero-based index of the list item to select
14656      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14657      * selected item if it is not currently in view (defaults to true)
14658      */
14659     select : function(index, scrollIntoView){
14660         this.selectedIndex = index;
14661         this.view.select(index);
14662         if(scrollIntoView !== false){
14663             var el = this.view.getNode(index);
14664             /*
14665              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14666              */
14667             if(el){
14668                 this.list.scrollChildIntoView(el, false);
14669             }
14670         }
14671     },
14672
14673     // private
14674     selectNext : function(){
14675         var ct = this.store.getCount();
14676         if(ct > 0){
14677             if(this.selectedIndex == -1){
14678                 this.select(0);
14679             }else if(this.selectedIndex < ct-1){
14680                 this.select(this.selectedIndex+1);
14681             }
14682         }
14683     },
14684
14685     // private
14686     selectPrev : function(){
14687         var ct = this.store.getCount();
14688         if(ct > 0){
14689             if(this.selectedIndex == -1){
14690                 this.select(0);
14691             }else if(this.selectedIndex != 0){
14692                 this.select(this.selectedIndex-1);
14693             }
14694         }
14695     },
14696
14697     // private
14698     onKeyUp : function(e){
14699         if(this.editable !== false && !e.isSpecialKey()){
14700             this.lastKey = e.getKey();
14701             this.dqTask.delay(this.queryDelay);
14702         }
14703     },
14704
14705     // private
14706     validateBlur : function(){
14707         return !this.list || !this.list.isVisible();   
14708     },
14709
14710     // private
14711     initQuery : function(){
14712         
14713         var v = this.getRawValue();
14714         
14715         if(this.tickable && this.editable){
14716             v = this.tickableInputEl().getValue();
14717         }
14718         
14719         this.doQuery(v);
14720     },
14721
14722     // private
14723     doForce : function(){
14724         if(this.inputEl().dom.value.length > 0){
14725             this.inputEl().dom.value =
14726                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14727              
14728         }
14729     },
14730
14731     /**
14732      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14733      * query allowing the query action to be canceled if needed.
14734      * @param {String} query The SQL query to execute
14735      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14736      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14737      * saved in the current store (defaults to false)
14738      */
14739     doQuery : function(q, forceAll){
14740         
14741         if(q === undefined || q === null){
14742             q = '';
14743         }
14744         var qe = {
14745             query: q,
14746             forceAll: forceAll,
14747             combo: this,
14748             cancel:false
14749         };
14750         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14751             return false;
14752         }
14753         q = qe.query;
14754         
14755         forceAll = qe.forceAll;
14756         if(forceAll === true || (q.length >= this.minChars)){
14757             
14758             this.hasQuery = true;
14759             
14760             if(this.lastQuery != q || this.alwaysQuery){
14761                 this.lastQuery = q;
14762                 if(this.mode == 'local'){
14763                     this.selectedIndex = -1;
14764                     if(forceAll){
14765                         this.store.clearFilter();
14766                     }else{
14767                         
14768                         if(this.specialFilter){
14769                             this.fireEvent('specialfilter', this);
14770                             this.onLoad();
14771                             return;
14772                         }
14773                         
14774                         this.store.filter(this.displayField, q);
14775                     }
14776                     
14777                     this.store.fireEvent("datachanged", this.store);
14778                     
14779                     this.onLoad();
14780                     
14781                     
14782                 }else{
14783                     
14784                     this.store.baseParams[this.queryParam] = q;
14785                     
14786                     var options = {params : this.getParams(q)};
14787                     
14788                     if(this.loadNext){
14789                         options.add = true;
14790                         options.params.start = this.page * this.pageSize;
14791                     }
14792                     
14793                     this.store.load(options);
14794                     
14795                     /*
14796                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14797                      *  we should expand the list on onLoad
14798                      *  so command out it
14799                      */
14800 //                    this.expand();
14801                 }
14802             }else{
14803                 this.selectedIndex = -1;
14804                 this.onLoad();   
14805             }
14806         }
14807         
14808         this.loadNext = false;
14809     },
14810     
14811     // private
14812     getParams : function(q){
14813         var p = {};
14814         //p[this.queryParam] = q;
14815         
14816         if(this.pageSize){
14817             p.start = 0;
14818             p.limit = this.pageSize;
14819         }
14820         return p;
14821     },
14822
14823     /**
14824      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14825      */
14826     collapse : function(){
14827         if(!this.isExpanded()){
14828             return;
14829         }
14830         
14831         this.list.hide();
14832         
14833         this.hasFocus = false;
14834         
14835         if(this.tickable){
14836             this.okBtn.hide();
14837             this.cancelBtn.hide();
14838             this.trigger.show();
14839             
14840             if(this.editable){
14841                 this.tickableInputEl().dom.value = '';
14842                 this.tickableInputEl().blur();
14843             }
14844             
14845         }
14846         
14847         Roo.get(document).un('mousedown', this.collapseIf, this);
14848         Roo.get(document).un('mousewheel', this.collapseIf, this);
14849         if (!this.editable) {
14850             Roo.get(document).un('keydown', this.listKeyPress, this);
14851         }
14852         this.fireEvent('collapse', this);
14853         
14854         this.validate();
14855     },
14856
14857     // private
14858     collapseIf : function(e){
14859         var in_combo  = e.within(this.el);
14860         var in_list =  e.within(this.list);
14861         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14862         
14863         if (in_combo || in_list || is_list) {
14864             //e.stopPropagation();
14865             return;
14866         }
14867         
14868         if(this.tickable){
14869             this.onTickableFooterButtonClick(e, false, false);
14870         }
14871
14872         this.collapse();
14873         
14874     },
14875
14876     /**
14877      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14878      */
14879     expand : function(){
14880        
14881         if(this.isExpanded() || !this.hasFocus){
14882             return;
14883         }
14884         
14885         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14886         this.list.setWidth(lw);
14887         
14888         Roo.log('expand');
14889         
14890         this.list.show();
14891         
14892         this.restrictHeight();
14893         
14894         if(this.tickable){
14895             
14896             this.tickItems = Roo.apply([], this.item);
14897             
14898             this.okBtn.show();
14899             this.cancelBtn.show();
14900             this.trigger.hide();
14901             
14902             if(this.editable){
14903                 this.tickableInputEl().focus();
14904             }
14905             
14906         }
14907         
14908         Roo.get(document).on('mousedown', this.collapseIf, this);
14909         Roo.get(document).on('mousewheel', this.collapseIf, this);
14910         if (!this.editable) {
14911             Roo.get(document).on('keydown', this.listKeyPress, this);
14912         }
14913         
14914         this.fireEvent('expand', this);
14915     },
14916
14917     // private
14918     // Implements the default empty TriggerField.onTriggerClick function
14919     onTriggerClick : function(e)
14920     {
14921         Roo.log('trigger click');
14922         
14923         if(this.disabled || !this.triggerList){
14924             return;
14925         }
14926         
14927         this.page = 0;
14928         this.loadNext = false;
14929         
14930         if(this.isExpanded()){
14931             this.collapse();
14932             if (!this.blockFocus) {
14933                 this.inputEl().focus();
14934             }
14935             
14936         }else {
14937             this.hasFocus = true;
14938             if(this.triggerAction == 'all') {
14939                 this.doQuery(this.allQuery, true);
14940             } else {
14941                 this.doQuery(this.getRawValue());
14942             }
14943             if (!this.blockFocus) {
14944                 this.inputEl().focus();
14945             }
14946         }
14947     },
14948     
14949     onTickableTriggerClick : function(e)
14950     {
14951         if(this.disabled){
14952             return;
14953         }
14954         
14955         this.page = 0;
14956         this.loadNext = false;
14957         this.hasFocus = true;
14958         
14959         if(this.triggerAction == 'all') {
14960             this.doQuery(this.allQuery, true);
14961         } else {
14962             this.doQuery(this.getRawValue());
14963         }
14964     },
14965     
14966     onSearchFieldClick : function(e)
14967     {
14968         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14969             this.onTickableFooterButtonClick(e, false, false);
14970             return;
14971         }
14972         
14973         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14974             return;
14975         }
14976         
14977         this.page = 0;
14978         this.loadNext = false;
14979         this.hasFocus = true;
14980         
14981         if(this.triggerAction == 'all') {
14982             this.doQuery(this.allQuery, true);
14983         } else {
14984             this.doQuery(this.getRawValue());
14985         }
14986     },
14987     
14988     listKeyPress : function(e)
14989     {
14990         //Roo.log('listkeypress');
14991         // scroll to first matching element based on key pres..
14992         if (e.isSpecialKey()) {
14993             return false;
14994         }
14995         var k = String.fromCharCode(e.getKey()).toUpperCase();
14996         //Roo.log(k);
14997         var match  = false;
14998         var csel = this.view.getSelectedNodes();
14999         var cselitem = false;
15000         if (csel.length) {
15001             var ix = this.view.indexOf(csel[0]);
15002             cselitem  = this.store.getAt(ix);
15003             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
15004                 cselitem = false;
15005             }
15006             
15007         }
15008         
15009         this.store.each(function(v) { 
15010             if (cselitem) {
15011                 // start at existing selection.
15012                 if (cselitem.id == v.id) {
15013                     cselitem = false;
15014                 }
15015                 return true;
15016             }
15017                 
15018             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
15019                 match = this.store.indexOf(v);
15020                 return false;
15021             }
15022             return true;
15023         }, this);
15024         
15025         if (match === false) {
15026             return true; // no more action?
15027         }
15028         // scroll to?
15029         this.view.select(match);
15030         var sn = Roo.get(this.view.getSelectedNodes()[0]);
15031         sn.scrollIntoView(sn.dom.parentNode, false);
15032     },
15033     
15034     onViewScroll : function(e, t){
15035         
15036         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){
15037             return;
15038         }
15039         
15040         this.hasQuery = true;
15041         
15042         this.loading = this.list.select('.loading', true).first();
15043         
15044         if(this.loading === null){
15045             this.list.createChild({
15046                 tag: 'div',
15047                 cls: 'loading roo-select2-more-results roo-select2-active',
15048                 html: 'Loading more results...'
15049             });
15050             
15051             this.loading = this.list.select('.loading', true).first();
15052             
15053             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
15054             
15055             this.loading.hide();
15056         }
15057         
15058         this.loading.show();
15059         
15060         var _combo = this;
15061         
15062         this.page++;
15063         this.loadNext = true;
15064         
15065         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
15066         
15067         return;
15068     },
15069     
15070     addItem : function(o)
15071     {   
15072         var dv = ''; // display value
15073         
15074         if (this.displayField) {
15075             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
15076         } else {
15077             // this is an error condition!!!
15078             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
15079         }
15080         
15081         if(!dv.length){
15082             return;
15083         }
15084         
15085         var choice = this.choices.createChild({
15086             tag: 'li',
15087             cls: 'roo-select2-search-choice',
15088             cn: [
15089                 {
15090                     tag: 'div',
15091                     html: dv
15092                 },
15093                 {
15094                     tag: 'a',
15095                     href: '#',
15096                     cls: 'roo-select2-search-choice-close fa fa-times',
15097                     tabindex: '-1'
15098                 }
15099             ]
15100             
15101         }, this.searchField);
15102         
15103         var close = choice.select('a.roo-select2-search-choice-close', true).first();
15104         
15105         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
15106         
15107         this.item.push(o);
15108         
15109         this.lastData = o;
15110         
15111         this.syncValue();
15112         
15113         this.inputEl().dom.value = '';
15114         
15115         this.validate();
15116     },
15117     
15118     onRemoveItem : function(e, _self, o)
15119     {
15120         e.preventDefault();
15121         
15122         this.lastItem = Roo.apply([], this.item);
15123         
15124         var index = this.item.indexOf(o.data) * 1;
15125         
15126         if( index < 0){
15127             Roo.log('not this item?!');
15128             return;
15129         }
15130         
15131         this.item.splice(index, 1);
15132         o.item.remove();
15133         
15134         this.syncValue();
15135         
15136         this.fireEvent('remove', this, e);
15137         
15138         this.validate();
15139         
15140     },
15141     
15142     syncValue : function()
15143     {
15144         if(!this.item.length){
15145             this.clearValue();
15146             return;
15147         }
15148             
15149         var value = [];
15150         var _this = this;
15151         Roo.each(this.item, function(i){
15152             if(_this.valueField){
15153                 value.push(i[_this.valueField]);
15154                 return;
15155             }
15156
15157             value.push(i);
15158         });
15159
15160         this.value = value.join(',');
15161
15162         if(this.hiddenField){
15163             this.hiddenField.dom.value = this.value;
15164         }
15165         
15166         this.store.fireEvent("datachanged", this.store);
15167         
15168         this.validate();
15169     },
15170     
15171     clearItem : function()
15172     {
15173         if(!this.multiple){
15174             return;
15175         }
15176         
15177         this.item = [];
15178         
15179         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15180            c.remove();
15181         });
15182         
15183         this.syncValue();
15184         
15185         this.validate();
15186         
15187         if(this.tickable && !Roo.isTouch){
15188             this.view.refresh();
15189         }
15190     },
15191     
15192     inputEl: function ()
15193     {
15194         if(Roo.isIOS && this.useNativeIOS){
15195             return this.el.select('select.roo-ios-select', true).first();
15196         }
15197         
15198         if(Roo.isTouch && this.mobileTouchView){
15199             return this.el.select('input.form-control',true).first();
15200         }
15201         
15202         if(this.tickable){
15203             return this.searchField;
15204         }
15205         
15206         return this.el.select('input.form-control',true).first();
15207     },
15208     
15209     onTickableFooterButtonClick : function(e, btn, el)
15210     {
15211         e.preventDefault();
15212         
15213         this.lastItem = Roo.apply([], this.item);
15214         
15215         if(btn && btn.name == 'cancel'){
15216             this.tickItems = Roo.apply([], this.item);
15217             this.collapse();
15218             return;
15219         }
15220         
15221         this.clearItem();
15222         
15223         var _this = this;
15224         
15225         Roo.each(this.tickItems, function(o){
15226             _this.addItem(o);
15227         });
15228         
15229         this.collapse();
15230         
15231     },
15232     
15233     validate : function()
15234     {
15235         if(this.getVisibilityEl().hasClass('hidden')){
15236             return true;
15237         }
15238         
15239         var v = this.getRawValue();
15240         
15241         if(this.multiple){
15242             v = this.getValue();
15243         }
15244         
15245         if(this.disabled || this.allowBlank || v.length){
15246             this.markValid();
15247             return true;
15248         }
15249         
15250         this.markInvalid();
15251         return false;
15252     },
15253     
15254     tickableInputEl : function()
15255     {
15256         if(!this.tickable || !this.editable){
15257             return this.inputEl();
15258         }
15259         
15260         return this.inputEl().select('.roo-select2-search-field-input', true).first();
15261     },
15262     
15263     
15264     getAutoCreateTouchView : function()
15265     {
15266         var id = Roo.id();
15267         
15268         var cfg = {
15269             cls: 'form-group' //input-group
15270         };
15271         
15272         var input =  {
15273             tag: 'input',
15274             id : id,
15275             type : this.inputType,
15276             cls : 'form-control x-combo-noedit',
15277             autocomplete: 'new-password',
15278             placeholder : this.placeholder || '',
15279             readonly : true
15280         };
15281         
15282         if (this.name) {
15283             input.name = this.name;
15284         }
15285         
15286         if (this.size) {
15287             input.cls += ' input-' + this.size;
15288         }
15289         
15290         if (this.disabled) {
15291             input.disabled = true;
15292         }
15293         
15294         var inputblock = {
15295             cls : '',
15296             cn : [
15297                 input
15298             ]
15299         };
15300         
15301         if(this.before){
15302             inputblock.cls += ' input-group';
15303             
15304             inputblock.cn.unshift({
15305                 tag :'span',
15306                 cls : 'input-group-addon input-group-prepend input-group-text',
15307                 html : this.before
15308             });
15309         }
15310         
15311         if(this.removable && !this.multiple){
15312             inputblock.cls += ' roo-removable';
15313             
15314             inputblock.cn.push({
15315                 tag: 'button',
15316                 html : 'x',
15317                 cls : 'roo-combo-removable-btn close'
15318             });
15319         }
15320
15321         if(this.hasFeedback && !this.allowBlank){
15322             
15323             inputblock.cls += ' has-feedback';
15324             
15325             inputblock.cn.push({
15326                 tag: 'span',
15327                 cls: 'glyphicon form-control-feedback'
15328             });
15329             
15330         }
15331         
15332         if (this.after) {
15333             
15334             inputblock.cls += (this.before) ? '' : ' input-group';
15335             
15336             inputblock.cn.push({
15337                 tag :'span',
15338                 cls : 'input-group-addon input-group-append input-group-text',
15339                 html : this.after
15340             });
15341         }
15342
15343         
15344         var ibwrap = inputblock;
15345         
15346         if(this.multiple){
15347             ibwrap = {
15348                 tag: 'ul',
15349                 cls: 'roo-select2-choices',
15350                 cn:[
15351                     {
15352                         tag: 'li',
15353                         cls: 'roo-select2-search-field',
15354                         cn: [
15355
15356                             inputblock
15357                         ]
15358                     }
15359                 ]
15360             };
15361         
15362             
15363         }
15364         
15365         var combobox = {
15366             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15367             cn: [
15368                 {
15369                     tag: 'input',
15370                     type : 'hidden',
15371                     cls: 'form-hidden-field'
15372                 },
15373                 ibwrap
15374             ]
15375         };
15376         
15377         if(!this.multiple && this.showToggleBtn){
15378             
15379             var caret = {
15380                         tag: 'span',
15381                         cls: 'caret'
15382             };
15383             
15384             if (this.caret != false) {
15385                 caret = {
15386                      tag: 'i',
15387                      cls: 'fa fa-' + this.caret
15388                 };
15389                 
15390             }
15391             
15392             combobox.cn.push({
15393                 tag :'span',
15394                 cls : 'input-group-addon input-group-append input-group-text btn' +
15395                     (Roo.bootstrap.version == 3 ? ' dropdown-toggle' : ''),
15396                 cn : [
15397                     caret,
15398                     {
15399                         tag: 'span',
15400                         cls: 'combobox-clear',
15401                         cn  : [
15402                             {
15403                                 tag : 'i',
15404                                 cls: 'icon-remove'
15405                             }
15406                         ]
15407                     }
15408                 ]
15409
15410             })
15411         }
15412         
15413         if(this.multiple){
15414             combobox.cls += ' roo-select2-container-multi';
15415         }
15416         
15417         var align = this.labelAlign || this.parentLabelAlign();
15418         
15419         if (align ==='left' && this.fieldLabel.length) {
15420
15421             cfg.cn = [
15422                 {
15423                    tag : 'i',
15424                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15425                    tooltip : 'This field is required'
15426                 },
15427                 {
15428                     tag: 'label',
15429                     cls : 'control-label col-form-label',
15430                     html : this.fieldLabel
15431
15432                 },
15433                 {
15434                     cls : '', 
15435                     cn: [
15436                         combobox
15437                     ]
15438                 }
15439             ];
15440             
15441             var labelCfg = cfg.cn[1];
15442             var contentCfg = cfg.cn[2];
15443             
15444
15445             if(this.indicatorpos == 'right'){
15446                 cfg.cn = [
15447                     {
15448                         tag: 'label',
15449                         'for' :  id,
15450                         cls : 'control-label col-form-label',
15451                         cn : [
15452                             {
15453                                 tag : 'span',
15454                                 html : this.fieldLabel
15455                             },
15456                             {
15457                                 tag : 'i',
15458                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15459                                 tooltip : 'This field is required'
15460                             }
15461                         ]
15462                     },
15463                     {
15464                         cls : "",
15465                         cn: [
15466                             combobox
15467                         ]
15468                     }
15469
15470                 ];
15471                 
15472                 labelCfg = cfg.cn[0];
15473                 contentCfg = cfg.cn[1];
15474             }
15475             
15476            
15477             
15478             if(this.labelWidth > 12){
15479                 labelCfg.style = "width: " + this.labelWidth + 'px';
15480             }
15481             
15482             if(this.labelWidth < 13 && this.labelmd == 0){
15483                 this.labelmd = this.labelWidth;
15484             }
15485             
15486             if(this.labellg > 0){
15487                 labelCfg.cls += ' col-lg-' + this.labellg;
15488                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15489             }
15490             
15491             if(this.labelmd > 0){
15492                 labelCfg.cls += ' col-md-' + this.labelmd;
15493                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15494             }
15495             
15496             if(this.labelsm > 0){
15497                 labelCfg.cls += ' col-sm-' + this.labelsm;
15498                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15499             }
15500             
15501             if(this.labelxs > 0){
15502                 labelCfg.cls += ' col-xs-' + this.labelxs;
15503                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15504             }
15505                 
15506                 
15507         } else if ( this.fieldLabel.length) {
15508             cfg.cn = [
15509                 {
15510                    tag : 'i',
15511                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15512                    tooltip : 'This field is required'
15513                 },
15514                 {
15515                     tag: 'label',
15516                     cls : 'control-label',
15517                     html : this.fieldLabel
15518
15519                 },
15520                 {
15521                     cls : '', 
15522                     cn: [
15523                         combobox
15524                     ]
15525                 }
15526             ];
15527             
15528             if(this.indicatorpos == 'right'){
15529                 cfg.cn = [
15530                     {
15531                         tag: 'label',
15532                         cls : 'control-label',
15533                         html : this.fieldLabel,
15534                         cn : [
15535                             {
15536                                tag : 'i',
15537                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15538                                tooltip : 'This field is required'
15539                             }
15540                         ]
15541                     },
15542                     {
15543                         cls : '', 
15544                         cn: [
15545                             combobox
15546                         ]
15547                     }
15548                 ];
15549             }
15550         } else {
15551             cfg.cn = combobox;    
15552         }
15553         
15554         
15555         var settings = this;
15556         
15557         ['xs','sm','md','lg'].map(function(size){
15558             if (settings[size]) {
15559                 cfg.cls += ' col-' + size + '-' + settings[size];
15560             }
15561         });
15562         
15563         return cfg;
15564     },
15565     
15566     initTouchView : function()
15567     {
15568         this.renderTouchView();
15569         
15570         this.touchViewEl.on('scroll', function(){
15571             this.el.dom.scrollTop = 0;
15572         }, this);
15573         
15574         this.originalValue = this.getValue();
15575         
15576         this.triggerEl = this.el.select('span.input-group-append',true).first();
15577         
15578         this.inputEl().on("click", this.showTouchView, this);
15579         if (this.triggerEl) {
15580             this.triggerEl.on("click", this.showTouchView, this);
15581         }
15582         
15583         
15584         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15585         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15586         
15587         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15588         
15589         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15590         this.store.on('load', this.onTouchViewLoad, this);
15591         this.store.on('loadexception', this.onTouchViewLoadException, this);
15592         
15593         if(this.hiddenName){
15594             
15595             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15596             
15597             this.hiddenField.dom.value =
15598                 this.hiddenValue !== undefined ? this.hiddenValue :
15599                 this.value !== undefined ? this.value : '';
15600         
15601             this.el.dom.removeAttribute('name');
15602             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15603         }
15604         
15605         if(this.multiple){
15606             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15607             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15608         }
15609         
15610         if(this.removable && !this.multiple){
15611             var close = this.closeTriggerEl();
15612             if(close){
15613                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15614                 close.on('click', this.removeBtnClick, this, close);
15615             }
15616         }
15617         /*
15618          * fix the bug in Safari iOS8
15619          */
15620         this.inputEl().on("focus", function(e){
15621             document.activeElement.blur();
15622         }, this);
15623         
15624         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15625         
15626         return;
15627         
15628         
15629     },
15630     
15631     renderTouchView : function()
15632     {
15633         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15634         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15635         
15636         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15637         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15638         
15639         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15640         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15641         this.touchViewBodyEl.setStyle('overflow', 'auto');
15642         
15643         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15644         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15645         
15646         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15647         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15648         
15649     },
15650     
15651     showTouchView : function()
15652     {
15653         if(this.disabled){
15654             return;
15655         }
15656         
15657         this.touchViewHeaderEl.hide();
15658
15659         if(this.modalTitle.length){
15660             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15661             this.touchViewHeaderEl.show();
15662         }
15663
15664         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15665         this.touchViewEl.show();
15666
15667         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15668         
15669         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15670         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15671
15672         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15673
15674         if(this.modalTitle.length){
15675             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15676         }
15677         
15678         this.touchViewBodyEl.setHeight(bodyHeight);
15679
15680         if(this.animate){
15681             var _this = this;
15682             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15683         }else{
15684             this.touchViewEl.addClass('in');
15685         }
15686         
15687         if(this._touchViewMask){
15688             Roo.get(document.body).addClass("x-body-masked");
15689             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15690             this._touchViewMask.setStyle('z-index', 10000);
15691             this._touchViewMask.addClass('show');
15692         }
15693         
15694         this.doTouchViewQuery();
15695         
15696     },
15697     
15698     hideTouchView : function()
15699     {
15700         this.touchViewEl.removeClass('in');
15701
15702         if(this.animate){
15703             var _this = this;
15704             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15705         }else{
15706             this.touchViewEl.setStyle('display', 'none');
15707         }
15708         
15709         if(this._touchViewMask){
15710             this._touchViewMask.removeClass('show');
15711             Roo.get(document.body).removeClass("x-body-masked");
15712         }
15713     },
15714     
15715     setTouchViewValue : function()
15716     {
15717         if(this.multiple){
15718             this.clearItem();
15719         
15720             var _this = this;
15721
15722             Roo.each(this.tickItems, function(o){
15723                 this.addItem(o);
15724             }, this);
15725         }
15726         
15727         this.hideTouchView();
15728     },
15729     
15730     doTouchViewQuery : function()
15731     {
15732         var qe = {
15733             query: '',
15734             forceAll: true,
15735             combo: this,
15736             cancel:false
15737         };
15738         
15739         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15740             return false;
15741         }
15742         
15743         if(!this.alwaysQuery || this.mode == 'local'){
15744             this.onTouchViewLoad();
15745             return;
15746         }
15747         
15748         this.store.load();
15749     },
15750     
15751     onTouchViewBeforeLoad : function(combo,opts)
15752     {
15753         return;
15754     },
15755
15756     // private
15757     onTouchViewLoad : function()
15758     {
15759         if(this.store.getCount() < 1){
15760             this.onTouchViewEmptyResults();
15761             return;
15762         }
15763         
15764         this.clearTouchView();
15765         
15766         var rawValue = this.getRawValue();
15767         
15768         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15769         
15770         this.tickItems = [];
15771         
15772         this.store.data.each(function(d, rowIndex){
15773             var row = this.touchViewListGroup.createChild(template);
15774             
15775             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15776                 row.addClass(d.data.cls);
15777             }
15778             
15779             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15780                 var cfg = {
15781                     data : d.data,
15782                     html : d.data[this.displayField]
15783                 };
15784                 
15785                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15786                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15787                 }
15788             }
15789             row.removeClass('selected');
15790             if(!this.multiple && this.valueField &&
15791                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15792             {
15793                 // radio buttons..
15794                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15795                 row.addClass('selected');
15796             }
15797             
15798             if(this.multiple && this.valueField &&
15799                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15800             {
15801                 
15802                 // checkboxes...
15803                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15804                 this.tickItems.push(d.data);
15805             }
15806             
15807             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15808             
15809         }, this);
15810         
15811         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15812         
15813         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15814
15815         if(this.modalTitle.length){
15816             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15817         }
15818
15819         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15820         
15821         if(this.mobile_restrict_height && listHeight < bodyHeight){
15822             this.touchViewBodyEl.setHeight(listHeight);
15823         }
15824         
15825         var _this = this;
15826         
15827         if(firstChecked && listHeight > bodyHeight){
15828             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15829         }
15830         
15831     },
15832     
15833     onTouchViewLoadException : function()
15834     {
15835         this.hideTouchView();
15836     },
15837     
15838     onTouchViewEmptyResults : function()
15839     {
15840         this.clearTouchView();
15841         
15842         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15843         
15844         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15845         
15846     },
15847     
15848     clearTouchView : function()
15849     {
15850         this.touchViewListGroup.dom.innerHTML = '';
15851     },
15852     
15853     onTouchViewClick : function(e, el, o)
15854     {
15855         e.preventDefault();
15856         
15857         var row = o.row;
15858         var rowIndex = o.rowIndex;
15859         
15860         var r = this.store.getAt(rowIndex);
15861         
15862         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15863             
15864             if(!this.multiple){
15865                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15866                     c.dom.removeAttribute('checked');
15867                 }, this);
15868
15869                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15870
15871                 this.setFromData(r.data);
15872
15873                 var close = this.closeTriggerEl();
15874
15875                 if(close){
15876                     close.show();
15877                 }
15878
15879                 this.hideTouchView();
15880
15881                 this.fireEvent('select', this, r, rowIndex);
15882
15883                 return;
15884             }
15885
15886             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15887                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15888                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15889                 return;
15890             }
15891
15892             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15893             this.addItem(r.data);
15894             this.tickItems.push(r.data);
15895         }
15896     },
15897     
15898     getAutoCreateNativeIOS : function()
15899     {
15900         var cfg = {
15901             cls: 'form-group' //input-group,
15902         };
15903         
15904         var combobox =  {
15905             tag: 'select',
15906             cls : 'roo-ios-select'
15907         };
15908         
15909         if (this.name) {
15910             combobox.name = this.name;
15911         }
15912         
15913         if (this.disabled) {
15914             combobox.disabled = true;
15915         }
15916         
15917         var settings = this;
15918         
15919         ['xs','sm','md','lg'].map(function(size){
15920             if (settings[size]) {
15921                 cfg.cls += ' col-' + size + '-' + settings[size];
15922             }
15923         });
15924         
15925         cfg.cn = combobox;
15926         
15927         return cfg;
15928         
15929     },
15930     
15931     initIOSView : function()
15932     {
15933         this.store.on('load', this.onIOSViewLoad, this);
15934         
15935         return;
15936     },
15937     
15938     onIOSViewLoad : function()
15939     {
15940         if(this.store.getCount() < 1){
15941             return;
15942         }
15943         
15944         this.clearIOSView();
15945         
15946         if(this.allowBlank) {
15947             
15948             var default_text = '-- SELECT --';
15949             
15950             if(this.placeholder.length){
15951                 default_text = this.placeholder;
15952             }
15953             
15954             if(this.emptyTitle.length){
15955                 default_text += ' - ' + this.emptyTitle + ' -';
15956             }
15957             
15958             var opt = this.inputEl().createChild({
15959                 tag: 'option',
15960                 value : 0,
15961                 html : default_text
15962             });
15963             
15964             var o = {};
15965             o[this.valueField] = 0;
15966             o[this.displayField] = default_text;
15967             
15968             this.ios_options.push({
15969                 data : o,
15970                 el : opt
15971             });
15972             
15973         }
15974         
15975         this.store.data.each(function(d, rowIndex){
15976             
15977             var html = '';
15978             
15979             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15980                 html = d.data[this.displayField];
15981             }
15982             
15983             var value = '';
15984             
15985             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15986                 value = d.data[this.valueField];
15987             }
15988             
15989             var option = {
15990                 tag: 'option',
15991                 value : value,
15992                 html : html
15993             };
15994             
15995             if(this.value == d.data[this.valueField]){
15996                 option['selected'] = true;
15997             }
15998             
15999             var opt = this.inputEl().createChild(option);
16000             
16001             this.ios_options.push({
16002                 data : d.data,
16003                 el : opt
16004             });
16005             
16006         }, this);
16007         
16008         this.inputEl().on('change', function(){
16009            this.fireEvent('select', this);
16010         }, this);
16011         
16012     },
16013     
16014     clearIOSView: function()
16015     {
16016         this.inputEl().dom.innerHTML = '';
16017         
16018         this.ios_options = [];
16019     },
16020     
16021     setIOSValue: function(v)
16022     {
16023         this.value = v;
16024         
16025         if(!this.ios_options){
16026             return;
16027         }
16028         
16029         Roo.each(this.ios_options, function(opts){
16030            
16031            opts.el.dom.removeAttribute('selected');
16032            
16033            if(opts.data[this.valueField] != v){
16034                return;
16035            }
16036            
16037            opts.el.dom.setAttribute('selected', true);
16038            
16039         }, this);
16040     }
16041
16042     /** 
16043     * @cfg {Boolean} grow 
16044     * @hide 
16045     */
16046     /** 
16047     * @cfg {Number} growMin 
16048     * @hide 
16049     */
16050     /** 
16051     * @cfg {Number} growMax 
16052     * @hide 
16053     */
16054     /**
16055      * @hide
16056      * @method autoSize
16057      */
16058 });
16059
16060 Roo.apply(Roo.bootstrap.ComboBox,  {
16061     
16062     header : {
16063         tag: 'div',
16064         cls: 'modal-header',
16065         cn: [
16066             {
16067                 tag: 'h4',
16068                 cls: 'modal-title'
16069             }
16070         ]
16071     },
16072     
16073     body : {
16074         tag: 'div',
16075         cls: 'modal-body',
16076         cn: [
16077             {
16078                 tag: 'ul',
16079                 cls: 'list-group'
16080             }
16081         ]
16082     },
16083     
16084     listItemRadio : {
16085         tag: 'li',
16086         cls: 'list-group-item',
16087         cn: [
16088             {
16089                 tag: 'span',
16090                 cls: 'roo-combobox-list-group-item-value'
16091             },
16092             {
16093                 tag: 'div',
16094                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
16095                 cn: [
16096                     {
16097                         tag: 'input',
16098                         type: 'radio'
16099                     },
16100                     {
16101                         tag: 'label'
16102                     }
16103                 ]
16104             }
16105         ]
16106     },
16107     
16108     listItemCheckbox : {
16109         tag: 'li',
16110         cls: 'list-group-item',
16111         cn: [
16112             {
16113                 tag: 'span',
16114                 cls: 'roo-combobox-list-group-item-value'
16115             },
16116             {
16117                 tag: 'div',
16118                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
16119                 cn: [
16120                     {
16121                         tag: 'input',
16122                         type: 'checkbox'
16123                     },
16124                     {
16125                         tag: 'label'
16126                     }
16127                 ]
16128             }
16129         ]
16130     },
16131     
16132     emptyResult : {
16133         tag: 'div',
16134         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
16135     },
16136     
16137     footer : {
16138         tag: 'div',
16139         cls: 'modal-footer',
16140         cn: [
16141             {
16142                 tag: 'div',
16143                 cls: 'row',
16144                 cn: [
16145                     {
16146                         tag: 'div',
16147                         cls: 'col-xs-6 text-left',
16148                         cn: {
16149                             tag: 'button',
16150                             cls: 'btn btn-danger roo-touch-view-cancel',
16151                             html: 'Cancel'
16152                         }
16153                     },
16154                     {
16155                         tag: 'div',
16156                         cls: 'col-xs-6 text-right',
16157                         cn: {
16158                             tag: 'button',
16159                             cls: 'btn btn-success roo-touch-view-ok',
16160                             html: 'OK'
16161                         }
16162                     }
16163                 ]
16164             }
16165         ]
16166         
16167     }
16168 });
16169
16170 Roo.apply(Roo.bootstrap.ComboBox,  {
16171     
16172     touchViewTemplate : {
16173         tag: 'div',
16174         cls: 'modal fade roo-combobox-touch-view',
16175         cn: [
16176             {
16177                 tag: 'div',
16178                 cls: 'modal-dialog',
16179                 style : 'position:fixed', // we have to fix position....
16180                 cn: [
16181                     {
16182                         tag: 'div',
16183                         cls: 'modal-content',
16184                         cn: [
16185                             Roo.bootstrap.ComboBox.header,
16186                             Roo.bootstrap.ComboBox.body,
16187                             Roo.bootstrap.ComboBox.footer
16188                         ]
16189                     }
16190                 ]
16191             }
16192         ]
16193     }
16194 });/*
16195  * Based on:
16196  * Ext JS Library 1.1.1
16197  * Copyright(c) 2006-2007, Ext JS, LLC.
16198  *
16199  * Originally Released Under LGPL - original licence link has changed is not relivant.
16200  *
16201  * Fork - LGPL
16202  * <script type="text/javascript">
16203  */
16204
16205 /**
16206  * @class Roo.View
16207  * @extends Roo.util.Observable
16208  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
16209  * This class also supports single and multi selection modes. <br>
16210  * Create a data model bound view:
16211  <pre><code>
16212  var store = new Roo.data.Store(...);
16213
16214  var view = new Roo.View({
16215     el : "my-element",
16216     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
16217  
16218     singleSelect: true,
16219     selectedClass: "ydataview-selected",
16220     store: store
16221  });
16222
16223  // listen for node click?
16224  view.on("click", function(vw, index, node, e){
16225  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16226  });
16227
16228  // load XML data
16229  dataModel.load("foobar.xml");
16230  </code></pre>
16231  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16232  * <br><br>
16233  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16234  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16235  * 
16236  * Note: old style constructor is still suported (container, template, config)
16237  * 
16238  * @constructor
16239  * Create a new View
16240  * @param {Object} config The config object
16241  * 
16242  */
16243 Roo.View = function(config, depreciated_tpl, depreciated_config){
16244     
16245     this.parent = false;
16246     
16247     if (typeof(depreciated_tpl) == 'undefined') {
16248         // new way.. - universal constructor.
16249         Roo.apply(this, config);
16250         this.el  = Roo.get(this.el);
16251     } else {
16252         // old format..
16253         this.el  = Roo.get(config);
16254         this.tpl = depreciated_tpl;
16255         Roo.apply(this, depreciated_config);
16256     }
16257     this.wrapEl  = this.el.wrap().wrap();
16258     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16259     
16260     
16261     if(typeof(this.tpl) == "string"){
16262         this.tpl = new Roo.Template(this.tpl);
16263     } else {
16264         // support xtype ctors..
16265         this.tpl = new Roo.factory(this.tpl, Roo);
16266     }
16267     
16268     
16269     this.tpl.compile();
16270     
16271     /** @private */
16272     this.addEvents({
16273         /**
16274          * @event beforeclick
16275          * Fires before a click is processed. Returns false to cancel the default action.
16276          * @param {Roo.View} this
16277          * @param {Number} index The index of the target node
16278          * @param {HTMLElement} node The target node
16279          * @param {Roo.EventObject} e The raw event object
16280          */
16281             "beforeclick" : true,
16282         /**
16283          * @event click
16284          * Fires when a template node is clicked.
16285          * @param {Roo.View} this
16286          * @param {Number} index The index of the target node
16287          * @param {HTMLElement} node The target node
16288          * @param {Roo.EventObject} e The raw event object
16289          */
16290             "click" : true,
16291         /**
16292          * @event dblclick
16293          * Fires when a template node is double clicked.
16294          * @param {Roo.View} this
16295          * @param {Number} index The index of the target node
16296          * @param {HTMLElement} node The target node
16297          * @param {Roo.EventObject} e The raw event object
16298          */
16299             "dblclick" : true,
16300         /**
16301          * @event contextmenu
16302          * Fires when a template node is right clicked.
16303          * @param {Roo.View} this
16304          * @param {Number} index The index of the target node
16305          * @param {HTMLElement} node The target node
16306          * @param {Roo.EventObject} e The raw event object
16307          */
16308             "contextmenu" : true,
16309         /**
16310          * @event selectionchange
16311          * Fires when the selected nodes change.
16312          * @param {Roo.View} this
16313          * @param {Array} selections Array of the selected nodes
16314          */
16315             "selectionchange" : true,
16316     
16317         /**
16318          * @event beforeselect
16319          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16320          * @param {Roo.View} this
16321          * @param {HTMLElement} node The node to be selected
16322          * @param {Array} selections Array of currently selected nodes
16323          */
16324             "beforeselect" : true,
16325         /**
16326          * @event preparedata
16327          * Fires on every row to render, to allow you to change the data.
16328          * @param {Roo.View} this
16329          * @param {Object} data to be rendered (change this)
16330          */
16331           "preparedata" : true
16332           
16333           
16334         });
16335
16336
16337
16338     this.el.on({
16339         "click": this.onClick,
16340         "dblclick": this.onDblClick,
16341         "contextmenu": this.onContextMenu,
16342         scope:this
16343     });
16344
16345     this.selections = [];
16346     this.nodes = [];
16347     this.cmp = new Roo.CompositeElementLite([]);
16348     if(this.store){
16349         this.store = Roo.factory(this.store, Roo.data);
16350         this.setStore(this.store, true);
16351     }
16352     
16353     if ( this.footer && this.footer.xtype) {
16354            
16355          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16356         
16357         this.footer.dataSource = this.store;
16358         this.footer.container = fctr;
16359         this.footer = Roo.factory(this.footer, Roo);
16360         fctr.insertFirst(this.el);
16361         
16362         // this is a bit insane - as the paging toolbar seems to detach the el..
16363 //        dom.parentNode.parentNode.parentNode
16364          // they get detached?
16365     }
16366     
16367     
16368     Roo.View.superclass.constructor.call(this);
16369     
16370     
16371 };
16372
16373 Roo.extend(Roo.View, Roo.util.Observable, {
16374     
16375      /**
16376      * @cfg {Roo.data.Store} store Data store to load data from.
16377      */
16378     store : false,
16379     
16380     /**
16381      * @cfg {String|Roo.Element} el The container element.
16382      */
16383     el : '',
16384     
16385     /**
16386      * @cfg {String|Roo.Template} tpl The template used by this View 
16387      */
16388     tpl : false,
16389     /**
16390      * @cfg {String} dataName the named area of the template to use as the data area
16391      *                          Works with domtemplates roo-name="name"
16392      */
16393     dataName: false,
16394     /**
16395      * @cfg {String} selectedClass The css class to add to selected nodes
16396      */
16397     selectedClass : "x-view-selected",
16398      /**
16399      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16400      */
16401     emptyText : "",
16402     
16403     /**
16404      * @cfg {String} text to display on mask (default Loading)
16405      */
16406     mask : false,
16407     /**
16408      * @cfg {Boolean} multiSelect Allow multiple selection
16409      */
16410     multiSelect : false,
16411     /**
16412      * @cfg {Boolean} singleSelect Allow single selection
16413      */
16414     singleSelect:  false,
16415     
16416     /**
16417      * @cfg {Boolean} toggleSelect - selecting 
16418      */
16419     toggleSelect : false,
16420     
16421     /**
16422      * @cfg {Boolean} tickable - selecting 
16423      */
16424     tickable : false,
16425     
16426     /**
16427      * Returns the element this view is bound to.
16428      * @return {Roo.Element}
16429      */
16430     getEl : function(){
16431         return this.wrapEl;
16432     },
16433     
16434     
16435
16436     /**
16437      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16438      */
16439     refresh : function(){
16440         //Roo.log('refresh');
16441         var t = this.tpl;
16442         
16443         // if we are using something like 'domtemplate', then
16444         // the what gets used is:
16445         // t.applySubtemplate(NAME, data, wrapping data..)
16446         // the outer template then get' applied with
16447         //     the store 'extra data'
16448         // and the body get's added to the
16449         //      roo-name="data" node?
16450         //      <span class='roo-tpl-{name}'></span> ?????
16451         
16452         
16453         
16454         this.clearSelections();
16455         this.el.update("");
16456         var html = [];
16457         var records = this.store.getRange();
16458         if(records.length < 1) {
16459             
16460             // is this valid??  = should it render a template??
16461             
16462             this.el.update(this.emptyText);
16463             return;
16464         }
16465         var el = this.el;
16466         if (this.dataName) {
16467             this.el.update(t.apply(this.store.meta)); //????
16468             el = this.el.child('.roo-tpl-' + this.dataName);
16469         }
16470         
16471         for(var i = 0, len = records.length; i < len; i++){
16472             var data = this.prepareData(records[i].data, i, records[i]);
16473             this.fireEvent("preparedata", this, data, i, records[i]);
16474             
16475             var d = Roo.apply({}, data);
16476             
16477             if(this.tickable){
16478                 Roo.apply(d, {'roo-id' : Roo.id()});
16479                 
16480                 var _this = this;
16481             
16482                 Roo.each(this.parent.item, function(item){
16483                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16484                         return;
16485                     }
16486                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16487                 });
16488             }
16489             
16490             html[html.length] = Roo.util.Format.trim(
16491                 this.dataName ?
16492                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16493                     t.apply(d)
16494             );
16495         }
16496         
16497         
16498         
16499         el.update(html.join(""));
16500         this.nodes = el.dom.childNodes;
16501         this.updateIndexes(0);
16502     },
16503     
16504
16505     /**
16506      * Function to override to reformat the data that is sent to
16507      * the template for each node.
16508      * DEPRICATED - use the preparedata event handler.
16509      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16510      * a JSON object for an UpdateManager bound view).
16511      */
16512     prepareData : function(data, index, record)
16513     {
16514         this.fireEvent("preparedata", this, data, index, record);
16515         return data;
16516     },
16517
16518     onUpdate : function(ds, record){
16519         // Roo.log('on update');   
16520         this.clearSelections();
16521         var index = this.store.indexOf(record);
16522         var n = this.nodes[index];
16523         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16524         n.parentNode.removeChild(n);
16525         this.updateIndexes(index, index);
16526     },
16527
16528     
16529     
16530 // --------- FIXME     
16531     onAdd : function(ds, records, index)
16532     {
16533         //Roo.log(['on Add', ds, records, index] );        
16534         this.clearSelections();
16535         if(this.nodes.length == 0){
16536             this.refresh();
16537             return;
16538         }
16539         var n = this.nodes[index];
16540         for(var i = 0, len = records.length; i < len; i++){
16541             var d = this.prepareData(records[i].data, i, records[i]);
16542             if(n){
16543                 this.tpl.insertBefore(n, d);
16544             }else{
16545                 
16546                 this.tpl.append(this.el, d);
16547             }
16548         }
16549         this.updateIndexes(index);
16550     },
16551
16552     onRemove : function(ds, record, index){
16553        // Roo.log('onRemove');
16554         this.clearSelections();
16555         var el = this.dataName  ?
16556             this.el.child('.roo-tpl-' + this.dataName) :
16557             this.el; 
16558         
16559         el.dom.removeChild(this.nodes[index]);
16560         this.updateIndexes(index);
16561     },
16562
16563     /**
16564      * Refresh an individual node.
16565      * @param {Number} index
16566      */
16567     refreshNode : function(index){
16568         this.onUpdate(this.store, this.store.getAt(index));
16569     },
16570
16571     updateIndexes : function(startIndex, endIndex){
16572         var ns = this.nodes;
16573         startIndex = startIndex || 0;
16574         endIndex = endIndex || ns.length - 1;
16575         for(var i = startIndex; i <= endIndex; i++){
16576             ns[i].nodeIndex = i;
16577         }
16578     },
16579
16580     /**
16581      * Changes the data store this view uses and refresh the view.
16582      * @param {Store} store
16583      */
16584     setStore : function(store, initial){
16585         if(!initial && this.store){
16586             this.store.un("datachanged", this.refresh);
16587             this.store.un("add", this.onAdd);
16588             this.store.un("remove", this.onRemove);
16589             this.store.un("update", this.onUpdate);
16590             this.store.un("clear", this.refresh);
16591             this.store.un("beforeload", this.onBeforeLoad);
16592             this.store.un("load", this.onLoad);
16593             this.store.un("loadexception", this.onLoad);
16594         }
16595         if(store){
16596           
16597             store.on("datachanged", this.refresh, this);
16598             store.on("add", this.onAdd, this);
16599             store.on("remove", this.onRemove, this);
16600             store.on("update", this.onUpdate, this);
16601             store.on("clear", this.refresh, this);
16602             store.on("beforeload", this.onBeforeLoad, this);
16603             store.on("load", this.onLoad, this);
16604             store.on("loadexception", this.onLoad, this);
16605         }
16606         
16607         if(store){
16608             this.refresh();
16609         }
16610     },
16611     /**
16612      * onbeforeLoad - masks the loading area.
16613      *
16614      */
16615     onBeforeLoad : function(store,opts)
16616     {
16617          //Roo.log('onBeforeLoad');   
16618         if (!opts.add) {
16619             this.el.update("");
16620         }
16621         this.el.mask(this.mask ? this.mask : "Loading" ); 
16622     },
16623     onLoad : function ()
16624     {
16625         this.el.unmask();
16626     },
16627     
16628
16629     /**
16630      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16631      * @param {HTMLElement} node
16632      * @return {HTMLElement} The template node
16633      */
16634     findItemFromChild : function(node){
16635         var el = this.dataName  ?
16636             this.el.child('.roo-tpl-' + this.dataName,true) :
16637             this.el.dom; 
16638         
16639         if(!node || node.parentNode == el){
16640                     return node;
16641             }
16642             var p = node.parentNode;
16643             while(p && p != el){
16644             if(p.parentNode == el){
16645                 return p;
16646             }
16647             p = p.parentNode;
16648         }
16649             return null;
16650     },
16651
16652     /** @ignore */
16653     onClick : function(e){
16654         var item = this.findItemFromChild(e.getTarget());
16655         if(item){
16656             var index = this.indexOf(item);
16657             if(this.onItemClick(item, index, e) !== false){
16658                 this.fireEvent("click", this, index, item, e);
16659             }
16660         }else{
16661             this.clearSelections();
16662         }
16663     },
16664
16665     /** @ignore */
16666     onContextMenu : function(e){
16667         var item = this.findItemFromChild(e.getTarget());
16668         if(item){
16669             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16670         }
16671     },
16672
16673     /** @ignore */
16674     onDblClick : function(e){
16675         var item = this.findItemFromChild(e.getTarget());
16676         if(item){
16677             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16678         }
16679     },
16680
16681     onItemClick : function(item, index, e)
16682     {
16683         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16684             return false;
16685         }
16686         if (this.toggleSelect) {
16687             var m = this.isSelected(item) ? 'unselect' : 'select';
16688             //Roo.log(m);
16689             var _t = this;
16690             _t[m](item, true, false);
16691             return true;
16692         }
16693         if(this.multiSelect || this.singleSelect){
16694             if(this.multiSelect && e.shiftKey && this.lastSelection){
16695                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16696             }else{
16697                 this.select(item, this.multiSelect && e.ctrlKey);
16698                 this.lastSelection = item;
16699             }
16700             
16701             if(!this.tickable){
16702                 e.preventDefault();
16703             }
16704             
16705         }
16706         return true;
16707     },
16708
16709     /**
16710      * Get the number of selected nodes.
16711      * @return {Number}
16712      */
16713     getSelectionCount : function(){
16714         return this.selections.length;
16715     },
16716
16717     /**
16718      * Get the currently selected nodes.
16719      * @return {Array} An array of HTMLElements
16720      */
16721     getSelectedNodes : function(){
16722         return this.selections;
16723     },
16724
16725     /**
16726      * Get the indexes of the selected nodes.
16727      * @return {Array}
16728      */
16729     getSelectedIndexes : function(){
16730         var indexes = [], s = this.selections;
16731         for(var i = 0, len = s.length; i < len; i++){
16732             indexes.push(s[i].nodeIndex);
16733         }
16734         return indexes;
16735     },
16736
16737     /**
16738      * Clear all selections
16739      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16740      */
16741     clearSelections : function(suppressEvent){
16742         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16743             this.cmp.elements = this.selections;
16744             this.cmp.removeClass(this.selectedClass);
16745             this.selections = [];
16746             if(!suppressEvent){
16747                 this.fireEvent("selectionchange", this, this.selections);
16748             }
16749         }
16750     },
16751
16752     /**
16753      * Returns true if the passed node is selected
16754      * @param {HTMLElement/Number} node The node or node index
16755      * @return {Boolean}
16756      */
16757     isSelected : function(node){
16758         var s = this.selections;
16759         if(s.length < 1){
16760             return false;
16761         }
16762         node = this.getNode(node);
16763         return s.indexOf(node) !== -1;
16764     },
16765
16766     /**
16767      * Selects nodes.
16768      * @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
16769      * @param {Boolean} keepExisting (optional) true to keep existing selections
16770      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16771      */
16772     select : function(nodeInfo, keepExisting, suppressEvent){
16773         if(nodeInfo instanceof Array){
16774             if(!keepExisting){
16775                 this.clearSelections(true);
16776             }
16777             for(var i = 0, len = nodeInfo.length; i < len; i++){
16778                 this.select(nodeInfo[i], true, true);
16779             }
16780             return;
16781         } 
16782         var node = this.getNode(nodeInfo);
16783         if(!node || this.isSelected(node)){
16784             return; // already selected.
16785         }
16786         if(!keepExisting){
16787             this.clearSelections(true);
16788         }
16789         
16790         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16791             Roo.fly(node).addClass(this.selectedClass);
16792             this.selections.push(node);
16793             if(!suppressEvent){
16794                 this.fireEvent("selectionchange", this, this.selections);
16795             }
16796         }
16797         
16798         
16799     },
16800       /**
16801      * Unselects nodes.
16802      * @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
16803      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16804      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16805      */
16806     unselect : function(nodeInfo, keepExisting, suppressEvent)
16807     {
16808         if(nodeInfo instanceof Array){
16809             Roo.each(this.selections, function(s) {
16810                 this.unselect(s, nodeInfo);
16811             }, this);
16812             return;
16813         }
16814         var node = this.getNode(nodeInfo);
16815         if(!node || !this.isSelected(node)){
16816             //Roo.log("not selected");
16817             return; // not selected.
16818         }
16819         // fireevent???
16820         var ns = [];
16821         Roo.each(this.selections, function(s) {
16822             if (s == node ) {
16823                 Roo.fly(node).removeClass(this.selectedClass);
16824
16825                 return;
16826             }
16827             ns.push(s);
16828         },this);
16829         
16830         this.selections= ns;
16831         this.fireEvent("selectionchange", this, this.selections);
16832     },
16833
16834     /**
16835      * Gets a template node.
16836      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16837      * @return {HTMLElement} The node or null if it wasn't found
16838      */
16839     getNode : function(nodeInfo){
16840         if(typeof nodeInfo == "string"){
16841             return document.getElementById(nodeInfo);
16842         }else if(typeof nodeInfo == "number"){
16843             return this.nodes[nodeInfo];
16844         }
16845         return nodeInfo;
16846     },
16847
16848     /**
16849      * Gets a range template nodes.
16850      * @param {Number} startIndex
16851      * @param {Number} endIndex
16852      * @return {Array} An array of nodes
16853      */
16854     getNodes : function(start, end){
16855         var ns = this.nodes;
16856         start = start || 0;
16857         end = typeof end == "undefined" ? ns.length - 1 : end;
16858         var nodes = [];
16859         if(start <= end){
16860             for(var i = start; i <= end; i++){
16861                 nodes.push(ns[i]);
16862             }
16863         } else{
16864             for(var i = start; i >= end; i--){
16865                 nodes.push(ns[i]);
16866             }
16867         }
16868         return nodes;
16869     },
16870
16871     /**
16872      * Finds the index of the passed node
16873      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16874      * @return {Number} The index of the node or -1
16875      */
16876     indexOf : function(node){
16877         node = this.getNode(node);
16878         if(typeof node.nodeIndex == "number"){
16879             return node.nodeIndex;
16880         }
16881         var ns = this.nodes;
16882         for(var i = 0, len = ns.length; i < len; i++){
16883             if(ns[i] == node){
16884                 return i;
16885             }
16886         }
16887         return -1;
16888     }
16889 });
16890 /*
16891  * - LGPL
16892  *
16893  * based on jquery fullcalendar
16894  * 
16895  */
16896
16897 Roo.bootstrap = Roo.bootstrap || {};
16898 /**
16899  * @class Roo.bootstrap.Calendar
16900  * @extends Roo.bootstrap.Component
16901  * Bootstrap Calendar class
16902  * @cfg {Boolean} loadMask (true|false) default false
16903  * @cfg {Object} header generate the user specific header of the calendar, default false
16904
16905  * @constructor
16906  * Create a new Container
16907  * @param {Object} config The config object
16908  */
16909
16910
16911
16912 Roo.bootstrap.Calendar = function(config){
16913     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16914      this.addEvents({
16915         /**
16916              * @event select
16917              * Fires when a date is selected
16918              * @param {DatePicker} this
16919              * @param {Date} date The selected date
16920              */
16921         'select': true,
16922         /**
16923              * @event monthchange
16924              * Fires when the displayed month changes 
16925              * @param {DatePicker} this
16926              * @param {Date} date The selected month
16927              */
16928         'monthchange': true,
16929         /**
16930              * @event evententer
16931              * Fires when mouse over an event
16932              * @param {Calendar} this
16933              * @param {event} Event
16934              */
16935         'evententer': true,
16936         /**
16937              * @event eventleave
16938              * Fires when the mouse leaves an
16939              * @param {Calendar} this
16940              * @param {event}
16941              */
16942         'eventleave': true,
16943         /**
16944              * @event eventclick
16945              * Fires when the mouse click an
16946              * @param {Calendar} this
16947              * @param {event}
16948              */
16949         'eventclick': true
16950         
16951     });
16952
16953 };
16954
16955 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16956     
16957      /**
16958      * @cfg {Number} startDay
16959      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16960      */
16961     startDay : 0,
16962     
16963     loadMask : false,
16964     
16965     header : false,
16966       
16967     getAutoCreate : function(){
16968         
16969         
16970         var fc_button = function(name, corner, style, content ) {
16971             return Roo.apply({},{
16972                 tag : 'span',
16973                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16974                          (corner.length ?
16975                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16976                             ''
16977                         ),
16978                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16979                 unselectable: 'on'
16980             });
16981         };
16982         
16983         var header = {};
16984         
16985         if(!this.header){
16986             header = {
16987                 tag : 'table',
16988                 cls : 'fc-header',
16989                 style : 'width:100%',
16990                 cn : [
16991                     {
16992                         tag: 'tr',
16993                         cn : [
16994                             {
16995                                 tag : 'td',
16996                                 cls : 'fc-header-left',
16997                                 cn : [
16998                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16999                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
17000                                     { tag: 'span', cls: 'fc-header-space' },
17001                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
17002
17003
17004                                 ]
17005                             },
17006
17007                             {
17008                                 tag : 'td',
17009                                 cls : 'fc-header-center',
17010                                 cn : [
17011                                     {
17012                                         tag: 'span',
17013                                         cls: 'fc-header-title',
17014                                         cn : {
17015                                             tag: 'H2',
17016                                             html : 'month / year'
17017                                         }
17018                                     }
17019
17020                                 ]
17021                             },
17022                             {
17023                                 tag : 'td',
17024                                 cls : 'fc-header-right',
17025                                 cn : [
17026                               /*      fc_button('month', 'left', '', 'month' ),
17027                                     fc_button('week', '', '', 'week' ),
17028                                     fc_button('day', 'right', '', 'day' )
17029                                 */    
17030
17031                                 ]
17032                             }
17033
17034                         ]
17035                     }
17036                 ]
17037             };
17038         }
17039         
17040         header = this.header;
17041         
17042        
17043         var cal_heads = function() {
17044             var ret = [];
17045             // fixme - handle this.
17046             
17047             for (var i =0; i < Date.dayNames.length; i++) {
17048                 var d = Date.dayNames[i];
17049                 ret.push({
17050                     tag: 'th',
17051                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
17052                     html : d.substring(0,3)
17053                 });
17054                 
17055             }
17056             ret[0].cls += ' fc-first';
17057             ret[6].cls += ' fc-last';
17058             return ret;
17059         };
17060         var cal_cell = function(n) {
17061             return  {
17062                 tag: 'td',
17063                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
17064                 cn : [
17065                     {
17066                         cn : [
17067                             {
17068                                 cls: 'fc-day-number',
17069                                 html: 'D'
17070                             },
17071                             {
17072                                 cls: 'fc-day-content',
17073                              
17074                                 cn : [
17075                                      {
17076                                         style: 'position: relative;' // height: 17px;
17077                                     }
17078                                 ]
17079                             }
17080                             
17081                             
17082                         ]
17083                     }
17084                 ]
17085                 
17086             }
17087         };
17088         var cal_rows = function() {
17089             
17090             var ret = [];
17091             for (var r = 0; r < 6; r++) {
17092                 var row= {
17093                     tag : 'tr',
17094                     cls : 'fc-week',
17095                     cn : []
17096                 };
17097                 
17098                 for (var i =0; i < Date.dayNames.length; i++) {
17099                     var d = Date.dayNames[i];
17100                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
17101
17102                 }
17103                 row.cn[0].cls+=' fc-first';
17104                 row.cn[0].cn[0].style = 'min-height:90px';
17105                 row.cn[6].cls+=' fc-last';
17106                 ret.push(row);
17107                 
17108             }
17109             ret[0].cls += ' fc-first';
17110             ret[4].cls += ' fc-prev-last';
17111             ret[5].cls += ' fc-last';
17112             return ret;
17113             
17114         };
17115         
17116         var cal_table = {
17117             tag: 'table',
17118             cls: 'fc-border-separate',
17119             style : 'width:100%',
17120             cellspacing  : 0,
17121             cn : [
17122                 { 
17123                     tag: 'thead',
17124                     cn : [
17125                         { 
17126                             tag: 'tr',
17127                             cls : 'fc-first fc-last',
17128                             cn : cal_heads()
17129                         }
17130                     ]
17131                 },
17132                 { 
17133                     tag: 'tbody',
17134                     cn : cal_rows()
17135                 }
17136                   
17137             ]
17138         };
17139          
17140          var cfg = {
17141             cls : 'fc fc-ltr',
17142             cn : [
17143                 header,
17144                 {
17145                     cls : 'fc-content',
17146                     style : "position: relative;",
17147                     cn : [
17148                         {
17149                             cls : 'fc-view fc-view-month fc-grid',
17150                             style : 'position: relative',
17151                             unselectable : 'on',
17152                             cn : [
17153                                 {
17154                                     cls : 'fc-event-container',
17155                                     style : 'position:absolute;z-index:8;top:0;left:0;'
17156                                 },
17157                                 cal_table
17158                             ]
17159                         }
17160                     ]
17161     
17162                 }
17163            ] 
17164             
17165         };
17166         
17167          
17168         
17169         return cfg;
17170     },
17171     
17172     
17173     initEvents : function()
17174     {
17175         if(!this.store){
17176             throw "can not find store for calendar";
17177         }
17178         
17179         var mark = {
17180             tag: "div",
17181             cls:"x-dlg-mask",
17182             style: "text-align:center",
17183             cn: [
17184                 {
17185                     tag: "div",
17186                     style: "background-color:white;width:50%;margin:250 auto",
17187                     cn: [
17188                         {
17189                             tag: "img",
17190                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
17191                         },
17192                         {
17193                             tag: "span",
17194                             html: "Loading"
17195                         }
17196                         
17197                     ]
17198                 }
17199             ]
17200         };
17201         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17202         
17203         var size = this.el.select('.fc-content', true).first().getSize();
17204         this.maskEl.setSize(size.width, size.height);
17205         this.maskEl.enableDisplayMode("block");
17206         if(!this.loadMask){
17207             this.maskEl.hide();
17208         }
17209         
17210         this.store = Roo.factory(this.store, Roo.data);
17211         this.store.on('load', this.onLoad, this);
17212         this.store.on('beforeload', this.onBeforeLoad, this);
17213         
17214         this.resize();
17215         
17216         this.cells = this.el.select('.fc-day',true);
17217         //Roo.log(this.cells);
17218         this.textNodes = this.el.query('.fc-day-number');
17219         this.cells.addClassOnOver('fc-state-hover');
17220         
17221         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17222         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17223         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17224         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17225         
17226         this.on('monthchange', this.onMonthChange, this);
17227         
17228         this.update(new Date().clearTime());
17229     },
17230     
17231     resize : function() {
17232         var sz  = this.el.getSize();
17233         
17234         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17235         this.el.select('.fc-day-content div',true).setHeight(34);
17236     },
17237     
17238     
17239     // private
17240     showPrevMonth : function(e){
17241         this.update(this.activeDate.add("mo", -1));
17242     },
17243     showToday : function(e){
17244         this.update(new Date().clearTime());
17245     },
17246     // private
17247     showNextMonth : function(e){
17248         this.update(this.activeDate.add("mo", 1));
17249     },
17250
17251     // private
17252     showPrevYear : function(){
17253         this.update(this.activeDate.add("y", -1));
17254     },
17255
17256     // private
17257     showNextYear : function(){
17258         this.update(this.activeDate.add("y", 1));
17259     },
17260
17261     
17262    // private
17263     update : function(date)
17264     {
17265         var vd = this.activeDate;
17266         this.activeDate = date;
17267 //        if(vd && this.el){
17268 //            var t = date.getTime();
17269 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17270 //                Roo.log('using add remove');
17271 //                
17272 //                this.fireEvent('monthchange', this, date);
17273 //                
17274 //                this.cells.removeClass("fc-state-highlight");
17275 //                this.cells.each(function(c){
17276 //                   if(c.dateValue == t){
17277 //                       c.addClass("fc-state-highlight");
17278 //                       setTimeout(function(){
17279 //                            try{c.dom.firstChild.focus();}catch(e){}
17280 //                       }, 50);
17281 //                       return false;
17282 //                   }
17283 //                   return true;
17284 //                });
17285 //                return;
17286 //            }
17287 //        }
17288         
17289         var days = date.getDaysInMonth();
17290         
17291         var firstOfMonth = date.getFirstDateOfMonth();
17292         var startingPos = firstOfMonth.getDay()-this.startDay;
17293         
17294         if(startingPos < this.startDay){
17295             startingPos += 7;
17296         }
17297         
17298         var pm = date.add(Date.MONTH, -1);
17299         var prevStart = pm.getDaysInMonth()-startingPos;
17300 //        
17301         this.cells = this.el.select('.fc-day',true);
17302         this.textNodes = this.el.query('.fc-day-number');
17303         this.cells.addClassOnOver('fc-state-hover');
17304         
17305         var cells = this.cells.elements;
17306         var textEls = this.textNodes;
17307         
17308         Roo.each(cells, function(cell){
17309             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17310         });
17311         
17312         days += startingPos;
17313
17314         // convert everything to numbers so it's fast
17315         var day = 86400000;
17316         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17317         //Roo.log(d);
17318         //Roo.log(pm);
17319         //Roo.log(prevStart);
17320         
17321         var today = new Date().clearTime().getTime();
17322         var sel = date.clearTime().getTime();
17323         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17324         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17325         var ddMatch = this.disabledDatesRE;
17326         var ddText = this.disabledDatesText;
17327         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17328         var ddaysText = this.disabledDaysText;
17329         var format = this.format;
17330         
17331         var setCellClass = function(cal, cell){
17332             cell.row = 0;
17333             cell.events = [];
17334             cell.more = [];
17335             //Roo.log('set Cell Class');
17336             cell.title = "";
17337             var t = d.getTime();
17338             
17339             //Roo.log(d);
17340             
17341             cell.dateValue = t;
17342             if(t == today){
17343                 cell.className += " fc-today";
17344                 cell.className += " fc-state-highlight";
17345                 cell.title = cal.todayText;
17346             }
17347             if(t == sel){
17348                 // disable highlight in other month..
17349                 //cell.className += " fc-state-highlight";
17350                 
17351             }
17352             // disabling
17353             if(t < min) {
17354                 cell.className = " fc-state-disabled";
17355                 cell.title = cal.minText;
17356                 return;
17357             }
17358             if(t > max) {
17359                 cell.className = " fc-state-disabled";
17360                 cell.title = cal.maxText;
17361                 return;
17362             }
17363             if(ddays){
17364                 if(ddays.indexOf(d.getDay()) != -1){
17365                     cell.title = ddaysText;
17366                     cell.className = " fc-state-disabled";
17367                 }
17368             }
17369             if(ddMatch && format){
17370                 var fvalue = d.dateFormat(format);
17371                 if(ddMatch.test(fvalue)){
17372                     cell.title = ddText.replace("%0", fvalue);
17373                     cell.className = " fc-state-disabled";
17374                 }
17375             }
17376             
17377             if (!cell.initialClassName) {
17378                 cell.initialClassName = cell.dom.className;
17379             }
17380             
17381             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17382         };
17383
17384         var i = 0;
17385         
17386         for(; i < startingPos; i++) {
17387             textEls[i].innerHTML = (++prevStart);
17388             d.setDate(d.getDate()+1);
17389             
17390             cells[i].className = "fc-past fc-other-month";
17391             setCellClass(this, cells[i]);
17392         }
17393         
17394         var intDay = 0;
17395         
17396         for(; i < days; i++){
17397             intDay = i - startingPos + 1;
17398             textEls[i].innerHTML = (intDay);
17399             d.setDate(d.getDate()+1);
17400             
17401             cells[i].className = ''; // "x-date-active";
17402             setCellClass(this, cells[i]);
17403         }
17404         var extraDays = 0;
17405         
17406         for(; i < 42; i++) {
17407             textEls[i].innerHTML = (++extraDays);
17408             d.setDate(d.getDate()+1);
17409             
17410             cells[i].className = "fc-future fc-other-month";
17411             setCellClass(this, cells[i]);
17412         }
17413         
17414         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17415         
17416         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17417         
17418         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17419         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17420         
17421         if(totalRows != 6){
17422             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17423             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17424         }
17425         
17426         this.fireEvent('monthchange', this, date);
17427         
17428         
17429         /*
17430         if(!this.internalRender){
17431             var main = this.el.dom.firstChild;
17432             var w = main.offsetWidth;
17433             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17434             Roo.fly(main).setWidth(w);
17435             this.internalRender = true;
17436             // opera does not respect the auto grow header center column
17437             // then, after it gets a width opera refuses to recalculate
17438             // without a second pass
17439             if(Roo.isOpera && !this.secondPass){
17440                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17441                 this.secondPass = true;
17442                 this.update.defer(10, this, [date]);
17443             }
17444         }
17445         */
17446         
17447     },
17448     
17449     findCell : function(dt) {
17450         dt = dt.clearTime().getTime();
17451         var ret = false;
17452         this.cells.each(function(c){
17453             //Roo.log("check " +c.dateValue + '?=' + dt);
17454             if(c.dateValue == dt){
17455                 ret = c;
17456                 return false;
17457             }
17458             return true;
17459         });
17460         
17461         return ret;
17462     },
17463     
17464     findCells : function(ev) {
17465         var s = ev.start.clone().clearTime().getTime();
17466        // Roo.log(s);
17467         var e= ev.end.clone().clearTime().getTime();
17468        // Roo.log(e);
17469         var ret = [];
17470         this.cells.each(function(c){
17471              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17472             
17473             if(c.dateValue > e){
17474                 return ;
17475             }
17476             if(c.dateValue < s){
17477                 return ;
17478             }
17479             ret.push(c);
17480         });
17481         
17482         return ret;    
17483     },
17484     
17485 //    findBestRow: function(cells)
17486 //    {
17487 //        var ret = 0;
17488 //        
17489 //        for (var i =0 ; i < cells.length;i++) {
17490 //            ret  = Math.max(cells[i].rows || 0,ret);
17491 //        }
17492 //        return ret;
17493 //        
17494 //    },
17495     
17496     
17497     addItem : function(ev)
17498     {
17499         // look for vertical location slot in
17500         var cells = this.findCells(ev);
17501         
17502 //        ev.row = this.findBestRow(cells);
17503         
17504         // work out the location.
17505         
17506         var crow = false;
17507         var rows = [];
17508         for(var i =0; i < cells.length; i++) {
17509             
17510             cells[i].row = cells[0].row;
17511             
17512             if(i == 0){
17513                 cells[i].row = cells[i].row + 1;
17514             }
17515             
17516             if (!crow) {
17517                 crow = {
17518                     start : cells[i],
17519                     end :  cells[i]
17520                 };
17521                 continue;
17522             }
17523             if (crow.start.getY() == cells[i].getY()) {
17524                 // on same row.
17525                 crow.end = cells[i];
17526                 continue;
17527             }
17528             // different row.
17529             rows.push(crow);
17530             crow = {
17531                 start: cells[i],
17532                 end : cells[i]
17533             };
17534             
17535         }
17536         
17537         rows.push(crow);
17538         ev.els = [];
17539         ev.rows = rows;
17540         ev.cells = cells;
17541         
17542         cells[0].events.push(ev);
17543         
17544         this.calevents.push(ev);
17545     },
17546     
17547     clearEvents: function() {
17548         
17549         if(!this.calevents){
17550             return;
17551         }
17552         
17553         Roo.each(this.cells.elements, function(c){
17554             c.row = 0;
17555             c.events = [];
17556             c.more = [];
17557         });
17558         
17559         Roo.each(this.calevents, function(e) {
17560             Roo.each(e.els, function(el) {
17561                 el.un('mouseenter' ,this.onEventEnter, this);
17562                 el.un('mouseleave' ,this.onEventLeave, this);
17563                 el.remove();
17564             },this);
17565         },this);
17566         
17567         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17568             e.remove();
17569         });
17570         
17571     },
17572     
17573     renderEvents: function()
17574     {   
17575         var _this = this;
17576         
17577         this.cells.each(function(c) {
17578             
17579             if(c.row < 5){
17580                 return;
17581             }
17582             
17583             var ev = c.events;
17584             
17585             var r = 4;
17586             if(c.row != c.events.length){
17587                 r = 4 - (4 - (c.row - c.events.length));
17588             }
17589             
17590             c.events = ev.slice(0, r);
17591             c.more = ev.slice(r);
17592             
17593             if(c.more.length && c.more.length == 1){
17594                 c.events.push(c.more.pop());
17595             }
17596             
17597             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17598             
17599         });
17600             
17601         this.cells.each(function(c) {
17602             
17603             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17604             
17605             
17606             for (var e = 0; e < c.events.length; e++){
17607                 var ev = c.events[e];
17608                 var rows = ev.rows;
17609                 
17610                 for(var i = 0; i < rows.length; i++) {
17611                 
17612                     // how many rows should it span..
17613
17614                     var  cfg = {
17615                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17616                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17617
17618                         unselectable : "on",
17619                         cn : [
17620                             {
17621                                 cls: 'fc-event-inner',
17622                                 cn : [
17623     //                                {
17624     //                                  tag:'span',
17625     //                                  cls: 'fc-event-time',
17626     //                                  html : cells.length > 1 ? '' : ev.time
17627     //                                },
17628                                     {
17629                                       tag:'span',
17630                                       cls: 'fc-event-title',
17631                                       html : String.format('{0}', ev.title)
17632                                     }
17633
17634
17635                                 ]
17636                             },
17637                             {
17638                                 cls: 'ui-resizable-handle ui-resizable-e',
17639                                 html : '&nbsp;&nbsp;&nbsp'
17640                             }
17641
17642                         ]
17643                     };
17644
17645                     if (i == 0) {
17646                         cfg.cls += ' fc-event-start';
17647                     }
17648                     if ((i+1) == rows.length) {
17649                         cfg.cls += ' fc-event-end';
17650                     }
17651
17652                     var ctr = _this.el.select('.fc-event-container',true).first();
17653                     var cg = ctr.createChild(cfg);
17654
17655                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17656                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17657
17658                     var r = (c.more.length) ? 1 : 0;
17659                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17660                     cg.setWidth(ebox.right - sbox.x -2);
17661
17662                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17663                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17664                     cg.on('click', _this.onEventClick, _this, ev);
17665
17666                     ev.els.push(cg);
17667                     
17668                 }
17669                 
17670             }
17671             
17672             
17673             if(c.more.length){
17674                 var  cfg = {
17675                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17676                     style : 'position: absolute',
17677                     unselectable : "on",
17678                     cn : [
17679                         {
17680                             cls: 'fc-event-inner',
17681                             cn : [
17682                                 {
17683                                   tag:'span',
17684                                   cls: 'fc-event-title',
17685                                   html : 'More'
17686                                 }
17687
17688
17689                             ]
17690                         },
17691                         {
17692                             cls: 'ui-resizable-handle ui-resizable-e',
17693                             html : '&nbsp;&nbsp;&nbsp'
17694                         }
17695
17696                     ]
17697                 };
17698
17699                 var ctr = _this.el.select('.fc-event-container',true).first();
17700                 var cg = ctr.createChild(cfg);
17701
17702                 var sbox = c.select('.fc-day-content',true).first().getBox();
17703                 var ebox = c.select('.fc-day-content',true).first().getBox();
17704                 //Roo.log(cg);
17705                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17706                 cg.setWidth(ebox.right - sbox.x -2);
17707
17708                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17709                 
17710             }
17711             
17712         });
17713         
17714         
17715         
17716     },
17717     
17718     onEventEnter: function (e, el,event,d) {
17719         this.fireEvent('evententer', this, el, event);
17720     },
17721     
17722     onEventLeave: function (e, el,event,d) {
17723         this.fireEvent('eventleave', this, el, event);
17724     },
17725     
17726     onEventClick: function (e, el,event,d) {
17727         this.fireEvent('eventclick', this, el, event);
17728     },
17729     
17730     onMonthChange: function () {
17731         this.store.load();
17732     },
17733     
17734     onMoreEventClick: function(e, el, more)
17735     {
17736         var _this = this;
17737         
17738         this.calpopover.placement = 'right';
17739         this.calpopover.setTitle('More');
17740         
17741         this.calpopover.setContent('');
17742         
17743         var ctr = this.calpopover.el.select('.popover-content', true).first();
17744         
17745         Roo.each(more, function(m){
17746             var cfg = {
17747                 cls : 'fc-event-hori fc-event-draggable',
17748                 html : m.title
17749             };
17750             var cg = ctr.createChild(cfg);
17751             
17752             cg.on('click', _this.onEventClick, _this, m);
17753         });
17754         
17755         this.calpopover.show(el);
17756         
17757         
17758     },
17759     
17760     onLoad: function () 
17761     {   
17762         this.calevents = [];
17763         var cal = this;
17764         
17765         if(this.store.getCount() > 0){
17766             this.store.data.each(function(d){
17767                cal.addItem({
17768                     id : d.data.id,
17769                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17770                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17771                     time : d.data.start_time,
17772                     title : d.data.title,
17773                     description : d.data.description,
17774                     venue : d.data.venue
17775                 });
17776             });
17777         }
17778         
17779         this.renderEvents();
17780         
17781         if(this.calevents.length && this.loadMask){
17782             this.maskEl.hide();
17783         }
17784     },
17785     
17786     onBeforeLoad: function()
17787     {
17788         this.clearEvents();
17789         if(this.loadMask){
17790             this.maskEl.show();
17791         }
17792     }
17793 });
17794
17795  
17796  /*
17797  * - LGPL
17798  *
17799  * element
17800  * 
17801  */
17802
17803 /**
17804  * @class Roo.bootstrap.Popover
17805  * @extends Roo.bootstrap.Component
17806  * Bootstrap Popover class
17807  * @cfg {String} html contents of the popover   (or false to use children..)
17808  * @cfg {String} title of popover (or false to hide)
17809  * @cfg {String} placement how it is placed
17810  * @cfg {String} trigger click || hover (or false to trigger manually)
17811  * @cfg {String} over what (parent or false to trigger manually.)
17812  * @cfg {Number} delay - delay before showing
17813  
17814  * @constructor
17815  * Create a new Popover
17816  * @param {Object} config The config object
17817  */
17818
17819 Roo.bootstrap.Popover = function(config){
17820     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17821     
17822     this.addEvents({
17823         // raw events
17824          /**
17825          * @event show
17826          * After the popover show
17827          * 
17828          * @param {Roo.bootstrap.Popover} this
17829          */
17830         "show" : true,
17831         /**
17832          * @event hide
17833          * After the popover hide
17834          * 
17835          * @param {Roo.bootstrap.Popover} this
17836          */
17837         "hide" : true
17838     });
17839 };
17840
17841 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17842     
17843     title: 'Fill in a title',
17844     html: false,
17845     
17846     placement : 'right',
17847     trigger : 'hover', // hover
17848     
17849     delay : 0,
17850     
17851     over: 'parent',
17852     
17853     can_build_overlaid : false,
17854     
17855     getChildContainer : function()
17856     {
17857         return this.el.select('.popover-content',true).first();
17858     },
17859     
17860     getAutoCreate : function(){
17861          
17862         var cfg = {
17863            cls : 'popover roo-dynamic',
17864            style: 'display:block',
17865            cn : [
17866                 {
17867                     cls : 'arrow'
17868                 },
17869                 {
17870                     cls : 'popover-inner',
17871                     cn : [
17872                         {
17873                             tag: 'h3',
17874                             cls: 'popover-title popover-header',
17875                             html : this.title
17876                         },
17877                         {
17878                             cls : 'popover-content popover-body',
17879                             html : this.html
17880                         }
17881                     ]
17882                     
17883                 }
17884            ]
17885         };
17886         
17887         return cfg;
17888     },
17889     setTitle: function(str)
17890     {
17891         this.title = str;
17892         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17893     },
17894     setContent: function(str)
17895     {
17896         this.html = str;
17897         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17898     },
17899     // as it get's added to the bottom of the page.
17900     onRender : function(ct, position)
17901     {
17902         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17903         if(!this.el){
17904             var cfg = Roo.apply({},  this.getAutoCreate());
17905             cfg.id = Roo.id();
17906             
17907             if (this.cls) {
17908                 cfg.cls += ' ' + this.cls;
17909             }
17910             if (this.style) {
17911                 cfg.style = this.style;
17912             }
17913             //Roo.log("adding to ");
17914             this.el = Roo.get(document.body).createChild(cfg, position);
17915 //            Roo.log(this.el);
17916         }
17917         this.initEvents();
17918     },
17919     
17920     initEvents : function()
17921     {
17922         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17923         this.el.enableDisplayMode('block');
17924         this.el.hide();
17925         if (this.over === false) {
17926             return; 
17927         }
17928         if (this.triggers === false) {
17929             return;
17930         }
17931         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17932         var triggers = this.trigger ? this.trigger.split(' ') : [];
17933         Roo.each(triggers, function(trigger) {
17934         
17935             if (trigger == 'click') {
17936                 on_el.on('click', this.toggle, this);
17937             } else if (trigger != 'manual') {
17938                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17939                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17940       
17941                 on_el.on(eventIn  ,this.enter, this);
17942                 on_el.on(eventOut, this.leave, this);
17943             }
17944         }, this);
17945         
17946     },
17947     
17948     
17949     // private
17950     timeout : null,
17951     hoverState : null,
17952     
17953     toggle : function () {
17954         this.hoverState == 'in' ? this.leave() : this.enter();
17955     },
17956     
17957     enter : function () {
17958         
17959         clearTimeout(this.timeout);
17960     
17961         this.hoverState = 'in';
17962     
17963         if (!this.delay || !this.delay.show) {
17964             this.show();
17965             return;
17966         }
17967         var _t = this;
17968         this.timeout = setTimeout(function () {
17969             if (_t.hoverState == 'in') {
17970                 _t.show();
17971             }
17972         }, this.delay.show)
17973     },
17974     
17975     leave : function() {
17976         clearTimeout(this.timeout);
17977     
17978         this.hoverState = 'out';
17979     
17980         if (!this.delay || !this.delay.hide) {
17981             this.hide();
17982             return;
17983         }
17984         var _t = this;
17985         this.timeout = setTimeout(function () {
17986             if (_t.hoverState == 'out') {
17987                 _t.hide();
17988             }
17989         }, this.delay.hide)
17990     },
17991     
17992     show : function (on_el)
17993     {
17994         if (!on_el) {
17995             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17996         }
17997         
17998         // set content.
17999         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
18000         if (this.html !== false) {
18001             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
18002         }
18003         this.el.removeClass([
18004             'fade','top','bottom', 'left', 'right','in',
18005             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
18006         ]);
18007         if (!this.title.length) {
18008             this.el.select('.popover-title',true).hide();
18009         }
18010         
18011         var placement = typeof this.placement == 'function' ?
18012             this.placement.call(this, this.el, on_el) :
18013             this.placement;
18014             
18015         var autoToken = /\s?auto?\s?/i;
18016         var autoPlace = autoToken.test(placement);
18017         if (autoPlace) {
18018             placement = placement.replace(autoToken, '') || 'top';
18019         }
18020         
18021         //this.el.detach()
18022         //this.el.setXY([0,0]);
18023         this.el.show();
18024         this.el.dom.style.display='block';
18025         this.el.addClass(placement);
18026         
18027         //this.el.appendTo(on_el);
18028         
18029         var p = this.getPosition();
18030         var box = this.el.getBox();
18031         
18032         if (autoPlace) {
18033             // fixme..
18034         }
18035         var align = Roo.bootstrap.Popover.alignment[placement];
18036         
18037 //        Roo.log(align);
18038         this.el.alignTo(on_el, align[0],align[1]);
18039         //var arrow = this.el.select('.arrow',true).first();
18040         //arrow.set(align[2], 
18041         
18042         this.el.addClass('in');
18043         
18044         
18045         if (this.el.hasClass('fade')) {
18046             // fade it?
18047         }
18048         
18049         this.hoverState = 'in';
18050         
18051         this.fireEvent('show', this);
18052         
18053     },
18054     hide : function()
18055     {
18056         this.el.setXY([0,0]);
18057         this.el.removeClass('in');
18058         this.el.hide();
18059         this.hoverState = null;
18060         
18061         this.fireEvent('hide', this);
18062     }
18063     
18064 });
18065
18066 Roo.bootstrap.Popover.alignment = {
18067     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
18068     'right' : ['l-r', [10,0], 'left bs-popover-left'],
18069     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
18070     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
18071 };
18072
18073  /*
18074  * - LGPL
18075  *
18076  * Progress
18077  * 
18078  */
18079
18080 /**
18081  * @class Roo.bootstrap.Progress
18082  * @extends Roo.bootstrap.Component
18083  * Bootstrap Progress class
18084  * @cfg {Boolean} striped striped of the progress bar
18085  * @cfg {Boolean} active animated of the progress bar
18086  * 
18087  * 
18088  * @constructor
18089  * Create a new Progress
18090  * @param {Object} config The config object
18091  */
18092
18093 Roo.bootstrap.Progress = function(config){
18094     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
18095 };
18096
18097 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
18098     
18099     striped : false,
18100     active: false,
18101     
18102     getAutoCreate : function(){
18103         var cfg = {
18104             tag: 'div',
18105             cls: 'progress'
18106         };
18107         
18108         
18109         if(this.striped){
18110             cfg.cls += ' progress-striped';
18111         }
18112       
18113         if(this.active){
18114             cfg.cls += ' active';
18115         }
18116         
18117         
18118         return cfg;
18119     }
18120    
18121 });
18122
18123  
18124
18125  /*
18126  * - LGPL
18127  *
18128  * ProgressBar
18129  * 
18130  */
18131
18132 /**
18133  * @class Roo.bootstrap.ProgressBar
18134  * @extends Roo.bootstrap.Component
18135  * Bootstrap ProgressBar class
18136  * @cfg {Number} aria_valuenow aria-value now
18137  * @cfg {Number} aria_valuemin aria-value min
18138  * @cfg {Number} aria_valuemax aria-value max
18139  * @cfg {String} label label for the progress bar
18140  * @cfg {String} panel (success | info | warning | danger )
18141  * @cfg {String} role role of the progress bar
18142  * @cfg {String} sr_only text
18143  * 
18144  * 
18145  * @constructor
18146  * Create a new ProgressBar
18147  * @param {Object} config The config object
18148  */
18149
18150 Roo.bootstrap.ProgressBar = function(config){
18151     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
18152 };
18153
18154 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
18155     
18156     aria_valuenow : 0,
18157     aria_valuemin : 0,
18158     aria_valuemax : 100,
18159     label : false,
18160     panel : false,
18161     role : false,
18162     sr_only: false,
18163     
18164     getAutoCreate : function()
18165     {
18166         
18167         var cfg = {
18168             tag: 'div',
18169             cls: 'progress-bar',
18170             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18171         };
18172         
18173         if(this.sr_only){
18174             cfg.cn = {
18175                 tag: 'span',
18176                 cls: 'sr-only',
18177                 html: this.sr_only
18178             }
18179         }
18180         
18181         if(this.role){
18182             cfg.role = this.role;
18183         }
18184         
18185         if(this.aria_valuenow){
18186             cfg['aria-valuenow'] = this.aria_valuenow;
18187         }
18188         
18189         if(this.aria_valuemin){
18190             cfg['aria-valuemin'] = this.aria_valuemin;
18191         }
18192         
18193         if(this.aria_valuemax){
18194             cfg['aria-valuemax'] = this.aria_valuemax;
18195         }
18196         
18197         if(this.label && !this.sr_only){
18198             cfg.html = this.label;
18199         }
18200         
18201         if(this.panel){
18202             cfg.cls += ' progress-bar-' + this.panel;
18203         }
18204         
18205         return cfg;
18206     },
18207     
18208     update : function(aria_valuenow)
18209     {
18210         this.aria_valuenow = aria_valuenow;
18211         
18212         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18213     }
18214    
18215 });
18216
18217  
18218
18219  /*
18220  * - LGPL
18221  *
18222  * column
18223  * 
18224  */
18225
18226 /**
18227  * @class Roo.bootstrap.TabGroup
18228  * @extends Roo.bootstrap.Column
18229  * Bootstrap Column class
18230  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18231  * @cfg {Boolean} carousel true to make the group behave like a carousel
18232  * @cfg {Boolean} bullets show bullets for the panels
18233  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18234  * @cfg {Number} timer auto slide timer .. default 0 millisecond
18235  * @cfg {Boolean} showarrow (true|false) show arrow default true
18236  * 
18237  * @constructor
18238  * Create a new TabGroup
18239  * @param {Object} config The config object
18240  */
18241
18242 Roo.bootstrap.TabGroup = function(config){
18243     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18244     if (!this.navId) {
18245         this.navId = Roo.id();
18246     }
18247     this.tabs = [];
18248     Roo.bootstrap.TabGroup.register(this);
18249     
18250 };
18251
18252 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
18253     
18254     carousel : false,
18255     transition : false,
18256     bullets : 0,
18257     timer : 0,
18258     autoslide : false,
18259     slideFn : false,
18260     slideOnTouch : false,
18261     showarrow : true,
18262     
18263     getAutoCreate : function()
18264     {
18265         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18266         
18267         cfg.cls += ' tab-content';
18268         
18269         if (this.carousel) {
18270             cfg.cls += ' carousel slide';
18271             
18272             cfg.cn = [{
18273                cls : 'carousel-inner',
18274                cn : []
18275             }];
18276         
18277             if(this.bullets  && !Roo.isTouch){
18278                 
18279                 var bullets = {
18280                     cls : 'carousel-bullets',
18281                     cn : []
18282                 };
18283                
18284                 if(this.bullets_cls){
18285                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18286                 }
18287                 
18288                 bullets.cn.push({
18289                     cls : 'clear'
18290                 });
18291                 
18292                 cfg.cn[0].cn.push(bullets);
18293             }
18294             
18295             if(this.showarrow){
18296                 cfg.cn[0].cn.push({
18297                     tag : 'div',
18298                     class : 'carousel-arrow',
18299                     cn : [
18300                         {
18301                             tag : 'div',
18302                             class : 'carousel-prev',
18303                             cn : [
18304                                 {
18305                                     tag : 'i',
18306                                     class : 'fa fa-chevron-left'
18307                                 }
18308                             ]
18309                         },
18310                         {
18311                             tag : 'div',
18312                             class : 'carousel-next',
18313                             cn : [
18314                                 {
18315                                     tag : 'i',
18316                                     class : 'fa fa-chevron-right'
18317                                 }
18318                             ]
18319                         }
18320                     ]
18321                 });
18322             }
18323             
18324         }
18325         
18326         return cfg;
18327     },
18328     
18329     initEvents:  function()
18330     {
18331 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18332 //            this.el.on("touchstart", this.onTouchStart, this);
18333 //        }
18334         
18335         if(this.autoslide){
18336             var _this = this;
18337             
18338             this.slideFn = window.setInterval(function() {
18339                 _this.showPanelNext();
18340             }, this.timer);
18341         }
18342         
18343         if(this.showarrow){
18344             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18345             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18346         }
18347         
18348         
18349     },
18350     
18351 //    onTouchStart : function(e, el, o)
18352 //    {
18353 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18354 //            return;
18355 //        }
18356 //        
18357 //        this.showPanelNext();
18358 //    },
18359     
18360     
18361     getChildContainer : function()
18362     {
18363         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18364     },
18365     
18366     /**
18367     * register a Navigation item
18368     * @param {Roo.bootstrap.NavItem} the navitem to add
18369     */
18370     register : function(item)
18371     {
18372         this.tabs.push( item);
18373         item.navId = this.navId; // not really needed..
18374         this.addBullet();
18375     
18376     },
18377     
18378     getActivePanel : function()
18379     {
18380         var r = false;
18381         Roo.each(this.tabs, function(t) {
18382             if (t.active) {
18383                 r = t;
18384                 return false;
18385             }
18386             return null;
18387         });
18388         return r;
18389         
18390     },
18391     getPanelByName : function(n)
18392     {
18393         var r = false;
18394         Roo.each(this.tabs, function(t) {
18395             if (t.tabId == n) {
18396                 r = t;
18397                 return false;
18398             }
18399             return null;
18400         });
18401         return r;
18402     },
18403     indexOfPanel : function(p)
18404     {
18405         var r = false;
18406         Roo.each(this.tabs, function(t,i) {
18407             if (t.tabId == p.tabId) {
18408                 r = i;
18409                 return false;
18410             }
18411             return null;
18412         });
18413         return r;
18414     },
18415     /**
18416      * show a specific panel
18417      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18418      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18419      */
18420     showPanel : function (pan)
18421     {
18422         if(this.transition || typeof(pan) == 'undefined'){
18423             Roo.log("waiting for the transitionend");
18424             return false;
18425         }
18426         
18427         if (typeof(pan) == 'number') {
18428             pan = this.tabs[pan];
18429         }
18430         
18431         if (typeof(pan) == 'string') {
18432             pan = this.getPanelByName(pan);
18433         }
18434         
18435         var cur = this.getActivePanel();
18436         
18437         if(!pan || !cur){
18438             Roo.log('pan or acitve pan is undefined');
18439             return false;
18440         }
18441         
18442         if (pan.tabId == this.getActivePanel().tabId) {
18443             return true;
18444         }
18445         
18446         if (false === cur.fireEvent('beforedeactivate')) {
18447             return false;
18448         }
18449         
18450         if(this.bullets > 0 && !Roo.isTouch){
18451             this.setActiveBullet(this.indexOfPanel(pan));
18452         }
18453         
18454         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18455             
18456             //class="carousel-item carousel-item-next carousel-item-left"
18457             
18458             this.transition = true;
18459             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18460             var lr = dir == 'next' ? 'left' : 'right';
18461             pan.el.addClass(dir); // or prev
18462             pan.el.addClass('carousel-item-' + dir); // or prev
18463             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18464             cur.el.addClass(lr); // or right
18465             pan.el.addClass(lr);
18466             cur.el.addClass('carousel-item-' +lr); // or right
18467             pan.el.addClass('carousel-item-' +lr);
18468             
18469             
18470             var _this = this;
18471             cur.el.on('transitionend', function() {
18472                 Roo.log("trans end?");
18473                 
18474                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
18475                 pan.setActive(true);
18476                 
18477                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
18478                 cur.setActive(false);
18479                 
18480                 _this.transition = false;
18481                 
18482             }, this, { single:  true } );
18483             
18484             return true;
18485         }
18486         
18487         cur.setActive(false);
18488         pan.setActive(true);
18489         
18490         return true;
18491         
18492     },
18493     showPanelNext : function()
18494     {
18495         var i = this.indexOfPanel(this.getActivePanel());
18496         
18497         if (i >= this.tabs.length - 1 && !this.autoslide) {
18498             return;
18499         }
18500         
18501         if (i >= this.tabs.length - 1 && this.autoslide) {
18502             i = -1;
18503         }
18504         
18505         this.showPanel(this.tabs[i+1]);
18506     },
18507     
18508     showPanelPrev : function()
18509     {
18510         var i = this.indexOfPanel(this.getActivePanel());
18511         
18512         if (i  < 1 && !this.autoslide) {
18513             return;
18514         }
18515         
18516         if (i < 1 && this.autoslide) {
18517             i = this.tabs.length;
18518         }
18519         
18520         this.showPanel(this.tabs[i-1]);
18521     },
18522     
18523     
18524     addBullet: function()
18525     {
18526         if(!this.bullets || Roo.isTouch){
18527             return;
18528         }
18529         var ctr = this.el.select('.carousel-bullets',true).first();
18530         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18531         var bullet = ctr.createChild({
18532             cls : 'bullet bullet-' + i
18533         },ctr.dom.lastChild);
18534         
18535         
18536         var _this = this;
18537         
18538         bullet.on('click', (function(e, el, o, ii, t){
18539
18540             e.preventDefault();
18541
18542             this.showPanel(ii);
18543
18544             if(this.autoslide && this.slideFn){
18545                 clearInterval(this.slideFn);
18546                 this.slideFn = window.setInterval(function() {
18547                     _this.showPanelNext();
18548                 }, this.timer);
18549             }
18550
18551         }).createDelegate(this, [i, bullet], true));
18552                 
18553         
18554     },
18555      
18556     setActiveBullet : function(i)
18557     {
18558         if(Roo.isTouch){
18559             return;
18560         }
18561         
18562         Roo.each(this.el.select('.bullet', true).elements, function(el){
18563             el.removeClass('selected');
18564         });
18565
18566         var bullet = this.el.select('.bullet-' + i, true).first();
18567         
18568         if(!bullet){
18569             return;
18570         }
18571         
18572         bullet.addClass('selected');
18573     }
18574     
18575     
18576   
18577 });
18578
18579  
18580
18581  
18582  
18583 Roo.apply(Roo.bootstrap.TabGroup, {
18584     
18585     groups: {},
18586      /**
18587     * register a Navigation Group
18588     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18589     */
18590     register : function(navgrp)
18591     {
18592         this.groups[navgrp.navId] = navgrp;
18593         
18594     },
18595     /**
18596     * fetch a Navigation Group based on the navigation ID
18597     * if one does not exist , it will get created.
18598     * @param {string} the navgroup to add
18599     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18600     */
18601     get: function(navId) {
18602         if (typeof(this.groups[navId]) == 'undefined') {
18603             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18604         }
18605         return this.groups[navId] ;
18606     }
18607     
18608     
18609     
18610 });
18611
18612  /*
18613  * - LGPL
18614  *
18615  * TabPanel
18616  * 
18617  */
18618
18619 /**
18620  * @class Roo.bootstrap.TabPanel
18621  * @extends Roo.bootstrap.Component
18622  * Bootstrap TabPanel class
18623  * @cfg {Boolean} active panel active
18624  * @cfg {String} html panel content
18625  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18626  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18627  * @cfg {String} href click to link..
18628  * 
18629  * 
18630  * @constructor
18631  * Create a new TabPanel
18632  * @param {Object} config The config object
18633  */
18634
18635 Roo.bootstrap.TabPanel = function(config){
18636     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18637     this.addEvents({
18638         /**
18639              * @event changed
18640              * Fires when the active status changes
18641              * @param {Roo.bootstrap.TabPanel} this
18642              * @param {Boolean} state the new state
18643             
18644          */
18645         'changed': true,
18646         /**
18647              * @event beforedeactivate
18648              * Fires before a tab is de-activated - can be used to do validation on a form.
18649              * @param {Roo.bootstrap.TabPanel} this
18650              * @return {Boolean} false if there is an error
18651             
18652          */
18653         'beforedeactivate': true
18654      });
18655     
18656     this.tabId = this.tabId || Roo.id();
18657   
18658 };
18659
18660 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18661     
18662     active: false,
18663     html: false,
18664     tabId: false,
18665     navId : false,
18666     href : '',
18667     
18668     getAutoCreate : function(){
18669         
18670         
18671         var cfg = {
18672             tag: 'div',
18673             // item is needed for carousel - not sure if it has any effect otherwise
18674             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18675             html: this.html || ''
18676         };
18677         
18678         if(this.active){
18679             cfg.cls += ' active';
18680         }
18681         
18682         if(this.tabId){
18683             cfg.tabId = this.tabId;
18684         }
18685         
18686         
18687         
18688         return cfg;
18689     },
18690     
18691     initEvents:  function()
18692     {
18693         var p = this.parent();
18694         
18695         this.navId = this.navId || p.navId;
18696         
18697         if (typeof(this.navId) != 'undefined') {
18698             // not really needed.. but just in case.. parent should be a NavGroup.
18699             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18700             
18701             tg.register(this);
18702             
18703             var i = tg.tabs.length - 1;
18704             
18705             if(this.active && tg.bullets > 0 && i < tg.bullets){
18706                 tg.setActiveBullet(i);
18707             }
18708         }
18709         
18710         this.el.on('click', this.onClick, this);
18711         
18712         if(Roo.isTouch){
18713             this.el.on("touchstart", this.onTouchStart, this);
18714             this.el.on("touchmove", this.onTouchMove, this);
18715             this.el.on("touchend", this.onTouchEnd, this);
18716         }
18717         
18718     },
18719     
18720     onRender : function(ct, position)
18721     {
18722         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18723     },
18724     
18725     setActive : function(state)
18726     {
18727         Roo.log("panel - set active " + this.tabId + "=" + state);
18728         
18729         this.active = state;
18730         if (!state) {
18731             this.el.removeClass('active');
18732             
18733         } else  if (!this.el.hasClass('active')) {
18734             this.el.addClass('active');
18735         }
18736         
18737         this.fireEvent('changed', this, state);
18738     },
18739     
18740     onClick : function(e)
18741     {
18742         e.preventDefault();
18743         
18744         if(!this.href.length){
18745             return;
18746         }
18747         
18748         window.location.href = this.href;
18749     },
18750     
18751     startX : 0,
18752     startY : 0,
18753     endX : 0,
18754     endY : 0,
18755     swiping : false,
18756     
18757     onTouchStart : function(e)
18758     {
18759         this.swiping = false;
18760         
18761         this.startX = e.browserEvent.touches[0].clientX;
18762         this.startY = e.browserEvent.touches[0].clientY;
18763     },
18764     
18765     onTouchMove : function(e)
18766     {
18767         this.swiping = true;
18768         
18769         this.endX = e.browserEvent.touches[0].clientX;
18770         this.endY = e.browserEvent.touches[0].clientY;
18771     },
18772     
18773     onTouchEnd : function(e)
18774     {
18775         if(!this.swiping){
18776             this.onClick(e);
18777             return;
18778         }
18779         
18780         var tabGroup = this.parent();
18781         
18782         if(this.endX > this.startX){ // swiping right
18783             tabGroup.showPanelPrev();
18784             return;
18785         }
18786         
18787         if(this.startX > this.endX){ // swiping left
18788             tabGroup.showPanelNext();
18789             return;
18790         }
18791     }
18792     
18793     
18794 });
18795  
18796
18797  
18798
18799  /*
18800  * - LGPL
18801  *
18802  * DateField
18803  * 
18804  */
18805
18806 /**
18807  * @class Roo.bootstrap.DateField
18808  * @extends Roo.bootstrap.Input
18809  * Bootstrap DateField class
18810  * @cfg {Number} weekStart default 0
18811  * @cfg {String} viewMode default empty, (months|years)
18812  * @cfg {String} minViewMode default empty, (months|years)
18813  * @cfg {Number} startDate default -Infinity
18814  * @cfg {Number} endDate default Infinity
18815  * @cfg {Boolean} todayHighlight default false
18816  * @cfg {Boolean} todayBtn default false
18817  * @cfg {Boolean} calendarWeeks default false
18818  * @cfg {Object} daysOfWeekDisabled default empty
18819  * @cfg {Boolean} singleMode default false (true | false)
18820  * 
18821  * @cfg {Boolean} keyboardNavigation default true
18822  * @cfg {String} language default en
18823  * 
18824  * @constructor
18825  * Create a new DateField
18826  * @param {Object} config The config object
18827  */
18828
18829 Roo.bootstrap.DateField = function(config){
18830     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18831      this.addEvents({
18832             /**
18833              * @event show
18834              * Fires when this field show.
18835              * @param {Roo.bootstrap.DateField} this
18836              * @param {Mixed} date The date value
18837              */
18838             show : true,
18839             /**
18840              * @event show
18841              * Fires when this field hide.
18842              * @param {Roo.bootstrap.DateField} this
18843              * @param {Mixed} date The date value
18844              */
18845             hide : true,
18846             /**
18847              * @event select
18848              * Fires when select a date.
18849              * @param {Roo.bootstrap.DateField} this
18850              * @param {Mixed} date The date value
18851              */
18852             select : true,
18853             /**
18854              * @event beforeselect
18855              * Fires when before select a date.
18856              * @param {Roo.bootstrap.DateField} this
18857              * @param {Mixed} date The date value
18858              */
18859             beforeselect : true
18860         });
18861 };
18862
18863 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18864     
18865     /**
18866      * @cfg {String} format
18867      * The default date format string which can be overriden for localization support.  The format must be
18868      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18869      */
18870     format : "m/d/y",
18871     /**
18872      * @cfg {String} altFormats
18873      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18874      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18875      */
18876     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18877     
18878     weekStart : 0,
18879     
18880     viewMode : '',
18881     
18882     minViewMode : '',
18883     
18884     todayHighlight : false,
18885     
18886     todayBtn: false,
18887     
18888     language: 'en',
18889     
18890     keyboardNavigation: true,
18891     
18892     calendarWeeks: false,
18893     
18894     startDate: -Infinity,
18895     
18896     endDate: Infinity,
18897     
18898     daysOfWeekDisabled: [],
18899     
18900     _events: [],
18901     
18902     singleMode : false,
18903     
18904     UTCDate: function()
18905     {
18906         return new Date(Date.UTC.apply(Date, arguments));
18907     },
18908     
18909     UTCToday: function()
18910     {
18911         var today = new Date();
18912         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18913     },
18914     
18915     getDate: function() {
18916             var d = this.getUTCDate();
18917             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18918     },
18919     
18920     getUTCDate: function() {
18921             return this.date;
18922     },
18923     
18924     setDate: function(d) {
18925             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18926     },
18927     
18928     setUTCDate: function(d) {
18929             this.date = d;
18930             this.setValue(this.formatDate(this.date));
18931     },
18932         
18933     onRender: function(ct, position)
18934     {
18935         
18936         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18937         
18938         this.language = this.language || 'en';
18939         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18940         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18941         
18942         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18943         this.format = this.format || 'm/d/y';
18944         this.isInline = false;
18945         this.isInput = true;
18946         this.component = this.el.select('.add-on', true).first() || false;
18947         this.component = (this.component && this.component.length === 0) ? false : this.component;
18948         this.hasInput = this.component && this.inputEl().length;
18949         
18950         if (typeof(this.minViewMode === 'string')) {
18951             switch (this.minViewMode) {
18952                 case 'months':
18953                     this.minViewMode = 1;
18954                     break;
18955                 case 'years':
18956                     this.minViewMode = 2;
18957                     break;
18958                 default:
18959                     this.minViewMode = 0;
18960                     break;
18961             }
18962         }
18963         
18964         if (typeof(this.viewMode === 'string')) {
18965             switch (this.viewMode) {
18966                 case 'months':
18967                     this.viewMode = 1;
18968                     break;
18969                 case 'years':
18970                     this.viewMode = 2;
18971                     break;
18972                 default:
18973                     this.viewMode = 0;
18974                     break;
18975             }
18976         }
18977                 
18978         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18979         
18980 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18981         
18982         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18983         
18984         this.picker().on('mousedown', this.onMousedown, this);
18985         this.picker().on('click', this.onClick, this);
18986         
18987         this.picker().addClass('datepicker-dropdown');
18988         
18989         this.startViewMode = this.viewMode;
18990         
18991         if(this.singleMode){
18992             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18993                 v.setVisibilityMode(Roo.Element.DISPLAY);
18994                 v.hide();
18995             });
18996             
18997             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18998                 v.setStyle('width', '189px');
18999             });
19000         }
19001         
19002         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
19003             if(!this.calendarWeeks){
19004                 v.remove();
19005                 return;
19006             }
19007             
19008             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19009             v.attr('colspan', function(i, val){
19010                 return parseInt(val) + 1;
19011             });
19012         });
19013                         
19014         
19015         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
19016         
19017         this.setStartDate(this.startDate);
19018         this.setEndDate(this.endDate);
19019         
19020         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
19021         
19022         this.fillDow();
19023         this.fillMonths();
19024         this.update();
19025         this.showMode();
19026         
19027         if(this.isInline) {
19028             this.showPopup();
19029         }
19030     },
19031     
19032     picker : function()
19033     {
19034         return this.pickerEl;
19035 //        return this.el.select('.datepicker', true).first();
19036     },
19037     
19038     fillDow: function()
19039     {
19040         var dowCnt = this.weekStart;
19041         
19042         var dow = {
19043             tag: 'tr',
19044             cn: [
19045                 
19046             ]
19047         };
19048         
19049         if(this.calendarWeeks){
19050             dow.cn.push({
19051                 tag: 'th',
19052                 cls: 'cw',
19053                 html: '&nbsp;'
19054             })
19055         }
19056         
19057         while (dowCnt < this.weekStart + 7) {
19058             dow.cn.push({
19059                 tag: 'th',
19060                 cls: 'dow',
19061                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
19062             });
19063         }
19064         
19065         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
19066     },
19067     
19068     fillMonths: function()
19069     {    
19070         var i = 0;
19071         var months = this.picker().select('>.datepicker-months td', true).first();
19072         
19073         months.dom.innerHTML = '';
19074         
19075         while (i < 12) {
19076             var month = {
19077                 tag: 'span',
19078                 cls: 'month',
19079                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
19080             };
19081             
19082             months.createChild(month);
19083         }
19084         
19085     },
19086     
19087     update: function()
19088     {
19089         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;
19090         
19091         if (this.date < this.startDate) {
19092             this.viewDate = new Date(this.startDate);
19093         } else if (this.date > this.endDate) {
19094             this.viewDate = new Date(this.endDate);
19095         } else {
19096             this.viewDate = new Date(this.date);
19097         }
19098         
19099         this.fill();
19100     },
19101     
19102     fill: function() 
19103     {
19104         var d = new Date(this.viewDate),
19105                 year = d.getUTCFullYear(),
19106                 month = d.getUTCMonth(),
19107                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
19108                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
19109                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
19110                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
19111                 currentDate = this.date && this.date.valueOf(),
19112                 today = this.UTCToday();
19113         
19114         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
19115         
19116 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19117         
19118 //        this.picker.select('>tfoot th.today').
19119 //                                              .text(dates[this.language].today)
19120 //                                              .toggle(this.todayBtn !== false);
19121     
19122         this.updateNavArrows();
19123         this.fillMonths();
19124                                                 
19125         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
19126         
19127         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
19128          
19129         prevMonth.setUTCDate(day);
19130         
19131         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
19132         
19133         var nextMonth = new Date(prevMonth);
19134         
19135         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
19136         
19137         nextMonth = nextMonth.valueOf();
19138         
19139         var fillMonths = false;
19140         
19141         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
19142         
19143         while(prevMonth.valueOf() <= nextMonth) {
19144             var clsName = '';
19145             
19146             if (prevMonth.getUTCDay() === this.weekStart) {
19147                 if(fillMonths){
19148                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
19149                 }
19150                     
19151                 fillMonths = {
19152                     tag: 'tr',
19153                     cn: []
19154                 };
19155                 
19156                 if(this.calendarWeeks){
19157                     // ISO 8601: First week contains first thursday.
19158                     // ISO also states week starts on Monday, but we can be more abstract here.
19159                     var
19160                     // Start of current week: based on weekstart/current date
19161                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
19162                     // Thursday of this week
19163                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
19164                     // First Thursday of year, year from thursday
19165                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
19166                     // Calendar week: ms between thursdays, div ms per day, div 7 days
19167                     calWeek =  (th - yth) / 864e5 / 7 + 1;
19168                     
19169                     fillMonths.cn.push({
19170                         tag: 'td',
19171                         cls: 'cw',
19172                         html: calWeek
19173                     });
19174                 }
19175             }
19176             
19177             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
19178                 clsName += ' old';
19179             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19180                 clsName += ' new';
19181             }
19182             if (this.todayHighlight &&
19183                 prevMonth.getUTCFullYear() == today.getFullYear() &&
19184                 prevMonth.getUTCMonth() == today.getMonth() &&
19185                 prevMonth.getUTCDate() == today.getDate()) {
19186                 clsName += ' today';
19187             }
19188             
19189             if (currentDate && prevMonth.valueOf() === currentDate) {
19190                 clsName += ' active';
19191             }
19192             
19193             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19194                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19195                     clsName += ' disabled';
19196             }
19197             
19198             fillMonths.cn.push({
19199                 tag: 'td',
19200                 cls: 'day ' + clsName,
19201                 html: prevMonth.getDate()
19202             });
19203             
19204             prevMonth.setDate(prevMonth.getDate()+1);
19205         }
19206           
19207         var currentYear = this.date && this.date.getUTCFullYear();
19208         var currentMonth = this.date && this.date.getUTCMonth();
19209         
19210         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19211         
19212         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19213             v.removeClass('active');
19214             
19215             if(currentYear === year && k === currentMonth){
19216                 v.addClass('active');
19217             }
19218             
19219             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19220                 v.addClass('disabled');
19221             }
19222             
19223         });
19224         
19225         
19226         year = parseInt(year/10, 10) * 10;
19227         
19228         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19229         
19230         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19231         
19232         year -= 1;
19233         for (var i = -1; i < 11; i++) {
19234             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19235                 tag: 'span',
19236                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19237                 html: year
19238             });
19239             
19240             year += 1;
19241         }
19242     },
19243     
19244     showMode: function(dir) 
19245     {
19246         if (dir) {
19247             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19248         }
19249         
19250         Roo.each(this.picker().select('>div',true).elements, function(v){
19251             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19252             v.hide();
19253         });
19254         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19255     },
19256     
19257     place: function()
19258     {
19259         if(this.isInline) {
19260             return;
19261         }
19262         
19263         this.picker().removeClass(['bottom', 'top']);
19264         
19265         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19266             /*
19267              * place to the top of element!
19268              *
19269              */
19270             
19271             this.picker().addClass('top');
19272             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19273             
19274             return;
19275         }
19276         
19277         this.picker().addClass('bottom');
19278         
19279         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19280     },
19281     
19282     parseDate : function(value)
19283     {
19284         if(!value || value instanceof Date){
19285             return value;
19286         }
19287         var v = Date.parseDate(value, this.format);
19288         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19289             v = Date.parseDate(value, 'Y-m-d');
19290         }
19291         if(!v && this.altFormats){
19292             if(!this.altFormatsArray){
19293                 this.altFormatsArray = this.altFormats.split("|");
19294             }
19295             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19296                 v = Date.parseDate(value, this.altFormatsArray[i]);
19297             }
19298         }
19299         return v;
19300     },
19301     
19302     formatDate : function(date, fmt)
19303     {   
19304         return (!date || !(date instanceof Date)) ?
19305         date : date.dateFormat(fmt || this.format);
19306     },
19307     
19308     onFocus : function()
19309     {
19310         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19311         this.showPopup();
19312     },
19313     
19314     onBlur : function()
19315     {
19316         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19317         
19318         var d = this.inputEl().getValue();
19319         
19320         this.setValue(d);
19321                 
19322         this.hidePopup();
19323     },
19324     
19325     showPopup : function()
19326     {
19327         this.picker().show();
19328         this.update();
19329         this.place();
19330         
19331         this.fireEvent('showpopup', this, this.date);
19332     },
19333     
19334     hidePopup : function()
19335     {
19336         if(this.isInline) {
19337             return;
19338         }
19339         this.picker().hide();
19340         this.viewMode = this.startViewMode;
19341         this.showMode();
19342         
19343         this.fireEvent('hidepopup', this, this.date);
19344         
19345     },
19346     
19347     onMousedown: function(e)
19348     {
19349         e.stopPropagation();
19350         e.preventDefault();
19351     },
19352     
19353     keyup: function(e)
19354     {
19355         Roo.bootstrap.DateField.superclass.keyup.call(this);
19356         this.update();
19357     },
19358
19359     setValue: function(v)
19360     {
19361         if(this.fireEvent('beforeselect', this, v) !== false){
19362             var d = new Date(this.parseDate(v) ).clearTime();
19363         
19364             if(isNaN(d.getTime())){
19365                 this.date = this.viewDate = '';
19366                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19367                 return;
19368             }
19369
19370             v = this.formatDate(d);
19371
19372             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19373
19374             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19375
19376             this.update();
19377
19378             this.fireEvent('select', this, this.date);
19379         }
19380     },
19381     
19382     getValue: function()
19383     {
19384         return this.formatDate(this.date);
19385     },
19386     
19387     fireKey: function(e)
19388     {
19389         if (!this.picker().isVisible()){
19390             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19391                 this.showPopup();
19392             }
19393             return;
19394         }
19395         
19396         var dateChanged = false,
19397         dir, day, month,
19398         newDate, newViewDate;
19399         
19400         switch(e.keyCode){
19401             case 27: // escape
19402                 this.hidePopup();
19403                 e.preventDefault();
19404                 break;
19405             case 37: // left
19406             case 39: // right
19407                 if (!this.keyboardNavigation) {
19408                     break;
19409                 }
19410                 dir = e.keyCode == 37 ? -1 : 1;
19411                 
19412                 if (e.ctrlKey){
19413                     newDate = this.moveYear(this.date, dir);
19414                     newViewDate = this.moveYear(this.viewDate, dir);
19415                 } else if (e.shiftKey){
19416                     newDate = this.moveMonth(this.date, dir);
19417                     newViewDate = this.moveMonth(this.viewDate, dir);
19418                 } else {
19419                     newDate = new Date(this.date);
19420                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19421                     newViewDate = new Date(this.viewDate);
19422                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19423                 }
19424                 if (this.dateWithinRange(newDate)){
19425                     this.date = newDate;
19426                     this.viewDate = newViewDate;
19427                     this.setValue(this.formatDate(this.date));
19428 //                    this.update();
19429                     e.preventDefault();
19430                     dateChanged = true;
19431                 }
19432                 break;
19433             case 38: // up
19434             case 40: // down
19435                 if (!this.keyboardNavigation) {
19436                     break;
19437                 }
19438                 dir = e.keyCode == 38 ? -1 : 1;
19439                 if (e.ctrlKey){
19440                     newDate = this.moveYear(this.date, dir);
19441                     newViewDate = this.moveYear(this.viewDate, dir);
19442                 } else if (e.shiftKey){
19443                     newDate = this.moveMonth(this.date, dir);
19444                     newViewDate = this.moveMonth(this.viewDate, dir);
19445                 } else {
19446                     newDate = new Date(this.date);
19447                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19448                     newViewDate = new Date(this.viewDate);
19449                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19450                 }
19451                 if (this.dateWithinRange(newDate)){
19452                     this.date = newDate;
19453                     this.viewDate = newViewDate;
19454                     this.setValue(this.formatDate(this.date));
19455 //                    this.update();
19456                     e.preventDefault();
19457                     dateChanged = true;
19458                 }
19459                 break;
19460             case 13: // enter
19461                 this.setValue(this.formatDate(this.date));
19462                 this.hidePopup();
19463                 e.preventDefault();
19464                 break;
19465             case 9: // tab
19466                 this.setValue(this.formatDate(this.date));
19467                 this.hidePopup();
19468                 break;
19469             case 16: // shift
19470             case 17: // ctrl
19471             case 18: // alt
19472                 break;
19473             default :
19474                 this.hidePopup();
19475                 
19476         }
19477     },
19478     
19479     
19480     onClick: function(e) 
19481     {
19482         e.stopPropagation();
19483         e.preventDefault();
19484         
19485         var target = e.getTarget();
19486         
19487         if(target.nodeName.toLowerCase() === 'i'){
19488             target = Roo.get(target).dom.parentNode;
19489         }
19490         
19491         var nodeName = target.nodeName;
19492         var className = target.className;
19493         var html = target.innerHTML;
19494         //Roo.log(nodeName);
19495         
19496         switch(nodeName.toLowerCase()) {
19497             case 'th':
19498                 switch(className) {
19499                     case 'switch':
19500                         this.showMode(1);
19501                         break;
19502                     case 'prev':
19503                     case 'next':
19504                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19505                         switch(this.viewMode){
19506                                 case 0:
19507                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19508                                         break;
19509                                 case 1:
19510                                 case 2:
19511                                         this.viewDate = this.moveYear(this.viewDate, dir);
19512                                         break;
19513                         }
19514                         this.fill();
19515                         break;
19516                     case 'today':
19517                         var date = new Date();
19518                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19519 //                        this.fill()
19520                         this.setValue(this.formatDate(this.date));
19521                         
19522                         this.hidePopup();
19523                         break;
19524                 }
19525                 break;
19526             case 'span':
19527                 if (className.indexOf('disabled') < 0) {
19528                     this.viewDate.setUTCDate(1);
19529                     if (className.indexOf('month') > -1) {
19530                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19531                     } else {
19532                         var year = parseInt(html, 10) || 0;
19533                         this.viewDate.setUTCFullYear(year);
19534                         
19535                     }
19536                     
19537                     if(this.singleMode){
19538                         this.setValue(this.formatDate(this.viewDate));
19539                         this.hidePopup();
19540                         return;
19541                     }
19542                     
19543                     this.showMode(-1);
19544                     this.fill();
19545                 }
19546                 break;
19547                 
19548             case 'td':
19549                 //Roo.log(className);
19550                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19551                     var day = parseInt(html, 10) || 1;
19552                     var year = this.viewDate.getUTCFullYear(),
19553                         month = this.viewDate.getUTCMonth();
19554
19555                     if (className.indexOf('old') > -1) {
19556                         if(month === 0 ){
19557                             month = 11;
19558                             year -= 1;
19559                         }else{
19560                             month -= 1;
19561                         }
19562                     } else if (className.indexOf('new') > -1) {
19563                         if (month == 11) {
19564                             month = 0;
19565                             year += 1;
19566                         } else {
19567                             month += 1;
19568                         }
19569                     }
19570                     //Roo.log([year,month,day]);
19571                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19572                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19573 //                    this.fill();
19574                     //Roo.log(this.formatDate(this.date));
19575                     this.setValue(this.formatDate(this.date));
19576                     this.hidePopup();
19577                 }
19578                 break;
19579         }
19580     },
19581     
19582     setStartDate: function(startDate)
19583     {
19584         this.startDate = startDate || -Infinity;
19585         if (this.startDate !== -Infinity) {
19586             this.startDate = this.parseDate(this.startDate);
19587         }
19588         this.update();
19589         this.updateNavArrows();
19590     },
19591
19592     setEndDate: function(endDate)
19593     {
19594         this.endDate = endDate || Infinity;
19595         if (this.endDate !== Infinity) {
19596             this.endDate = this.parseDate(this.endDate);
19597         }
19598         this.update();
19599         this.updateNavArrows();
19600     },
19601     
19602     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19603     {
19604         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19605         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19606             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19607         }
19608         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19609             return parseInt(d, 10);
19610         });
19611         this.update();
19612         this.updateNavArrows();
19613     },
19614     
19615     updateNavArrows: function() 
19616     {
19617         if(this.singleMode){
19618             return;
19619         }
19620         
19621         var d = new Date(this.viewDate),
19622         year = d.getUTCFullYear(),
19623         month = d.getUTCMonth();
19624         
19625         Roo.each(this.picker().select('.prev', true).elements, function(v){
19626             v.show();
19627             switch (this.viewMode) {
19628                 case 0:
19629
19630                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19631                         v.hide();
19632                     }
19633                     break;
19634                 case 1:
19635                 case 2:
19636                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19637                         v.hide();
19638                     }
19639                     break;
19640             }
19641         });
19642         
19643         Roo.each(this.picker().select('.next', true).elements, function(v){
19644             v.show();
19645             switch (this.viewMode) {
19646                 case 0:
19647
19648                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19649                         v.hide();
19650                     }
19651                     break;
19652                 case 1:
19653                 case 2:
19654                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19655                         v.hide();
19656                     }
19657                     break;
19658             }
19659         })
19660     },
19661     
19662     moveMonth: function(date, dir)
19663     {
19664         if (!dir) {
19665             return date;
19666         }
19667         var new_date = new Date(date.valueOf()),
19668         day = new_date.getUTCDate(),
19669         month = new_date.getUTCMonth(),
19670         mag = Math.abs(dir),
19671         new_month, test;
19672         dir = dir > 0 ? 1 : -1;
19673         if (mag == 1){
19674             test = dir == -1
19675             // If going back one month, make sure month is not current month
19676             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19677             ? function(){
19678                 return new_date.getUTCMonth() == month;
19679             }
19680             // If going forward one month, make sure month is as expected
19681             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19682             : function(){
19683                 return new_date.getUTCMonth() != new_month;
19684             };
19685             new_month = month + dir;
19686             new_date.setUTCMonth(new_month);
19687             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19688             if (new_month < 0 || new_month > 11) {
19689                 new_month = (new_month + 12) % 12;
19690             }
19691         } else {
19692             // For magnitudes >1, move one month at a time...
19693             for (var i=0; i<mag; i++) {
19694                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19695                 new_date = this.moveMonth(new_date, dir);
19696             }
19697             // ...then reset the day, keeping it in the new month
19698             new_month = new_date.getUTCMonth();
19699             new_date.setUTCDate(day);
19700             test = function(){
19701                 return new_month != new_date.getUTCMonth();
19702             };
19703         }
19704         // Common date-resetting loop -- if date is beyond end of month, make it
19705         // end of month
19706         while (test()){
19707             new_date.setUTCDate(--day);
19708             new_date.setUTCMonth(new_month);
19709         }
19710         return new_date;
19711     },
19712
19713     moveYear: function(date, dir)
19714     {
19715         return this.moveMonth(date, dir*12);
19716     },
19717
19718     dateWithinRange: function(date)
19719     {
19720         return date >= this.startDate && date <= this.endDate;
19721     },
19722
19723     
19724     remove: function() 
19725     {
19726         this.picker().remove();
19727     },
19728     
19729     validateValue : function(value)
19730     {
19731         if(this.getVisibilityEl().hasClass('hidden')){
19732             return true;
19733         }
19734         
19735         if(value.length < 1)  {
19736             if(this.allowBlank){
19737                 return true;
19738             }
19739             return false;
19740         }
19741         
19742         if(value.length < this.minLength){
19743             return false;
19744         }
19745         if(value.length > this.maxLength){
19746             return false;
19747         }
19748         if(this.vtype){
19749             var vt = Roo.form.VTypes;
19750             if(!vt[this.vtype](value, this)){
19751                 return false;
19752             }
19753         }
19754         if(typeof this.validator == "function"){
19755             var msg = this.validator(value);
19756             if(msg !== true){
19757                 return false;
19758             }
19759         }
19760         
19761         if(this.regex && !this.regex.test(value)){
19762             return false;
19763         }
19764         
19765         if(typeof(this.parseDate(value)) == 'undefined'){
19766             return false;
19767         }
19768         
19769         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19770             return false;
19771         }      
19772         
19773         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19774             return false;
19775         } 
19776         
19777         
19778         return true;
19779     },
19780     
19781     reset : function()
19782     {
19783         this.date = this.viewDate = '';
19784         
19785         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19786     }
19787    
19788 });
19789
19790 Roo.apply(Roo.bootstrap.DateField,  {
19791     
19792     head : {
19793         tag: 'thead',
19794         cn: [
19795         {
19796             tag: 'tr',
19797             cn: [
19798             {
19799                 tag: 'th',
19800                 cls: 'prev',
19801                 html: '<i class="fa fa-arrow-left"/>'
19802             },
19803             {
19804                 tag: 'th',
19805                 cls: 'switch',
19806                 colspan: '5'
19807             },
19808             {
19809                 tag: 'th',
19810                 cls: 'next',
19811                 html: '<i class="fa fa-arrow-right"/>'
19812             }
19813
19814             ]
19815         }
19816         ]
19817     },
19818     
19819     content : {
19820         tag: 'tbody',
19821         cn: [
19822         {
19823             tag: 'tr',
19824             cn: [
19825             {
19826                 tag: 'td',
19827                 colspan: '7'
19828             }
19829             ]
19830         }
19831         ]
19832     },
19833     
19834     footer : {
19835         tag: 'tfoot',
19836         cn: [
19837         {
19838             tag: 'tr',
19839             cn: [
19840             {
19841                 tag: 'th',
19842                 colspan: '7',
19843                 cls: 'today'
19844             }
19845                     
19846             ]
19847         }
19848         ]
19849     },
19850     
19851     dates:{
19852         en: {
19853             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19854             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19855             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19856             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19857             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19858             today: "Today"
19859         }
19860     },
19861     
19862     modes: [
19863     {
19864         clsName: 'days',
19865         navFnc: 'Month',
19866         navStep: 1
19867     },
19868     {
19869         clsName: 'months',
19870         navFnc: 'FullYear',
19871         navStep: 1
19872     },
19873     {
19874         clsName: 'years',
19875         navFnc: 'FullYear',
19876         navStep: 10
19877     }]
19878 });
19879
19880 Roo.apply(Roo.bootstrap.DateField,  {
19881   
19882     template : {
19883         tag: 'div',
19884         cls: 'datepicker dropdown-menu roo-dynamic',
19885         cn: [
19886         {
19887             tag: 'div',
19888             cls: 'datepicker-days',
19889             cn: [
19890             {
19891                 tag: 'table',
19892                 cls: 'table-condensed',
19893                 cn:[
19894                 Roo.bootstrap.DateField.head,
19895                 {
19896                     tag: 'tbody'
19897                 },
19898                 Roo.bootstrap.DateField.footer
19899                 ]
19900             }
19901             ]
19902         },
19903         {
19904             tag: 'div',
19905             cls: 'datepicker-months',
19906             cn: [
19907             {
19908                 tag: 'table',
19909                 cls: 'table-condensed',
19910                 cn:[
19911                 Roo.bootstrap.DateField.head,
19912                 Roo.bootstrap.DateField.content,
19913                 Roo.bootstrap.DateField.footer
19914                 ]
19915             }
19916             ]
19917         },
19918         {
19919             tag: 'div',
19920             cls: 'datepicker-years',
19921             cn: [
19922             {
19923                 tag: 'table',
19924                 cls: 'table-condensed',
19925                 cn:[
19926                 Roo.bootstrap.DateField.head,
19927                 Roo.bootstrap.DateField.content,
19928                 Roo.bootstrap.DateField.footer
19929                 ]
19930             }
19931             ]
19932         }
19933         ]
19934     }
19935 });
19936
19937  
19938
19939  /*
19940  * - LGPL
19941  *
19942  * TimeField
19943  * 
19944  */
19945
19946 /**
19947  * @class Roo.bootstrap.TimeField
19948  * @extends Roo.bootstrap.Input
19949  * Bootstrap DateField class
19950  * 
19951  * 
19952  * @constructor
19953  * Create a new TimeField
19954  * @param {Object} config The config object
19955  */
19956
19957 Roo.bootstrap.TimeField = function(config){
19958     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19959     this.addEvents({
19960             /**
19961              * @event show
19962              * Fires when this field show.
19963              * @param {Roo.bootstrap.DateField} thisthis
19964              * @param {Mixed} date The date value
19965              */
19966             show : true,
19967             /**
19968              * @event show
19969              * Fires when this field hide.
19970              * @param {Roo.bootstrap.DateField} this
19971              * @param {Mixed} date The date value
19972              */
19973             hide : true,
19974             /**
19975              * @event select
19976              * Fires when select a date.
19977              * @param {Roo.bootstrap.DateField} this
19978              * @param {Mixed} date The date value
19979              */
19980             select : true
19981         });
19982 };
19983
19984 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19985     
19986     /**
19987      * @cfg {String} format
19988      * The default time format string which can be overriden for localization support.  The format must be
19989      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19990      */
19991     format : "H:i",
19992        
19993     onRender: function(ct, position)
19994     {
19995         
19996         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19997                 
19998         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19999         
20000         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20001         
20002         this.pop = this.picker().select('>.datepicker-time',true).first();
20003         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20004         
20005         this.picker().on('mousedown', this.onMousedown, this);
20006         this.picker().on('click', this.onClick, this);
20007         
20008         this.picker().addClass('datepicker-dropdown');
20009     
20010         this.fillTime();
20011         this.update();
20012             
20013         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
20014         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
20015         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
20016         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
20017         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
20018         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
20019
20020     },
20021     
20022     fireKey: function(e){
20023         if (!this.picker().isVisible()){
20024             if (e.keyCode == 27) { // allow escape to hide and re-show picker
20025                 this.show();
20026             }
20027             return;
20028         }
20029
20030         e.preventDefault();
20031         
20032         switch(e.keyCode){
20033             case 27: // escape
20034                 this.hide();
20035                 break;
20036             case 37: // left
20037             case 39: // right
20038                 this.onTogglePeriod();
20039                 break;
20040             case 38: // up
20041                 this.onIncrementMinutes();
20042                 break;
20043             case 40: // down
20044                 this.onDecrementMinutes();
20045                 break;
20046             case 13: // enter
20047             case 9: // tab
20048                 this.setTime();
20049                 break;
20050         }
20051     },
20052     
20053     onClick: function(e) {
20054         e.stopPropagation();
20055         e.preventDefault();
20056     },
20057     
20058     picker : function()
20059     {
20060         return this.el.select('.datepicker', true).first();
20061     },
20062     
20063     fillTime: function()
20064     {    
20065         var time = this.pop.select('tbody', true).first();
20066         
20067         time.dom.innerHTML = '';
20068         
20069         time.createChild({
20070             tag: 'tr',
20071             cn: [
20072                 {
20073                     tag: 'td',
20074                     cn: [
20075                         {
20076                             tag: 'a',
20077                             href: '#',
20078                             cls: 'btn',
20079                             cn: [
20080                                 {
20081                                     tag: 'span',
20082                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
20083                                 }
20084                             ]
20085                         } 
20086                     ]
20087                 },
20088                 {
20089                     tag: 'td',
20090                     cls: 'separator'
20091                 },
20092                 {
20093                     tag: 'td',
20094                     cn: [
20095                         {
20096                             tag: 'a',
20097                             href: '#',
20098                             cls: 'btn',
20099                             cn: [
20100                                 {
20101                                     tag: 'span',
20102                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
20103                                 }
20104                             ]
20105                         }
20106                     ]
20107                 },
20108                 {
20109                     tag: 'td',
20110                     cls: 'separator'
20111                 }
20112             ]
20113         });
20114         
20115         time.createChild({
20116             tag: 'tr',
20117             cn: [
20118                 {
20119                     tag: 'td',
20120                     cn: [
20121                         {
20122                             tag: 'span',
20123                             cls: 'timepicker-hour',
20124                             html: '00'
20125                         }  
20126                     ]
20127                 },
20128                 {
20129                     tag: 'td',
20130                     cls: 'separator',
20131                     html: ':'
20132                 },
20133                 {
20134                     tag: 'td',
20135                     cn: [
20136                         {
20137                             tag: 'span',
20138                             cls: 'timepicker-minute',
20139                             html: '00'
20140                         }  
20141                     ]
20142                 },
20143                 {
20144                     tag: 'td',
20145                     cls: 'separator'
20146                 },
20147                 {
20148                     tag: 'td',
20149                     cn: [
20150                         {
20151                             tag: 'button',
20152                             type: 'button',
20153                             cls: 'btn btn-primary period',
20154                             html: 'AM'
20155                             
20156                         }
20157                     ]
20158                 }
20159             ]
20160         });
20161         
20162         time.createChild({
20163             tag: 'tr',
20164             cn: [
20165                 {
20166                     tag: 'td',
20167                     cn: [
20168                         {
20169                             tag: 'a',
20170                             href: '#',
20171                             cls: 'btn',
20172                             cn: [
20173                                 {
20174                                     tag: 'span',
20175                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
20176                                 }
20177                             ]
20178                         }
20179                     ]
20180                 },
20181                 {
20182                     tag: 'td',
20183                     cls: 'separator'
20184                 },
20185                 {
20186                     tag: 'td',
20187                     cn: [
20188                         {
20189                             tag: 'a',
20190                             href: '#',
20191                             cls: 'btn',
20192                             cn: [
20193                                 {
20194                                     tag: 'span',
20195                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
20196                                 }
20197                             ]
20198                         }
20199                     ]
20200                 },
20201                 {
20202                     tag: 'td',
20203                     cls: 'separator'
20204                 }
20205             ]
20206         });
20207         
20208     },
20209     
20210     update: function()
20211     {
20212         
20213         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20214         
20215         this.fill();
20216     },
20217     
20218     fill: function() 
20219     {
20220         var hours = this.time.getHours();
20221         var minutes = this.time.getMinutes();
20222         var period = 'AM';
20223         
20224         if(hours > 11){
20225             period = 'PM';
20226         }
20227         
20228         if(hours == 0){
20229             hours = 12;
20230         }
20231         
20232         
20233         if(hours > 12){
20234             hours = hours - 12;
20235         }
20236         
20237         if(hours < 10){
20238             hours = '0' + hours;
20239         }
20240         
20241         if(minutes < 10){
20242             minutes = '0' + minutes;
20243         }
20244         
20245         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20246         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20247         this.pop.select('button', true).first().dom.innerHTML = period;
20248         
20249     },
20250     
20251     place: function()
20252     {   
20253         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20254         
20255         var cls = ['bottom'];
20256         
20257         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20258             cls.pop();
20259             cls.push('top');
20260         }
20261         
20262         cls.push('right');
20263         
20264         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20265             cls.pop();
20266             cls.push('left');
20267         }
20268         
20269         this.picker().addClass(cls.join('-'));
20270         
20271         var _this = this;
20272         
20273         Roo.each(cls, function(c){
20274             if(c == 'bottom'){
20275                 _this.picker().setTop(_this.inputEl().getHeight());
20276                 return;
20277             }
20278             if(c == 'top'){
20279                 _this.picker().setTop(0 - _this.picker().getHeight());
20280                 return;
20281             }
20282             
20283             if(c == 'left'){
20284                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20285                 return;
20286             }
20287             if(c == 'right'){
20288                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20289                 return;
20290             }
20291         });
20292         
20293     },
20294   
20295     onFocus : function()
20296     {
20297         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20298         this.show();
20299     },
20300     
20301     onBlur : function()
20302     {
20303         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20304         this.hide();
20305     },
20306     
20307     show : function()
20308     {
20309         this.picker().show();
20310         this.pop.show();
20311         this.update();
20312         this.place();
20313         
20314         this.fireEvent('show', this, this.date);
20315     },
20316     
20317     hide : function()
20318     {
20319         this.picker().hide();
20320         this.pop.hide();
20321         
20322         this.fireEvent('hide', this, this.date);
20323     },
20324     
20325     setTime : function()
20326     {
20327         this.hide();
20328         this.setValue(this.time.format(this.format));
20329         
20330         this.fireEvent('select', this, this.date);
20331         
20332         
20333     },
20334     
20335     onMousedown: function(e){
20336         e.stopPropagation();
20337         e.preventDefault();
20338     },
20339     
20340     onIncrementHours: function()
20341     {
20342         Roo.log('onIncrementHours');
20343         this.time = this.time.add(Date.HOUR, 1);
20344         this.update();
20345         
20346     },
20347     
20348     onDecrementHours: function()
20349     {
20350         Roo.log('onDecrementHours');
20351         this.time = this.time.add(Date.HOUR, -1);
20352         this.update();
20353     },
20354     
20355     onIncrementMinutes: function()
20356     {
20357         Roo.log('onIncrementMinutes');
20358         this.time = this.time.add(Date.MINUTE, 1);
20359         this.update();
20360     },
20361     
20362     onDecrementMinutes: function()
20363     {
20364         Roo.log('onDecrementMinutes');
20365         this.time = this.time.add(Date.MINUTE, -1);
20366         this.update();
20367     },
20368     
20369     onTogglePeriod: function()
20370     {
20371         Roo.log('onTogglePeriod');
20372         this.time = this.time.add(Date.HOUR, 12);
20373         this.update();
20374     }
20375     
20376    
20377 });
20378
20379 Roo.apply(Roo.bootstrap.TimeField,  {
20380     
20381     content : {
20382         tag: 'tbody',
20383         cn: [
20384             {
20385                 tag: 'tr',
20386                 cn: [
20387                 {
20388                     tag: 'td',
20389                     colspan: '7'
20390                 }
20391                 ]
20392             }
20393         ]
20394     },
20395     
20396     footer : {
20397         tag: 'tfoot',
20398         cn: [
20399             {
20400                 tag: 'tr',
20401                 cn: [
20402                 {
20403                     tag: 'th',
20404                     colspan: '7',
20405                     cls: '',
20406                     cn: [
20407                         {
20408                             tag: 'button',
20409                             cls: 'btn btn-info ok',
20410                             html: 'OK'
20411                         }
20412                     ]
20413                 }
20414
20415                 ]
20416             }
20417         ]
20418     }
20419 });
20420
20421 Roo.apply(Roo.bootstrap.TimeField,  {
20422   
20423     template : {
20424         tag: 'div',
20425         cls: 'datepicker dropdown-menu',
20426         cn: [
20427             {
20428                 tag: 'div',
20429                 cls: 'datepicker-time',
20430                 cn: [
20431                 {
20432                     tag: 'table',
20433                     cls: 'table-condensed',
20434                     cn:[
20435                     Roo.bootstrap.TimeField.content,
20436                     Roo.bootstrap.TimeField.footer
20437                     ]
20438                 }
20439                 ]
20440             }
20441         ]
20442     }
20443 });
20444
20445  
20446
20447  /*
20448  * - LGPL
20449  *
20450  * MonthField
20451  * 
20452  */
20453
20454 /**
20455  * @class Roo.bootstrap.MonthField
20456  * @extends Roo.bootstrap.Input
20457  * Bootstrap MonthField class
20458  * 
20459  * @cfg {String} language default en
20460  * 
20461  * @constructor
20462  * Create a new MonthField
20463  * @param {Object} config The config object
20464  */
20465
20466 Roo.bootstrap.MonthField = function(config){
20467     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20468     
20469     this.addEvents({
20470         /**
20471          * @event show
20472          * Fires when this field show.
20473          * @param {Roo.bootstrap.MonthField} this
20474          * @param {Mixed} date The date value
20475          */
20476         show : true,
20477         /**
20478          * @event show
20479          * Fires when this field hide.
20480          * @param {Roo.bootstrap.MonthField} this
20481          * @param {Mixed} date The date value
20482          */
20483         hide : true,
20484         /**
20485          * @event select
20486          * Fires when select a date.
20487          * @param {Roo.bootstrap.MonthField} this
20488          * @param {String} oldvalue The old value
20489          * @param {String} newvalue The new value
20490          */
20491         select : true
20492     });
20493 };
20494
20495 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20496     
20497     onRender: function(ct, position)
20498     {
20499         
20500         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20501         
20502         this.language = this.language || 'en';
20503         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20504         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20505         
20506         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20507         this.isInline = false;
20508         this.isInput = true;
20509         this.component = this.el.select('.add-on', true).first() || false;
20510         this.component = (this.component && this.component.length === 0) ? false : this.component;
20511         this.hasInput = this.component && this.inputEL().length;
20512         
20513         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20514         
20515         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20516         
20517         this.picker().on('mousedown', this.onMousedown, this);
20518         this.picker().on('click', this.onClick, this);
20519         
20520         this.picker().addClass('datepicker-dropdown');
20521         
20522         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20523             v.setStyle('width', '189px');
20524         });
20525         
20526         this.fillMonths();
20527         
20528         this.update();
20529         
20530         if(this.isInline) {
20531             this.show();
20532         }
20533         
20534     },
20535     
20536     setValue: function(v, suppressEvent)
20537     {   
20538         var o = this.getValue();
20539         
20540         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20541         
20542         this.update();
20543
20544         if(suppressEvent !== true){
20545             this.fireEvent('select', this, o, v);
20546         }
20547         
20548     },
20549     
20550     getValue: function()
20551     {
20552         return this.value;
20553     },
20554     
20555     onClick: function(e) 
20556     {
20557         e.stopPropagation();
20558         e.preventDefault();
20559         
20560         var target = e.getTarget();
20561         
20562         if(target.nodeName.toLowerCase() === 'i'){
20563             target = Roo.get(target).dom.parentNode;
20564         }
20565         
20566         var nodeName = target.nodeName;
20567         var className = target.className;
20568         var html = target.innerHTML;
20569         
20570         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20571             return;
20572         }
20573         
20574         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20575         
20576         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20577         
20578         this.hide();
20579                         
20580     },
20581     
20582     picker : function()
20583     {
20584         return this.pickerEl;
20585     },
20586     
20587     fillMonths: function()
20588     {    
20589         var i = 0;
20590         var months = this.picker().select('>.datepicker-months td', true).first();
20591         
20592         months.dom.innerHTML = '';
20593         
20594         while (i < 12) {
20595             var month = {
20596                 tag: 'span',
20597                 cls: 'month',
20598                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20599             };
20600             
20601             months.createChild(month);
20602         }
20603         
20604     },
20605     
20606     update: function()
20607     {
20608         var _this = this;
20609         
20610         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20611             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20612         }
20613         
20614         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20615             e.removeClass('active');
20616             
20617             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20618                 e.addClass('active');
20619             }
20620         })
20621     },
20622     
20623     place: function()
20624     {
20625         if(this.isInline) {
20626             return;
20627         }
20628         
20629         this.picker().removeClass(['bottom', 'top']);
20630         
20631         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20632             /*
20633              * place to the top of element!
20634              *
20635              */
20636             
20637             this.picker().addClass('top');
20638             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20639             
20640             return;
20641         }
20642         
20643         this.picker().addClass('bottom');
20644         
20645         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20646     },
20647     
20648     onFocus : function()
20649     {
20650         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20651         this.show();
20652     },
20653     
20654     onBlur : function()
20655     {
20656         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20657         
20658         var d = this.inputEl().getValue();
20659         
20660         this.setValue(d);
20661                 
20662         this.hide();
20663     },
20664     
20665     show : function()
20666     {
20667         this.picker().show();
20668         this.picker().select('>.datepicker-months', true).first().show();
20669         this.update();
20670         this.place();
20671         
20672         this.fireEvent('show', this, this.date);
20673     },
20674     
20675     hide : function()
20676     {
20677         if(this.isInline) {
20678             return;
20679         }
20680         this.picker().hide();
20681         this.fireEvent('hide', this, this.date);
20682         
20683     },
20684     
20685     onMousedown: function(e)
20686     {
20687         e.stopPropagation();
20688         e.preventDefault();
20689     },
20690     
20691     keyup: function(e)
20692     {
20693         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20694         this.update();
20695     },
20696
20697     fireKey: function(e)
20698     {
20699         if (!this.picker().isVisible()){
20700             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20701                 this.show();
20702             }
20703             return;
20704         }
20705         
20706         var dir;
20707         
20708         switch(e.keyCode){
20709             case 27: // escape
20710                 this.hide();
20711                 e.preventDefault();
20712                 break;
20713             case 37: // left
20714             case 39: // right
20715                 dir = e.keyCode == 37 ? -1 : 1;
20716                 
20717                 this.vIndex = this.vIndex + dir;
20718                 
20719                 if(this.vIndex < 0){
20720                     this.vIndex = 0;
20721                 }
20722                 
20723                 if(this.vIndex > 11){
20724                     this.vIndex = 11;
20725                 }
20726                 
20727                 if(isNaN(this.vIndex)){
20728                     this.vIndex = 0;
20729                 }
20730                 
20731                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20732                 
20733                 break;
20734             case 38: // up
20735             case 40: // down
20736                 
20737                 dir = e.keyCode == 38 ? -1 : 1;
20738                 
20739                 this.vIndex = this.vIndex + dir * 4;
20740                 
20741                 if(this.vIndex < 0){
20742                     this.vIndex = 0;
20743                 }
20744                 
20745                 if(this.vIndex > 11){
20746                     this.vIndex = 11;
20747                 }
20748                 
20749                 if(isNaN(this.vIndex)){
20750                     this.vIndex = 0;
20751                 }
20752                 
20753                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20754                 break;
20755                 
20756             case 13: // enter
20757                 
20758                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20759                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20760                 }
20761                 
20762                 this.hide();
20763                 e.preventDefault();
20764                 break;
20765             case 9: // tab
20766                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20767                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20768                 }
20769                 this.hide();
20770                 break;
20771             case 16: // shift
20772             case 17: // ctrl
20773             case 18: // alt
20774                 break;
20775             default :
20776                 this.hide();
20777                 
20778         }
20779     },
20780     
20781     remove: function() 
20782     {
20783         this.picker().remove();
20784     }
20785    
20786 });
20787
20788 Roo.apply(Roo.bootstrap.MonthField,  {
20789     
20790     content : {
20791         tag: 'tbody',
20792         cn: [
20793         {
20794             tag: 'tr',
20795             cn: [
20796             {
20797                 tag: 'td',
20798                 colspan: '7'
20799             }
20800             ]
20801         }
20802         ]
20803     },
20804     
20805     dates:{
20806         en: {
20807             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20808             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20809         }
20810     }
20811 });
20812
20813 Roo.apply(Roo.bootstrap.MonthField,  {
20814   
20815     template : {
20816         tag: 'div',
20817         cls: 'datepicker dropdown-menu roo-dynamic',
20818         cn: [
20819             {
20820                 tag: 'div',
20821                 cls: 'datepicker-months',
20822                 cn: [
20823                 {
20824                     tag: 'table',
20825                     cls: 'table-condensed',
20826                     cn:[
20827                         Roo.bootstrap.DateField.content
20828                     ]
20829                 }
20830                 ]
20831             }
20832         ]
20833     }
20834 });
20835
20836  
20837
20838  
20839  /*
20840  * - LGPL
20841  *
20842  * CheckBox
20843  * 
20844  */
20845
20846 /**
20847  * @class Roo.bootstrap.CheckBox
20848  * @extends Roo.bootstrap.Input
20849  * Bootstrap CheckBox class
20850  * 
20851  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20852  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20853  * @cfg {String} boxLabel The text that appears beside the checkbox
20854  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20855  * @cfg {Boolean} checked initnal the element
20856  * @cfg {Boolean} inline inline the element (default false)
20857  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20858  * @cfg {String} tooltip label tooltip
20859  * 
20860  * @constructor
20861  * Create a new CheckBox
20862  * @param {Object} config The config object
20863  */
20864
20865 Roo.bootstrap.CheckBox = function(config){
20866     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20867    
20868     this.addEvents({
20869         /**
20870         * @event check
20871         * Fires when the element is checked or unchecked.
20872         * @param {Roo.bootstrap.CheckBox} this This input
20873         * @param {Boolean} checked The new checked value
20874         */
20875        check : true,
20876        /**
20877         * @event click
20878         * Fires when the element is click.
20879         * @param {Roo.bootstrap.CheckBox} this This input
20880         */
20881        click : true
20882     });
20883     
20884 };
20885
20886 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20887   
20888     inputType: 'checkbox',
20889     inputValue: 1,
20890     valueOff: 0,
20891     boxLabel: false,
20892     checked: false,
20893     weight : false,
20894     inline: false,
20895     tooltip : '',
20896     
20897     getAutoCreate : function()
20898     {
20899         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20900         
20901         var id = Roo.id();
20902         
20903         var cfg = {};
20904         
20905         cfg.cls = 'form-group ' + this.inputType; //input-group
20906         
20907         if(this.inline){
20908             cfg.cls += ' ' + this.inputType + '-inline';
20909         }
20910         
20911         var input =  {
20912             tag: 'input',
20913             id : id,
20914             type : this.inputType,
20915             value : this.inputValue,
20916             cls : 'roo-' + this.inputType, //'form-box',
20917             placeholder : this.placeholder || ''
20918             
20919         };
20920         
20921         if(this.inputType != 'radio'){
20922             var hidden =  {
20923                 tag: 'input',
20924                 type : 'hidden',
20925                 cls : 'roo-hidden-value',
20926                 value : this.checked ? this.inputValue : this.valueOff
20927             };
20928         }
20929         
20930             
20931         if (this.weight) { // Validity check?
20932             cfg.cls += " " + this.inputType + "-" + this.weight;
20933         }
20934         
20935         if (this.disabled) {
20936             input.disabled=true;
20937         }
20938         
20939         if(this.checked){
20940             input.checked = this.checked;
20941         }
20942         
20943         if (this.name) {
20944             
20945             input.name = this.name;
20946             
20947             if(this.inputType != 'radio'){
20948                 hidden.name = this.name;
20949                 input.name = '_hidden_' + this.name;
20950             }
20951         }
20952         
20953         if (this.size) {
20954             input.cls += ' input-' + this.size;
20955         }
20956         
20957         var settings=this;
20958         
20959         ['xs','sm','md','lg'].map(function(size){
20960             if (settings[size]) {
20961                 cfg.cls += ' col-' + size + '-' + settings[size];
20962             }
20963         });
20964         
20965         var inputblock = input;
20966          
20967         if (this.before || this.after) {
20968             
20969             inputblock = {
20970                 cls : 'input-group',
20971                 cn :  [] 
20972             };
20973             
20974             if (this.before) {
20975                 inputblock.cn.push({
20976                     tag :'span',
20977                     cls : 'input-group-addon',
20978                     html : this.before
20979                 });
20980             }
20981             
20982             inputblock.cn.push(input);
20983             
20984             if(this.inputType != 'radio'){
20985                 inputblock.cn.push(hidden);
20986             }
20987             
20988             if (this.after) {
20989                 inputblock.cn.push({
20990                     tag :'span',
20991                     cls : 'input-group-addon',
20992                     html : this.after
20993                 });
20994             }
20995             
20996         }
20997         
20998         if (align ==='left' && this.fieldLabel.length) {
20999 //                Roo.log("left and has label");
21000             cfg.cn = [
21001                 {
21002                     tag: 'label',
21003                     'for' :  id,
21004                     cls : 'control-label',
21005                     html : this.fieldLabel
21006                 },
21007                 {
21008                     cls : "", 
21009                     cn: [
21010                         inputblock
21011                     ]
21012                 }
21013             ];
21014             
21015             if(this.labelWidth > 12){
21016                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
21017             }
21018             
21019             if(this.labelWidth < 13 && this.labelmd == 0){
21020                 this.labelmd = this.labelWidth;
21021             }
21022             
21023             if(this.labellg > 0){
21024                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
21025                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
21026             }
21027             
21028             if(this.labelmd > 0){
21029                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
21030                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
21031             }
21032             
21033             if(this.labelsm > 0){
21034                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
21035                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
21036             }
21037             
21038             if(this.labelxs > 0){
21039                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
21040                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
21041             }
21042             
21043         } else if ( this.fieldLabel.length) {
21044 //                Roo.log(" label");
21045                 cfg.cn = [
21046                    
21047                     {
21048                         tag: this.boxLabel ? 'span' : 'label',
21049                         'for': id,
21050                         cls: 'control-label box-input-label',
21051                         //cls : 'input-group-addon',
21052                         html : this.fieldLabel
21053                     },
21054                     
21055                     inputblock
21056                     
21057                 ];
21058
21059         } else {
21060             
21061 //                Roo.log(" no label && no align");
21062                 cfg.cn = [  inputblock ] ;
21063                 
21064                 
21065         }
21066         
21067         if(this.boxLabel){
21068              var boxLabelCfg = {
21069                 tag: 'label',
21070                 //'for': id, // box label is handled by onclick - so no for...
21071                 cls: 'box-label',
21072                 html: this.boxLabel
21073             };
21074             
21075             if(this.tooltip){
21076                 boxLabelCfg.tooltip = this.tooltip;
21077             }
21078              
21079             cfg.cn.push(boxLabelCfg);
21080         }
21081         
21082         if(this.inputType != 'radio'){
21083             cfg.cn.push(hidden);
21084         }
21085         
21086         return cfg;
21087         
21088     },
21089     
21090     /**
21091      * return the real input element.
21092      */
21093     inputEl: function ()
21094     {
21095         return this.el.select('input.roo-' + this.inputType,true).first();
21096     },
21097     hiddenEl: function ()
21098     {
21099         return this.el.select('input.roo-hidden-value',true).first();
21100     },
21101     
21102     labelEl: function()
21103     {
21104         return this.el.select('label.control-label',true).first();
21105     },
21106     /* depricated... */
21107     
21108     label: function()
21109     {
21110         return this.labelEl();
21111     },
21112     
21113     boxLabelEl: function()
21114     {
21115         return this.el.select('label.box-label',true).first();
21116     },
21117     
21118     initEvents : function()
21119     {
21120 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
21121         
21122         this.inputEl().on('click', this.onClick,  this);
21123         
21124         if (this.boxLabel) { 
21125             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
21126         }
21127         
21128         this.startValue = this.getValue();
21129         
21130         if(this.groupId){
21131             Roo.bootstrap.CheckBox.register(this);
21132         }
21133     },
21134     
21135     onClick : function(e)
21136     {   
21137         if(this.fireEvent('click', this, e) !== false){
21138             this.setChecked(!this.checked);
21139         }
21140         
21141     },
21142     
21143     setChecked : function(state,suppressEvent)
21144     {
21145         this.startValue = this.getValue();
21146
21147         if(this.inputType == 'radio'){
21148             
21149             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21150                 e.dom.checked = false;
21151             });
21152             
21153             this.inputEl().dom.checked = true;
21154             
21155             this.inputEl().dom.value = this.inputValue;
21156             
21157             if(suppressEvent !== true){
21158                 this.fireEvent('check', this, true);
21159             }
21160             
21161             this.validate();
21162             
21163             return;
21164         }
21165         
21166         this.checked = state;
21167         
21168         this.inputEl().dom.checked = state;
21169         
21170         
21171         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
21172         
21173         if(suppressEvent !== true){
21174             this.fireEvent('check', this, state);
21175         }
21176         
21177         this.validate();
21178     },
21179     
21180     getValue : function()
21181     {
21182         if(this.inputType == 'radio'){
21183             return this.getGroupValue();
21184         }
21185         
21186         return this.hiddenEl().dom.value;
21187         
21188     },
21189     
21190     getGroupValue : function()
21191     {
21192         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21193             return '';
21194         }
21195         
21196         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21197     },
21198     
21199     setValue : function(v,suppressEvent)
21200     {
21201         if(this.inputType == 'radio'){
21202             this.setGroupValue(v, suppressEvent);
21203             return;
21204         }
21205         
21206         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21207         
21208         this.validate();
21209     },
21210     
21211     setGroupValue : function(v, suppressEvent)
21212     {
21213         this.startValue = this.getValue();
21214         
21215         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21216             e.dom.checked = false;
21217             
21218             if(e.dom.value == v){
21219                 e.dom.checked = true;
21220             }
21221         });
21222         
21223         if(suppressEvent !== true){
21224             this.fireEvent('check', this, true);
21225         }
21226
21227         this.validate();
21228         
21229         return;
21230     },
21231     
21232     validate : function()
21233     {
21234         if(this.getVisibilityEl().hasClass('hidden')){
21235             return true;
21236         }
21237         
21238         if(
21239                 this.disabled || 
21240                 (this.inputType == 'radio' && this.validateRadio()) ||
21241                 (this.inputType == 'checkbox' && this.validateCheckbox())
21242         ){
21243             this.markValid();
21244             return true;
21245         }
21246         
21247         this.markInvalid();
21248         return false;
21249     },
21250     
21251     validateRadio : function()
21252     {
21253         if(this.getVisibilityEl().hasClass('hidden')){
21254             return true;
21255         }
21256         
21257         if(this.allowBlank){
21258             return true;
21259         }
21260         
21261         var valid = false;
21262         
21263         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21264             if(!e.dom.checked){
21265                 return;
21266             }
21267             
21268             valid = true;
21269             
21270             return false;
21271         });
21272         
21273         return valid;
21274     },
21275     
21276     validateCheckbox : function()
21277     {
21278         if(!this.groupId){
21279             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21280             //return (this.getValue() == this.inputValue) ? true : false;
21281         }
21282         
21283         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21284         
21285         if(!group){
21286             return false;
21287         }
21288         
21289         var r = false;
21290         
21291         for(var i in group){
21292             if(group[i].el.isVisible(true)){
21293                 r = false;
21294                 break;
21295             }
21296             
21297             r = true;
21298         }
21299         
21300         for(var i in group){
21301             if(r){
21302                 break;
21303             }
21304             
21305             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21306         }
21307         
21308         return r;
21309     },
21310     
21311     /**
21312      * Mark this field as valid
21313      */
21314     markValid : function()
21315     {
21316         var _this = this;
21317         
21318         this.fireEvent('valid', this);
21319         
21320         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21321         
21322         if(this.groupId){
21323             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21324         }
21325         
21326         if(label){
21327             label.markValid();
21328         }
21329
21330         if(this.inputType == 'radio'){
21331             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21332                 var fg = e.findParent('.form-group', false, true);
21333                 if (Roo.bootstrap.version == 3) {
21334                     fg.removeClass([_this.invalidClass, _this.validClass]);
21335                     fg.addClass(_this.validClass);
21336                 } else {
21337                     fg.removeClass(['is-valid', 'is-invalid']);
21338                     fg.addClass('is-valid');
21339                 }
21340             });
21341             
21342             return;
21343         }
21344
21345         if(!this.groupId){
21346             var fg = this.el.findParent('.form-group', false, true);
21347             if (Roo.bootstrap.version == 3) {
21348                 fg.removeClass([this.invalidClass, this.validClass]);
21349                 fg.addClass(this.validClass);
21350             } else {
21351                 fg.removeClass(['is-valid', 'is-invalid']);
21352                 fg.addClass('is-valid');
21353             }
21354             return;
21355         }
21356         
21357         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21358         
21359         if(!group){
21360             return;
21361         }
21362         
21363         for(var i in group){
21364             var fg = group[i].el.findParent('.form-group', false, true);
21365             if (Roo.bootstrap.version == 3) {
21366                 fg.removeClass([this.invalidClass, this.validClass]);
21367                 fg.addClass(this.validClass);
21368             } else {
21369                 fg.removeClass(['is-valid', 'is-invalid']);
21370                 fg.addClass('is-valid');
21371             }
21372         }
21373     },
21374     
21375      /**
21376      * Mark this field as invalid
21377      * @param {String} msg The validation message
21378      */
21379     markInvalid : function(msg)
21380     {
21381         if(this.allowBlank){
21382             return;
21383         }
21384         
21385         var _this = this;
21386         
21387         this.fireEvent('invalid', this, msg);
21388         
21389         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21390         
21391         if(this.groupId){
21392             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21393         }
21394         
21395         if(label){
21396             label.markInvalid();
21397         }
21398             
21399         if(this.inputType == 'radio'){
21400             
21401             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21402                 var fg = e.findParent('.form-group', false, true);
21403                 if (Roo.bootstrap.version == 3) {
21404                     fg.removeClass([_this.invalidClass, _this.validClass]);
21405                     fg.addClass(_this.invalidClass);
21406                 } else {
21407                     fg.removeClass(['is-invalid', 'is-valid']);
21408                     fg.addClass('is-invalid');
21409                 }
21410             });
21411             
21412             return;
21413         }
21414         
21415         if(!this.groupId){
21416             var fg = this.el.findParent('.form-group', false, true);
21417             if (Roo.bootstrap.version == 3) {
21418                 fg.removeClass([_this.invalidClass, _this.validClass]);
21419                 fg.addClass(_this.invalidClass);
21420             } else {
21421                 fg.removeClass(['is-invalid', 'is-valid']);
21422                 fg.addClass('is-invalid');
21423             }
21424             return;
21425         }
21426         
21427         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21428         
21429         if(!group){
21430             return;
21431         }
21432         
21433         for(var i in group){
21434             var fg = group[i].el.findParent('.form-group', false, true);
21435             if (Roo.bootstrap.version == 3) {
21436                 fg.removeClass([_this.invalidClass, _this.validClass]);
21437                 fg.addClass(_this.invalidClass);
21438             } else {
21439                 fg.removeClass(['is-invalid', 'is-valid']);
21440                 fg.addClass('is-invalid');
21441             }
21442         }
21443         
21444     },
21445     
21446     clearInvalid : function()
21447     {
21448         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21449         
21450         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21451         
21452         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21453         
21454         if (label && label.iconEl) {
21455             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
21456             label.iconEl.removeClass(['is-invalid', 'is-valid']);
21457         }
21458     },
21459     
21460     disable : function()
21461     {
21462         if(this.inputType != 'radio'){
21463             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21464             return;
21465         }
21466         
21467         var _this = this;
21468         
21469         if(this.rendered){
21470             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21471                 _this.getActionEl().addClass(this.disabledClass);
21472                 e.dom.disabled = true;
21473             });
21474         }
21475         
21476         this.disabled = true;
21477         this.fireEvent("disable", this);
21478         return this;
21479     },
21480
21481     enable : function()
21482     {
21483         if(this.inputType != 'radio'){
21484             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21485             return;
21486         }
21487         
21488         var _this = this;
21489         
21490         if(this.rendered){
21491             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21492                 _this.getActionEl().removeClass(this.disabledClass);
21493                 e.dom.disabled = false;
21494             });
21495         }
21496         
21497         this.disabled = false;
21498         this.fireEvent("enable", this);
21499         return this;
21500     },
21501     
21502     setBoxLabel : function(v)
21503     {
21504         this.boxLabel = v;
21505         
21506         if(this.rendered){
21507             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21508         }
21509     }
21510
21511 });
21512
21513 Roo.apply(Roo.bootstrap.CheckBox, {
21514     
21515     groups: {},
21516     
21517      /**
21518     * register a CheckBox Group
21519     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21520     */
21521     register : function(checkbox)
21522     {
21523         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21524             this.groups[checkbox.groupId] = {};
21525         }
21526         
21527         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21528             return;
21529         }
21530         
21531         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21532         
21533     },
21534     /**
21535     * fetch a CheckBox Group based on the group ID
21536     * @param {string} the group ID
21537     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21538     */
21539     get: function(groupId) {
21540         if (typeof(this.groups[groupId]) == 'undefined') {
21541             return false;
21542         }
21543         
21544         return this.groups[groupId] ;
21545     }
21546     
21547     
21548 });
21549 /*
21550  * - LGPL
21551  *
21552  * RadioItem
21553  * 
21554  */
21555
21556 /**
21557  * @class Roo.bootstrap.Radio
21558  * @extends Roo.bootstrap.Component
21559  * Bootstrap Radio class
21560  * @cfg {String} boxLabel - the label associated
21561  * @cfg {String} value - the value of radio
21562  * 
21563  * @constructor
21564  * Create a new Radio
21565  * @param {Object} config The config object
21566  */
21567 Roo.bootstrap.Radio = function(config){
21568     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21569     
21570 };
21571
21572 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21573     
21574     boxLabel : '',
21575     
21576     value : '',
21577     
21578     getAutoCreate : function()
21579     {
21580         var cfg = {
21581             tag : 'div',
21582             cls : 'form-group radio',
21583             cn : [
21584                 {
21585                     tag : 'label',
21586                     cls : 'box-label',
21587                     html : this.boxLabel
21588                 }
21589             ]
21590         };
21591         
21592         return cfg;
21593     },
21594     
21595     initEvents : function() 
21596     {
21597         this.parent().register(this);
21598         
21599         this.el.on('click', this.onClick, this);
21600         
21601     },
21602     
21603     onClick : function(e)
21604     {
21605         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21606             this.setChecked(true);
21607         }
21608     },
21609     
21610     setChecked : function(state, suppressEvent)
21611     {
21612         this.parent().setValue(this.value, suppressEvent);
21613         
21614     },
21615     
21616     setBoxLabel : function(v)
21617     {
21618         this.boxLabel = v;
21619         
21620         if(this.rendered){
21621             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21622         }
21623     }
21624     
21625 });
21626  
21627
21628  /*
21629  * - LGPL
21630  *
21631  * Input
21632  * 
21633  */
21634
21635 /**
21636  * @class Roo.bootstrap.SecurePass
21637  * @extends Roo.bootstrap.Input
21638  * Bootstrap SecurePass class
21639  *
21640  * 
21641  * @constructor
21642  * Create a new SecurePass
21643  * @param {Object} config The config object
21644  */
21645  
21646 Roo.bootstrap.SecurePass = function (config) {
21647     // these go here, so the translation tool can replace them..
21648     this.errors = {
21649         PwdEmpty: "Please type a password, and then retype it to confirm.",
21650         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21651         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21652         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21653         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21654         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21655         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21656         TooWeak: "Your password is Too Weak."
21657     },
21658     this.meterLabel = "Password strength:";
21659     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21660     this.meterClass = [
21661         "roo-password-meter-tooweak", 
21662         "roo-password-meter-weak", 
21663         "roo-password-meter-medium", 
21664         "roo-password-meter-strong", 
21665         "roo-password-meter-grey"
21666     ];
21667     
21668     this.errors = {};
21669     
21670     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21671 }
21672
21673 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21674     /**
21675      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21676      * {
21677      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21678      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21679      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21680      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21681      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21682      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21683      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21684      * })
21685      */
21686     // private
21687     
21688     meterWidth: 300,
21689     errorMsg :'',    
21690     errors: false,
21691     imageRoot: '/',
21692     /**
21693      * @cfg {String/Object} Label for the strength meter (defaults to
21694      * 'Password strength:')
21695      */
21696     // private
21697     meterLabel: '',
21698     /**
21699      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21700      * ['Weak', 'Medium', 'Strong'])
21701      */
21702     // private    
21703     pwdStrengths: false,    
21704     // private
21705     strength: 0,
21706     // private
21707     _lastPwd: null,
21708     // private
21709     kCapitalLetter: 0,
21710     kSmallLetter: 1,
21711     kDigit: 2,
21712     kPunctuation: 3,
21713     
21714     insecure: false,
21715     // private
21716     initEvents: function ()
21717     {
21718         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21719
21720         if (this.el.is('input[type=password]') && Roo.isSafari) {
21721             this.el.on('keydown', this.SafariOnKeyDown, this);
21722         }
21723
21724         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21725     },
21726     // private
21727     onRender: function (ct, position)
21728     {
21729         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21730         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21731         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21732
21733         this.trigger.createChild({
21734                    cn: [
21735                     {
21736                     //id: 'PwdMeter',
21737                     tag: 'div',
21738                     cls: 'roo-password-meter-grey col-xs-12',
21739                     style: {
21740                         //width: 0,
21741                         //width: this.meterWidth + 'px'                                                
21742                         }
21743                     },
21744                     {                            
21745                          cls: 'roo-password-meter-text'                          
21746                     }
21747                 ]            
21748         });
21749
21750          
21751         if (this.hideTrigger) {
21752             this.trigger.setDisplayed(false);
21753         }
21754         this.setSize(this.width || '', this.height || '');
21755     },
21756     // private
21757     onDestroy: function ()
21758     {
21759         if (this.trigger) {
21760             this.trigger.removeAllListeners();
21761             this.trigger.remove();
21762         }
21763         if (this.wrap) {
21764             this.wrap.remove();
21765         }
21766         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21767     },
21768     // private
21769     checkStrength: function ()
21770     {
21771         var pwd = this.inputEl().getValue();
21772         if (pwd == this._lastPwd) {
21773             return;
21774         }
21775
21776         var strength;
21777         if (this.ClientSideStrongPassword(pwd)) {
21778             strength = 3;
21779         } else if (this.ClientSideMediumPassword(pwd)) {
21780             strength = 2;
21781         } else if (this.ClientSideWeakPassword(pwd)) {
21782             strength = 1;
21783         } else {
21784             strength = 0;
21785         }
21786         
21787         Roo.log('strength1: ' + strength);
21788         
21789         //var pm = this.trigger.child('div/div/div').dom;
21790         var pm = this.trigger.child('div/div');
21791         pm.removeClass(this.meterClass);
21792         pm.addClass(this.meterClass[strength]);
21793                 
21794         
21795         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21796                 
21797         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21798         
21799         this._lastPwd = pwd;
21800     },
21801     reset: function ()
21802     {
21803         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21804         
21805         this._lastPwd = '';
21806         
21807         var pm = this.trigger.child('div/div');
21808         pm.removeClass(this.meterClass);
21809         pm.addClass('roo-password-meter-grey');        
21810         
21811         
21812         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21813         
21814         pt.innerHTML = '';
21815         this.inputEl().dom.type='password';
21816     },
21817     // private
21818     validateValue: function (value)
21819     {
21820         
21821         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21822             return false;
21823         }
21824         if (value.length == 0) {
21825             if (this.allowBlank) {
21826                 this.clearInvalid();
21827                 return true;
21828             }
21829
21830             this.markInvalid(this.errors.PwdEmpty);
21831             this.errorMsg = this.errors.PwdEmpty;
21832             return false;
21833         }
21834         
21835         if(this.insecure){
21836             return true;
21837         }
21838         
21839         if ('[\x21-\x7e]*'.match(value)) {
21840             this.markInvalid(this.errors.PwdBadChar);
21841             this.errorMsg = this.errors.PwdBadChar;
21842             return false;
21843         }
21844         if (value.length < 6) {
21845             this.markInvalid(this.errors.PwdShort);
21846             this.errorMsg = this.errors.PwdShort;
21847             return false;
21848         }
21849         if (value.length > 16) {
21850             this.markInvalid(this.errors.PwdLong);
21851             this.errorMsg = this.errors.PwdLong;
21852             return false;
21853         }
21854         var strength;
21855         if (this.ClientSideStrongPassword(value)) {
21856             strength = 3;
21857         } else if (this.ClientSideMediumPassword(value)) {
21858             strength = 2;
21859         } else if (this.ClientSideWeakPassword(value)) {
21860             strength = 1;
21861         } else {
21862             strength = 0;
21863         }
21864
21865         
21866         if (strength < 2) {
21867             //this.markInvalid(this.errors.TooWeak);
21868             this.errorMsg = this.errors.TooWeak;
21869             //return false;
21870         }
21871         
21872         
21873         console.log('strength2: ' + strength);
21874         
21875         //var pm = this.trigger.child('div/div/div').dom;
21876         
21877         var pm = this.trigger.child('div/div');
21878         pm.removeClass(this.meterClass);
21879         pm.addClass(this.meterClass[strength]);
21880                 
21881         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21882                 
21883         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21884         
21885         this.errorMsg = ''; 
21886         return true;
21887     },
21888     // private
21889     CharacterSetChecks: function (type)
21890     {
21891         this.type = type;
21892         this.fResult = false;
21893     },
21894     // private
21895     isctype: function (character, type)
21896     {
21897         switch (type) {  
21898             case this.kCapitalLetter:
21899                 if (character >= 'A' && character <= 'Z') {
21900                     return true;
21901                 }
21902                 break;
21903             
21904             case this.kSmallLetter:
21905                 if (character >= 'a' && character <= 'z') {
21906                     return true;
21907                 }
21908                 break;
21909             
21910             case this.kDigit:
21911                 if (character >= '0' && character <= '9') {
21912                     return true;
21913                 }
21914                 break;
21915             
21916             case this.kPunctuation:
21917                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21918                     return true;
21919                 }
21920                 break;
21921             
21922             default:
21923                 return false;
21924         }
21925
21926     },
21927     // private
21928     IsLongEnough: function (pwd, size)
21929     {
21930         return !(pwd == null || isNaN(size) || pwd.length < size);
21931     },
21932     // private
21933     SpansEnoughCharacterSets: function (word, nb)
21934     {
21935         if (!this.IsLongEnough(word, nb))
21936         {
21937             return false;
21938         }
21939
21940         var characterSetChecks = new Array(
21941             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21942             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21943         );
21944         
21945         for (var index = 0; index < word.length; ++index) {
21946             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21947                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21948                     characterSetChecks[nCharSet].fResult = true;
21949                     break;
21950                 }
21951             }
21952         }
21953
21954         var nCharSets = 0;
21955         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21956             if (characterSetChecks[nCharSet].fResult) {
21957                 ++nCharSets;
21958             }
21959         }
21960
21961         if (nCharSets < nb) {
21962             return false;
21963         }
21964         return true;
21965     },
21966     // private
21967     ClientSideStrongPassword: function (pwd)
21968     {
21969         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21970     },
21971     // private
21972     ClientSideMediumPassword: function (pwd)
21973     {
21974         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21975     },
21976     // private
21977     ClientSideWeakPassword: function (pwd)
21978     {
21979         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21980     }
21981           
21982 })//<script type="text/javascript">
21983
21984 /*
21985  * Based  Ext JS Library 1.1.1
21986  * Copyright(c) 2006-2007, Ext JS, LLC.
21987  * LGPL
21988  *
21989  */
21990  
21991 /**
21992  * @class Roo.HtmlEditorCore
21993  * @extends Roo.Component
21994  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21995  *
21996  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21997  */
21998
21999 Roo.HtmlEditorCore = function(config){
22000     
22001     
22002     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
22003     
22004     
22005     this.addEvents({
22006         /**
22007          * @event initialize
22008          * Fires when the editor is fully initialized (including the iframe)
22009          * @param {Roo.HtmlEditorCore} this
22010          */
22011         initialize: true,
22012         /**
22013          * @event activate
22014          * Fires when the editor is first receives the focus. Any insertion must wait
22015          * until after this event.
22016          * @param {Roo.HtmlEditorCore} this
22017          */
22018         activate: true,
22019          /**
22020          * @event beforesync
22021          * Fires before the textarea is updated with content from the editor iframe. Return false
22022          * to cancel the sync.
22023          * @param {Roo.HtmlEditorCore} this
22024          * @param {String} html
22025          */
22026         beforesync: true,
22027          /**
22028          * @event beforepush
22029          * Fires before the iframe editor is updated with content from the textarea. Return false
22030          * to cancel the push.
22031          * @param {Roo.HtmlEditorCore} this
22032          * @param {String} html
22033          */
22034         beforepush: true,
22035          /**
22036          * @event sync
22037          * Fires when the textarea is updated with content from the editor iframe.
22038          * @param {Roo.HtmlEditorCore} this
22039          * @param {String} html
22040          */
22041         sync: true,
22042          /**
22043          * @event push
22044          * Fires when the iframe editor is updated with content from the textarea.
22045          * @param {Roo.HtmlEditorCore} this
22046          * @param {String} html
22047          */
22048         push: true,
22049         
22050         /**
22051          * @event editorevent
22052          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22053          * @param {Roo.HtmlEditorCore} this
22054          */
22055         editorevent: true
22056         
22057     });
22058     
22059     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
22060     
22061     // defaults : white / black...
22062     this.applyBlacklists();
22063     
22064     
22065     
22066 };
22067
22068
22069 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
22070
22071
22072      /**
22073      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
22074      */
22075     
22076     owner : false,
22077     
22078      /**
22079      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22080      *                        Roo.resizable.
22081      */
22082     resizable : false,
22083      /**
22084      * @cfg {Number} height (in pixels)
22085      */   
22086     height: 300,
22087    /**
22088      * @cfg {Number} width (in pixels)
22089      */   
22090     width: 500,
22091     
22092     /**
22093      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22094      * 
22095      */
22096     stylesheets: false,
22097     
22098     // id of frame..
22099     frameId: false,
22100     
22101     // private properties
22102     validationEvent : false,
22103     deferHeight: true,
22104     initialized : false,
22105     activated : false,
22106     sourceEditMode : false,
22107     onFocus : Roo.emptyFn,
22108     iframePad:3,
22109     hideMode:'offsets',
22110     
22111     clearUp: true,
22112     
22113     // blacklist + whitelisted elements..
22114     black: false,
22115     white: false,
22116      
22117     bodyCls : '',
22118
22119     /**
22120      * Protected method that will not generally be called directly. It
22121      * is called when the editor initializes the iframe with HTML contents. Override this method if you
22122      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
22123      */
22124     getDocMarkup : function(){
22125         // body styles..
22126         var st = '';
22127         
22128         // inherit styels from page...?? 
22129         if (this.stylesheets === false) {
22130             
22131             Roo.get(document.head).select('style').each(function(node) {
22132                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22133             });
22134             
22135             Roo.get(document.head).select('link').each(function(node) { 
22136                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22137             });
22138             
22139         } else if (!this.stylesheets.length) {
22140                 // simple..
22141                 st = '<style type="text/css">' +
22142                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22143                    '</style>';
22144         } else { 
22145             st = '<style type="text/css">' +
22146                     this.stylesheets +
22147                 '</style>';
22148         }
22149         
22150         st +=  '<style type="text/css">' +
22151             'IMG { cursor: pointer } ' +
22152         '</style>';
22153
22154         var cls = 'roo-htmleditor-body';
22155         
22156         if(this.bodyCls.length){
22157             cls += ' ' + this.bodyCls;
22158         }
22159         
22160         return '<html><head>' + st  +
22161             //<style type="text/css">' +
22162             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22163             //'</style>' +
22164             ' </head><body class="' +  cls + '"></body></html>';
22165     },
22166
22167     // private
22168     onRender : function(ct, position)
22169     {
22170         var _t = this;
22171         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
22172         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
22173         
22174         
22175         this.el.dom.style.border = '0 none';
22176         this.el.dom.setAttribute('tabIndex', -1);
22177         this.el.addClass('x-hidden hide');
22178         
22179         
22180         
22181         if(Roo.isIE){ // fix IE 1px bogus margin
22182             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
22183         }
22184        
22185         
22186         this.frameId = Roo.id();
22187         
22188          
22189         
22190         var iframe = this.owner.wrap.createChild({
22191             tag: 'iframe',
22192             cls: 'form-control', // bootstrap..
22193             id: this.frameId,
22194             name: this.frameId,
22195             frameBorder : 'no',
22196             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
22197         }, this.el
22198         );
22199         
22200         
22201         this.iframe = iframe.dom;
22202
22203          this.assignDocWin();
22204         
22205         this.doc.designMode = 'on';
22206        
22207         this.doc.open();
22208         this.doc.write(this.getDocMarkup());
22209         this.doc.close();
22210
22211         
22212         var task = { // must defer to wait for browser to be ready
22213             run : function(){
22214                 //console.log("run task?" + this.doc.readyState);
22215                 this.assignDocWin();
22216                 if(this.doc.body || this.doc.readyState == 'complete'){
22217                     try {
22218                         this.doc.designMode="on";
22219                     } catch (e) {
22220                         return;
22221                     }
22222                     Roo.TaskMgr.stop(task);
22223                     this.initEditor.defer(10, this);
22224                 }
22225             },
22226             interval : 10,
22227             duration: 10000,
22228             scope: this
22229         };
22230         Roo.TaskMgr.start(task);
22231
22232     },
22233
22234     // private
22235     onResize : function(w, h)
22236     {
22237          Roo.log('resize: ' +w + ',' + h );
22238         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22239         if(!this.iframe){
22240             return;
22241         }
22242         if(typeof w == 'number'){
22243             
22244             this.iframe.style.width = w + 'px';
22245         }
22246         if(typeof h == 'number'){
22247             
22248             this.iframe.style.height = h + 'px';
22249             if(this.doc){
22250                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22251             }
22252         }
22253         
22254     },
22255
22256     /**
22257      * Toggles the editor between standard and source edit mode.
22258      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22259      */
22260     toggleSourceEdit : function(sourceEditMode){
22261         
22262         this.sourceEditMode = sourceEditMode === true;
22263         
22264         if(this.sourceEditMode){
22265  
22266             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
22267             
22268         }else{
22269             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22270             //this.iframe.className = '';
22271             this.deferFocus();
22272         }
22273         //this.setSize(this.owner.wrap.getSize());
22274         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22275     },
22276
22277     
22278   
22279
22280     /**
22281      * Protected method that will not generally be called directly. If you need/want
22282      * custom HTML cleanup, this is the method you should override.
22283      * @param {String} html The HTML to be cleaned
22284      * return {String} The cleaned HTML
22285      */
22286     cleanHtml : function(html){
22287         html = String(html);
22288         if(html.length > 5){
22289             if(Roo.isSafari){ // strip safari nonsense
22290                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22291             }
22292         }
22293         if(html == '&nbsp;'){
22294             html = '';
22295         }
22296         return html;
22297     },
22298
22299     /**
22300      * HTML Editor -> Textarea
22301      * Protected method that will not generally be called directly. Syncs the contents
22302      * of the editor iframe with the textarea.
22303      */
22304     syncValue : function(){
22305         if(this.initialized){
22306             var bd = (this.doc.body || this.doc.documentElement);
22307             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22308             var html = bd.innerHTML;
22309             if(Roo.isSafari){
22310                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22311                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22312                 if(m && m[1]){
22313                     html = '<div style="'+m[0]+'">' + html + '</div>';
22314                 }
22315             }
22316             html = this.cleanHtml(html);
22317             // fix up the special chars.. normaly like back quotes in word...
22318             // however we do not want to do this with chinese..
22319             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22320                 var cc = b.charCodeAt();
22321                 if (
22322                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22323                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22324                     (cc >= 0xf900 && cc < 0xfb00 )
22325                 ) {
22326                         return b;
22327                 }
22328                 return "&#"+cc+";" 
22329             });
22330             if(this.owner.fireEvent('beforesync', this, html) !== false){
22331                 this.el.dom.value = html;
22332                 this.owner.fireEvent('sync', this, html);
22333             }
22334         }
22335     },
22336
22337     /**
22338      * Protected method that will not generally be called directly. Pushes the value of the textarea
22339      * into the iframe editor.
22340      */
22341     pushValue : function(){
22342         if(this.initialized){
22343             var v = this.el.dom.value.trim();
22344             
22345 //            if(v.length < 1){
22346 //                v = '&#160;';
22347 //            }
22348             
22349             if(this.owner.fireEvent('beforepush', this, v) !== false){
22350                 var d = (this.doc.body || this.doc.documentElement);
22351                 d.innerHTML = v;
22352                 this.cleanUpPaste();
22353                 this.el.dom.value = d.innerHTML;
22354                 this.owner.fireEvent('push', this, v);
22355             }
22356         }
22357     },
22358
22359     // private
22360     deferFocus : function(){
22361         this.focus.defer(10, this);
22362     },
22363
22364     // doc'ed in Field
22365     focus : function(){
22366         if(this.win && !this.sourceEditMode){
22367             this.win.focus();
22368         }else{
22369             this.el.focus();
22370         }
22371     },
22372     
22373     assignDocWin: function()
22374     {
22375         var iframe = this.iframe;
22376         
22377          if(Roo.isIE){
22378             this.doc = iframe.contentWindow.document;
22379             this.win = iframe.contentWindow;
22380         } else {
22381 //            if (!Roo.get(this.frameId)) {
22382 //                return;
22383 //            }
22384 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22385 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22386             
22387             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22388                 return;
22389             }
22390             
22391             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22392             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22393         }
22394     },
22395     
22396     // private
22397     initEditor : function(){
22398         //console.log("INIT EDITOR");
22399         this.assignDocWin();
22400         
22401         
22402         
22403         this.doc.designMode="on";
22404         this.doc.open();
22405         this.doc.write(this.getDocMarkup());
22406         this.doc.close();
22407         
22408         var dbody = (this.doc.body || this.doc.documentElement);
22409         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22410         // this copies styles from the containing element into thsi one..
22411         // not sure why we need all of this..
22412         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22413         
22414         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22415         //ss['background-attachment'] = 'fixed'; // w3c
22416         dbody.bgProperties = 'fixed'; // ie
22417         //Roo.DomHelper.applyStyles(dbody, ss);
22418         Roo.EventManager.on(this.doc, {
22419             //'mousedown': this.onEditorEvent,
22420             'mouseup': this.onEditorEvent,
22421             'dblclick': this.onEditorEvent,
22422             'click': this.onEditorEvent,
22423             'keyup': this.onEditorEvent,
22424             buffer:100,
22425             scope: this
22426         });
22427         if(Roo.isGecko){
22428             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22429         }
22430         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22431             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22432         }
22433         this.initialized = true;
22434
22435         this.owner.fireEvent('initialize', this);
22436         this.pushValue();
22437     },
22438
22439     // private
22440     onDestroy : function(){
22441         
22442         
22443         
22444         if(this.rendered){
22445             
22446             //for (var i =0; i < this.toolbars.length;i++) {
22447             //    // fixme - ask toolbars for heights?
22448             //    this.toolbars[i].onDestroy();
22449            // }
22450             
22451             //this.wrap.dom.innerHTML = '';
22452             //this.wrap.remove();
22453         }
22454     },
22455
22456     // private
22457     onFirstFocus : function(){
22458         
22459         this.assignDocWin();
22460         
22461         
22462         this.activated = true;
22463          
22464     
22465         if(Roo.isGecko){ // prevent silly gecko errors
22466             this.win.focus();
22467             var s = this.win.getSelection();
22468             if(!s.focusNode || s.focusNode.nodeType != 3){
22469                 var r = s.getRangeAt(0);
22470                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22471                 r.collapse(true);
22472                 this.deferFocus();
22473             }
22474             try{
22475                 this.execCmd('useCSS', true);
22476                 this.execCmd('styleWithCSS', false);
22477             }catch(e){}
22478         }
22479         this.owner.fireEvent('activate', this);
22480     },
22481
22482     // private
22483     adjustFont: function(btn){
22484         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22485         //if(Roo.isSafari){ // safari
22486         //    adjust *= 2;
22487        // }
22488         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22489         if(Roo.isSafari){ // safari
22490             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22491             v =  (v < 10) ? 10 : v;
22492             v =  (v > 48) ? 48 : v;
22493             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22494             
22495         }
22496         
22497         
22498         v = Math.max(1, v+adjust);
22499         
22500         this.execCmd('FontSize', v  );
22501     },
22502
22503     onEditorEvent : function(e)
22504     {
22505         this.owner.fireEvent('editorevent', this, e);
22506       //  this.updateToolbar();
22507         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22508     },
22509
22510     insertTag : function(tg)
22511     {
22512         // could be a bit smarter... -> wrap the current selected tRoo..
22513         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22514             
22515             range = this.createRange(this.getSelection());
22516             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22517             wrappingNode.appendChild(range.extractContents());
22518             range.insertNode(wrappingNode);
22519
22520             return;
22521             
22522             
22523             
22524         }
22525         this.execCmd("formatblock",   tg);
22526         
22527     },
22528     
22529     insertText : function(txt)
22530     {
22531         
22532         
22533         var range = this.createRange();
22534         range.deleteContents();
22535                //alert(Sender.getAttribute('label'));
22536                
22537         range.insertNode(this.doc.createTextNode(txt));
22538     } ,
22539     
22540      
22541
22542     /**
22543      * Executes a Midas editor command on the editor document and performs necessary focus and
22544      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22545      * @param {String} cmd The Midas command
22546      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22547      */
22548     relayCmd : function(cmd, value){
22549         this.win.focus();
22550         this.execCmd(cmd, value);
22551         this.owner.fireEvent('editorevent', this);
22552         //this.updateToolbar();
22553         this.owner.deferFocus();
22554     },
22555
22556     /**
22557      * Executes a Midas editor command directly on the editor document.
22558      * For visual commands, you should use {@link #relayCmd} instead.
22559      * <b>This should only be called after the editor is initialized.</b>
22560      * @param {String} cmd The Midas command
22561      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22562      */
22563     execCmd : function(cmd, value){
22564         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22565         this.syncValue();
22566     },
22567  
22568  
22569    
22570     /**
22571      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22572      * to insert tRoo.
22573      * @param {String} text | dom node.. 
22574      */
22575     insertAtCursor : function(text)
22576     {
22577         
22578         if(!this.activated){
22579             return;
22580         }
22581         /*
22582         if(Roo.isIE){
22583             this.win.focus();
22584             var r = this.doc.selection.createRange();
22585             if(r){
22586                 r.collapse(true);
22587                 r.pasteHTML(text);
22588                 this.syncValue();
22589                 this.deferFocus();
22590             
22591             }
22592             return;
22593         }
22594         */
22595         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22596             this.win.focus();
22597             
22598             
22599             // from jquery ui (MIT licenced)
22600             var range, node;
22601             var win = this.win;
22602             
22603             if (win.getSelection && win.getSelection().getRangeAt) {
22604                 range = win.getSelection().getRangeAt(0);
22605                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22606                 range.insertNode(node);
22607             } else if (win.document.selection && win.document.selection.createRange) {
22608                 // no firefox support
22609                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22610                 win.document.selection.createRange().pasteHTML(txt);
22611             } else {
22612                 // no firefox support
22613                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22614                 this.execCmd('InsertHTML', txt);
22615             } 
22616             
22617             this.syncValue();
22618             
22619             this.deferFocus();
22620         }
22621     },
22622  // private
22623     mozKeyPress : function(e){
22624         if(e.ctrlKey){
22625             var c = e.getCharCode(), cmd;
22626           
22627             if(c > 0){
22628                 c = String.fromCharCode(c).toLowerCase();
22629                 switch(c){
22630                     case 'b':
22631                         cmd = 'bold';
22632                         break;
22633                     case 'i':
22634                         cmd = 'italic';
22635                         break;
22636                     
22637                     case 'u':
22638                         cmd = 'underline';
22639                         break;
22640                     
22641                     case 'v':
22642                         this.cleanUpPaste.defer(100, this);
22643                         return;
22644                         
22645                 }
22646                 if(cmd){
22647                     this.win.focus();
22648                     this.execCmd(cmd);
22649                     this.deferFocus();
22650                     e.preventDefault();
22651                 }
22652                 
22653             }
22654         }
22655     },
22656
22657     // private
22658     fixKeys : function(){ // load time branching for fastest keydown performance
22659         if(Roo.isIE){
22660             return function(e){
22661                 var k = e.getKey(), r;
22662                 if(k == e.TAB){
22663                     e.stopEvent();
22664                     r = this.doc.selection.createRange();
22665                     if(r){
22666                         r.collapse(true);
22667                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22668                         this.deferFocus();
22669                     }
22670                     return;
22671                 }
22672                 
22673                 if(k == e.ENTER){
22674                     r = this.doc.selection.createRange();
22675                     if(r){
22676                         var target = r.parentElement();
22677                         if(!target || target.tagName.toLowerCase() != 'li'){
22678                             e.stopEvent();
22679                             r.pasteHTML('<br />');
22680                             r.collapse(false);
22681                             r.select();
22682                         }
22683                     }
22684                 }
22685                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22686                     this.cleanUpPaste.defer(100, this);
22687                     return;
22688                 }
22689                 
22690                 
22691             };
22692         }else if(Roo.isOpera){
22693             return function(e){
22694                 var k = e.getKey();
22695                 if(k == e.TAB){
22696                     e.stopEvent();
22697                     this.win.focus();
22698                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22699                     this.deferFocus();
22700                 }
22701                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22702                     this.cleanUpPaste.defer(100, this);
22703                     return;
22704                 }
22705                 
22706             };
22707         }else if(Roo.isSafari){
22708             return function(e){
22709                 var k = e.getKey();
22710                 
22711                 if(k == e.TAB){
22712                     e.stopEvent();
22713                     this.execCmd('InsertText','\t');
22714                     this.deferFocus();
22715                     return;
22716                 }
22717                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22718                     this.cleanUpPaste.defer(100, this);
22719                     return;
22720                 }
22721                 
22722              };
22723         }
22724     }(),
22725     
22726     getAllAncestors: function()
22727     {
22728         var p = this.getSelectedNode();
22729         var a = [];
22730         if (!p) {
22731             a.push(p); // push blank onto stack..
22732             p = this.getParentElement();
22733         }
22734         
22735         
22736         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22737             a.push(p);
22738             p = p.parentNode;
22739         }
22740         a.push(this.doc.body);
22741         return a;
22742     },
22743     lastSel : false,
22744     lastSelNode : false,
22745     
22746     
22747     getSelection : function() 
22748     {
22749         this.assignDocWin();
22750         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22751     },
22752     
22753     getSelectedNode: function() 
22754     {
22755         // this may only work on Gecko!!!
22756         
22757         // should we cache this!!!!
22758         
22759         
22760         
22761          
22762         var range = this.createRange(this.getSelection()).cloneRange();
22763         
22764         if (Roo.isIE) {
22765             var parent = range.parentElement();
22766             while (true) {
22767                 var testRange = range.duplicate();
22768                 testRange.moveToElementText(parent);
22769                 if (testRange.inRange(range)) {
22770                     break;
22771                 }
22772                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22773                     break;
22774                 }
22775                 parent = parent.parentElement;
22776             }
22777             return parent;
22778         }
22779         
22780         // is ancestor a text element.
22781         var ac =  range.commonAncestorContainer;
22782         if (ac.nodeType == 3) {
22783             ac = ac.parentNode;
22784         }
22785         
22786         var ar = ac.childNodes;
22787          
22788         var nodes = [];
22789         var other_nodes = [];
22790         var has_other_nodes = false;
22791         for (var i=0;i<ar.length;i++) {
22792             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22793                 continue;
22794             }
22795             // fullly contained node.
22796             
22797             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22798                 nodes.push(ar[i]);
22799                 continue;
22800             }
22801             
22802             // probably selected..
22803             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22804                 other_nodes.push(ar[i]);
22805                 continue;
22806             }
22807             // outer..
22808             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22809                 continue;
22810             }
22811             
22812             
22813             has_other_nodes = true;
22814         }
22815         if (!nodes.length && other_nodes.length) {
22816             nodes= other_nodes;
22817         }
22818         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22819             return false;
22820         }
22821         
22822         return nodes[0];
22823     },
22824     createRange: function(sel)
22825     {
22826         // this has strange effects when using with 
22827         // top toolbar - not sure if it's a great idea.
22828         //this.editor.contentWindow.focus();
22829         if (typeof sel != "undefined") {
22830             try {
22831                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22832             } catch(e) {
22833                 return this.doc.createRange();
22834             }
22835         } else {
22836             return this.doc.createRange();
22837         }
22838     },
22839     getParentElement: function()
22840     {
22841         
22842         this.assignDocWin();
22843         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22844         
22845         var range = this.createRange(sel);
22846          
22847         try {
22848             var p = range.commonAncestorContainer;
22849             while (p.nodeType == 3) { // text node
22850                 p = p.parentNode;
22851             }
22852             return p;
22853         } catch (e) {
22854             return null;
22855         }
22856     
22857     },
22858     /***
22859      *
22860      * Range intersection.. the hard stuff...
22861      *  '-1' = before
22862      *  '0' = hits..
22863      *  '1' = after.
22864      *         [ -- selected range --- ]
22865      *   [fail]                        [fail]
22866      *
22867      *    basically..
22868      *      if end is before start or  hits it. fail.
22869      *      if start is after end or hits it fail.
22870      *
22871      *   if either hits (but other is outside. - then it's not 
22872      *   
22873      *    
22874      **/
22875     
22876     
22877     // @see http://www.thismuchiknow.co.uk/?p=64.
22878     rangeIntersectsNode : function(range, node)
22879     {
22880         var nodeRange = node.ownerDocument.createRange();
22881         try {
22882             nodeRange.selectNode(node);
22883         } catch (e) {
22884             nodeRange.selectNodeContents(node);
22885         }
22886     
22887         var rangeStartRange = range.cloneRange();
22888         rangeStartRange.collapse(true);
22889     
22890         var rangeEndRange = range.cloneRange();
22891         rangeEndRange.collapse(false);
22892     
22893         var nodeStartRange = nodeRange.cloneRange();
22894         nodeStartRange.collapse(true);
22895     
22896         var nodeEndRange = nodeRange.cloneRange();
22897         nodeEndRange.collapse(false);
22898     
22899         return rangeStartRange.compareBoundaryPoints(
22900                  Range.START_TO_START, nodeEndRange) == -1 &&
22901                rangeEndRange.compareBoundaryPoints(
22902                  Range.START_TO_START, nodeStartRange) == 1;
22903         
22904          
22905     },
22906     rangeCompareNode : function(range, node)
22907     {
22908         var nodeRange = node.ownerDocument.createRange();
22909         try {
22910             nodeRange.selectNode(node);
22911         } catch (e) {
22912             nodeRange.selectNodeContents(node);
22913         }
22914         
22915         
22916         range.collapse(true);
22917     
22918         nodeRange.collapse(true);
22919      
22920         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22921         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22922          
22923         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22924         
22925         var nodeIsBefore   =  ss == 1;
22926         var nodeIsAfter    = ee == -1;
22927         
22928         if (nodeIsBefore && nodeIsAfter) {
22929             return 0; // outer
22930         }
22931         if (!nodeIsBefore && nodeIsAfter) {
22932             return 1; //right trailed.
22933         }
22934         
22935         if (nodeIsBefore && !nodeIsAfter) {
22936             return 2;  // left trailed.
22937         }
22938         // fully contined.
22939         return 3;
22940     },
22941
22942     // private? - in a new class?
22943     cleanUpPaste :  function()
22944     {
22945         // cleans up the whole document..
22946         Roo.log('cleanuppaste');
22947         
22948         this.cleanUpChildren(this.doc.body);
22949         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22950         if (clean != this.doc.body.innerHTML) {
22951             this.doc.body.innerHTML = clean;
22952         }
22953         
22954     },
22955     
22956     cleanWordChars : function(input) {// change the chars to hex code
22957         var he = Roo.HtmlEditorCore;
22958         
22959         var output = input;
22960         Roo.each(he.swapCodes, function(sw) { 
22961             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22962             
22963             output = output.replace(swapper, sw[1]);
22964         });
22965         
22966         return output;
22967     },
22968     
22969     
22970     cleanUpChildren : function (n)
22971     {
22972         if (!n.childNodes.length) {
22973             return;
22974         }
22975         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22976            this.cleanUpChild(n.childNodes[i]);
22977         }
22978     },
22979     
22980     
22981         
22982     
22983     cleanUpChild : function (node)
22984     {
22985         var ed = this;
22986         //console.log(node);
22987         if (node.nodeName == "#text") {
22988             // clean up silly Windows -- stuff?
22989             return; 
22990         }
22991         if (node.nodeName == "#comment") {
22992             node.parentNode.removeChild(node);
22993             // clean up silly Windows -- stuff?
22994             return; 
22995         }
22996         var lcname = node.tagName.toLowerCase();
22997         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22998         // whitelist of tags..
22999         
23000         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
23001             // remove node.
23002             node.parentNode.removeChild(node);
23003             return;
23004             
23005         }
23006         
23007         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
23008         
23009         // remove <a name=....> as rendering on yahoo mailer is borked with this.
23010         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
23011         
23012         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
23013         //    remove_keep_children = true;
23014         //}
23015         
23016         if (remove_keep_children) {
23017             this.cleanUpChildren(node);
23018             // inserts everything just before this node...
23019             while (node.childNodes.length) {
23020                 var cn = node.childNodes[0];
23021                 node.removeChild(cn);
23022                 node.parentNode.insertBefore(cn, node);
23023             }
23024             node.parentNode.removeChild(node);
23025             return;
23026         }
23027         
23028         if (!node.attributes || !node.attributes.length) {
23029             this.cleanUpChildren(node);
23030             return;
23031         }
23032         
23033         function cleanAttr(n,v)
23034         {
23035             
23036             if (v.match(/^\./) || v.match(/^\//)) {
23037                 return;
23038             }
23039             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
23040                 return;
23041             }
23042             if (v.match(/^#/)) {
23043                 return;
23044             }
23045 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
23046             node.removeAttribute(n);
23047             
23048         }
23049         
23050         var cwhite = this.cwhite;
23051         var cblack = this.cblack;
23052             
23053         function cleanStyle(n,v)
23054         {
23055             if (v.match(/expression/)) { //XSS?? should we even bother..
23056                 node.removeAttribute(n);
23057                 return;
23058             }
23059             
23060             var parts = v.split(/;/);
23061             var clean = [];
23062             
23063             Roo.each(parts, function(p) {
23064                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
23065                 if (!p.length) {
23066                     return true;
23067                 }
23068                 var l = p.split(':').shift().replace(/\s+/g,'');
23069                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
23070                 
23071                 if ( cwhite.length && cblack.indexOf(l) > -1) {
23072 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23073                     //node.removeAttribute(n);
23074                     return true;
23075                 }
23076                 //Roo.log()
23077                 // only allow 'c whitelisted system attributes'
23078                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
23079 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23080                     //node.removeAttribute(n);
23081                     return true;
23082                 }
23083                 
23084                 
23085                  
23086                 
23087                 clean.push(p);
23088                 return true;
23089             });
23090             if (clean.length) { 
23091                 node.setAttribute(n, clean.join(';'));
23092             } else {
23093                 node.removeAttribute(n);
23094             }
23095             
23096         }
23097         
23098         
23099         for (var i = node.attributes.length-1; i > -1 ; i--) {
23100             var a = node.attributes[i];
23101             //console.log(a);
23102             
23103             if (a.name.toLowerCase().substr(0,2)=='on')  {
23104                 node.removeAttribute(a.name);
23105                 continue;
23106             }
23107             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
23108                 node.removeAttribute(a.name);
23109                 continue;
23110             }
23111             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
23112                 cleanAttr(a.name,a.value); // fixme..
23113                 continue;
23114             }
23115             if (a.name == 'style') {
23116                 cleanStyle(a.name,a.value);
23117                 continue;
23118             }
23119             /// clean up MS crap..
23120             // tecnically this should be a list of valid class'es..
23121             
23122             
23123             if (a.name == 'class') {
23124                 if (a.value.match(/^Mso/)) {
23125                     node.className = '';
23126                 }
23127                 
23128                 if (a.value.match(/^body$/)) {
23129                     node.className = '';
23130                 }
23131                 continue;
23132             }
23133             
23134             // style cleanup!?
23135             // class cleanup?
23136             
23137         }
23138         
23139         
23140         this.cleanUpChildren(node);
23141         
23142         
23143     },
23144     
23145     /**
23146      * Clean up MS wordisms...
23147      */
23148     cleanWord : function(node)
23149     {
23150         
23151         
23152         if (!node) {
23153             this.cleanWord(this.doc.body);
23154             return;
23155         }
23156         if (node.nodeName == "#text") {
23157             // clean up silly Windows -- stuff?
23158             return; 
23159         }
23160         if (node.nodeName == "#comment") {
23161             node.parentNode.removeChild(node);
23162             // clean up silly Windows -- stuff?
23163             return; 
23164         }
23165         
23166         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
23167             node.parentNode.removeChild(node);
23168             return;
23169         }
23170         
23171         // remove - but keep children..
23172         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
23173             while (node.childNodes.length) {
23174                 var cn = node.childNodes[0];
23175                 node.removeChild(cn);
23176                 node.parentNode.insertBefore(cn, node);
23177             }
23178             node.parentNode.removeChild(node);
23179             this.iterateChildren(node, this.cleanWord);
23180             return;
23181         }
23182         // clean styles
23183         if (node.className.length) {
23184             
23185             var cn = node.className.split(/\W+/);
23186             var cna = [];
23187             Roo.each(cn, function(cls) {
23188                 if (cls.match(/Mso[a-zA-Z]+/)) {
23189                     return;
23190                 }
23191                 cna.push(cls);
23192             });
23193             node.className = cna.length ? cna.join(' ') : '';
23194             if (!cna.length) {
23195                 node.removeAttribute("class");
23196             }
23197         }
23198         
23199         if (node.hasAttribute("lang")) {
23200             node.removeAttribute("lang");
23201         }
23202         
23203         if (node.hasAttribute("style")) {
23204             
23205             var styles = node.getAttribute("style").split(";");
23206             var nstyle = [];
23207             Roo.each(styles, function(s) {
23208                 if (!s.match(/:/)) {
23209                     return;
23210                 }
23211                 var kv = s.split(":");
23212                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
23213                     return;
23214                 }
23215                 // what ever is left... we allow.
23216                 nstyle.push(s);
23217             });
23218             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23219             if (!nstyle.length) {
23220                 node.removeAttribute('style');
23221             }
23222         }
23223         this.iterateChildren(node, this.cleanWord);
23224         
23225         
23226         
23227     },
23228     /**
23229      * iterateChildren of a Node, calling fn each time, using this as the scole..
23230      * @param {DomNode} node node to iterate children of.
23231      * @param {Function} fn method of this class to call on each item.
23232      */
23233     iterateChildren : function(node, fn)
23234     {
23235         if (!node.childNodes.length) {
23236                 return;
23237         }
23238         for (var i = node.childNodes.length-1; i > -1 ; i--) {
23239            fn.call(this, node.childNodes[i])
23240         }
23241     },
23242     
23243     
23244     /**
23245      * cleanTableWidths.
23246      *
23247      * Quite often pasting from word etc.. results in tables with column and widths.
23248      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23249      *
23250      */
23251     cleanTableWidths : function(node)
23252     {
23253          
23254          
23255         if (!node) {
23256             this.cleanTableWidths(this.doc.body);
23257             return;
23258         }
23259         
23260         // ignore list...
23261         if (node.nodeName == "#text" || node.nodeName == "#comment") {
23262             return; 
23263         }
23264         Roo.log(node.tagName);
23265         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23266             this.iterateChildren(node, this.cleanTableWidths);
23267             return;
23268         }
23269         if (node.hasAttribute('width')) {
23270             node.removeAttribute('width');
23271         }
23272         
23273          
23274         if (node.hasAttribute("style")) {
23275             // pretty basic...
23276             
23277             var styles = node.getAttribute("style").split(";");
23278             var nstyle = [];
23279             Roo.each(styles, function(s) {
23280                 if (!s.match(/:/)) {
23281                     return;
23282                 }
23283                 var kv = s.split(":");
23284                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23285                     return;
23286                 }
23287                 // what ever is left... we allow.
23288                 nstyle.push(s);
23289             });
23290             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23291             if (!nstyle.length) {
23292                 node.removeAttribute('style');
23293             }
23294         }
23295         
23296         this.iterateChildren(node, this.cleanTableWidths);
23297         
23298         
23299     },
23300     
23301     
23302     
23303     
23304     domToHTML : function(currentElement, depth, nopadtext) {
23305         
23306         depth = depth || 0;
23307         nopadtext = nopadtext || false;
23308     
23309         if (!currentElement) {
23310             return this.domToHTML(this.doc.body);
23311         }
23312         
23313         //Roo.log(currentElement);
23314         var j;
23315         var allText = false;
23316         var nodeName = currentElement.nodeName;
23317         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23318         
23319         if  (nodeName == '#text') {
23320             
23321             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23322         }
23323         
23324         
23325         var ret = '';
23326         if (nodeName != 'BODY') {
23327              
23328             var i = 0;
23329             // Prints the node tagName, such as <A>, <IMG>, etc
23330             if (tagName) {
23331                 var attr = [];
23332                 for(i = 0; i < currentElement.attributes.length;i++) {
23333                     // quoting?
23334                     var aname = currentElement.attributes.item(i).name;
23335                     if (!currentElement.attributes.item(i).value.length) {
23336                         continue;
23337                     }
23338                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23339                 }
23340                 
23341                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23342             } 
23343             else {
23344                 
23345                 // eack
23346             }
23347         } else {
23348             tagName = false;
23349         }
23350         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23351             return ret;
23352         }
23353         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23354             nopadtext = true;
23355         }
23356         
23357         
23358         // Traverse the tree
23359         i = 0;
23360         var currentElementChild = currentElement.childNodes.item(i);
23361         var allText = true;
23362         var innerHTML  = '';
23363         lastnode = '';
23364         while (currentElementChild) {
23365             // Formatting code (indent the tree so it looks nice on the screen)
23366             var nopad = nopadtext;
23367             if (lastnode == 'SPAN') {
23368                 nopad  = true;
23369             }
23370             // text
23371             if  (currentElementChild.nodeName == '#text') {
23372                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23373                 toadd = nopadtext ? toadd : toadd.trim();
23374                 if (!nopad && toadd.length > 80) {
23375                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23376                 }
23377                 innerHTML  += toadd;
23378                 
23379                 i++;
23380                 currentElementChild = currentElement.childNodes.item(i);
23381                 lastNode = '';
23382                 continue;
23383             }
23384             allText = false;
23385             
23386             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23387                 
23388             // Recursively traverse the tree structure of the child node
23389             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23390             lastnode = currentElementChild.nodeName;
23391             i++;
23392             currentElementChild=currentElement.childNodes.item(i);
23393         }
23394         
23395         ret += innerHTML;
23396         
23397         if (!allText) {
23398                 // The remaining code is mostly for formatting the tree
23399             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23400         }
23401         
23402         
23403         if (tagName) {
23404             ret+= "</"+tagName+">";
23405         }
23406         return ret;
23407         
23408     },
23409         
23410     applyBlacklists : function()
23411     {
23412         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23413         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23414         
23415         this.white = [];
23416         this.black = [];
23417         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23418             if (b.indexOf(tag) > -1) {
23419                 return;
23420             }
23421             this.white.push(tag);
23422             
23423         }, this);
23424         
23425         Roo.each(w, function(tag) {
23426             if (b.indexOf(tag) > -1) {
23427                 return;
23428             }
23429             if (this.white.indexOf(tag) > -1) {
23430                 return;
23431             }
23432             this.white.push(tag);
23433             
23434         }, this);
23435         
23436         
23437         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23438             if (w.indexOf(tag) > -1) {
23439                 return;
23440             }
23441             this.black.push(tag);
23442             
23443         }, this);
23444         
23445         Roo.each(b, function(tag) {
23446             if (w.indexOf(tag) > -1) {
23447                 return;
23448             }
23449             if (this.black.indexOf(tag) > -1) {
23450                 return;
23451             }
23452             this.black.push(tag);
23453             
23454         }, this);
23455         
23456         
23457         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23458         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23459         
23460         this.cwhite = [];
23461         this.cblack = [];
23462         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23463             if (b.indexOf(tag) > -1) {
23464                 return;
23465             }
23466             this.cwhite.push(tag);
23467             
23468         }, this);
23469         
23470         Roo.each(w, function(tag) {
23471             if (b.indexOf(tag) > -1) {
23472                 return;
23473             }
23474             if (this.cwhite.indexOf(tag) > -1) {
23475                 return;
23476             }
23477             this.cwhite.push(tag);
23478             
23479         }, this);
23480         
23481         
23482         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23483             if (w.indexOf(tag) > -1) {
23484                 return;
23485             }
23486             this.cblack.push(tag);
23487             
23488         }, this);
23489         
23490         Roo.each(b, function(tag) {
23491             if (w.indexOf(tag) > -1) {
23492                 return;
23493             }
23494             if (this.cblack.indexOf(tag) > -1) {
23495                 return;
23496             }
23497             this.cblack.push(tag);
23498             
23499         }, this);
23500     },
23501     
23502     setStylesheets : function(stylesheets)
23503     {
23504         if(typeof(stylesheets) == 'string'){
23505             Roo.get(this.iframe.contentDocument.head).createChild({
23506                 tag : 'link',
23507                 rel : 'stylesheet',
23508                 type : 'text/css',
23509                 href : stylesheets
23510             });
23511             
23512             return;
23513         }
23514         var _this = this;
23515      
23516         Roo.each(stylesheets, function(s) {
23517             if(!s.length){
23518                 return;
23519             }
23520             
23521             Roo.get(_this.iframe.contentDocument.head).createChild({
23522                 tag : 'link',
23523                 rel : 'stylesheet',
23524                 type : 'text/css',
23525                 href : s
23526             });
23527         });
23528
23529         
23530     },
23531     
23532     removeStylesheets : function()
23533     {
23534         var _this = this;
23535         
23536         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23537             s.remove();
23538         });
23539     },
23540     
23541     setStyle : function(style)
23542     {
23543         Roo.get(this.iframe.contentDocument.head).createChild({
23544             tag : 'style',
23545             type : 'text/css',
23546             html : style
23547         });
23548
23549         return;
23550     }
23551     
23552     // hide stuff that is not compatible
23553     /**
23554      * @event blur
23555      * @hide
23556      */
23557     /**
23558      * @event change
23559      * @hide
23560      */
23561     /**
23562      * @event focus
23563      * @hide
23564      */
23565     /**
23566      * @event specialkey
23567      * @hide
23568      */
23569     /**
23570      * @cfg {String} fieldClass @hide
23571      */
23572     /**
23573      * @cfg {String} focusClass @hide
23574      */
23575     /**
23576      * @cfg {String} autoCreate @hide
23577      */
23578     /**
23579      * @cfg {String} inputType @hide
23580      */
23581     /**
23582      * @cfg {String} invalidClass @hide
23583      */
23584     /**
23585      * @cfg {String} invalidText @hide
23586      */
23587     /**
23588      * @cfg {String} msgFx @hide
23589      */
23590     /**
23591      * @cfg {String} validateOnBlur @hide
23592      */
23593 });
23594
23595 Roo.HtmlEditorCore.white = [
23596         'area', 'br', 'img', 'input', 'hr', 'wbr',
23597         
23598        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23599        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23600        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23601        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23602        'table',   'ul',         'xmp', 
23603        
23604        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23605       'thead',   'tr', 
23606      
23607       'dir', 'menu', 'ol', 'ul', 'dl',
23608        
23609       'embed',  'object'
23610 ];
23611
23612
23613 Roo.HtmlEditorCore.black = [
23614     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23615         'applet', // 
23616         'base',   'basefont', 'bgsound', 'blink',  'body', 
23617         'frame',  'frameset', 'head',    'html',   'ilayer', 
23618         'iframe', 'layer',  'link',     'meta',    'object',   
23619         'script', 'style' ,'title',  'xml' // clean later..
23620 ];
23621 Roo.HtmlEditorCore.clean = [
23622     'script', 'style', 'title', 'xml'
23623 ];
23624 Roo.HtmlEditorCore.remove = [
23625     'font'
23626 ];
23627 // attributes..
23628
23629 Roo.HtmlEditorCore.ablack = [
23630     'on'
23631 ];
23632     
23633 Roo.HtmlEditorCore.aclean = [ 
23634     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23635 ];
23636
23637 // protocols..
23638 Roo.HtmlEditorCore.pwhite= [
23639         'http',  'https',  'mailto'
23640 ];
23641
23642 // white listed style attributes.
23643 Roo.HtmlEditorCore.cwhite= [
23644       //  'text-align', /// default is to allow most things..
23645       
23646          
23647 //        'font-size'//??
23648 ];
23649
23650 // black listed style attributes.
23651 Roo.HtmlEditorCore.cblack= [
23652       //  'font-size' -- this can be set by the project 
23653 ];
23654
23655
23656 Roo.HtmlEditorCore.swapCodes   =[ 
23657     [    8211, "--" ], 
23658     [    8212, "--" ], 
23659     [    8216,  "'" ],  
23660     [    8217, "'" ],  
23661     [    8220, '"' ],  
23662     [    8221, '"' ],  
23663     [    8226, "*" ],  
23664     [    8230, "..." ]
23665 ]; 
23666
23667     /*
23668  * - LGPL
23669  *
23670  * HtmlEditor
23671  * 
23672  */
23673
23674 /**
23675  * @class Roo.bootstrap.HtmlEditor
23676  * @extends Roo.bootstrap.TextArea
23677  * Bootstrap HtmlEditor class
23678
23679  * @constructor
23680  * Create a new HtmlEditor
23681  * @param {Object} config The config object
23682  */
23683
23684 Roo.bootstrap.HtmlEditor = function(config){
23685     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23686     if (!this.toolbars) {
23687         this.toolbars = [];
23688     }
23689     
23690     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23691     this.addEvents({
23692             /**
23693              * @event initialize
23694              * Fires when the editor is fully initialized (including the iframe)
23695              * @param {HtmlEditor} this
23696              */
23697             initialize: true,
23698             /**
23699              * @event activate
23700              * Fires when the editor is first receives the focus. Any insertion must wait
23701              * until after this event.
23702              * @param {HtmlEditor} this
23703              */
23704             activate: true,
23705              /**
23706              * @event beforesync
23707              * Fires before the textarea is updated with content from the editor iframe. Return false
23708              * to cancel the sync.
23709              * @param {HtmlEditor} this
23710              * @param {String} html
23711              */
23712             beforesync: true,
23713              /**
23714              * @event beforepush
23715              * Fires before the iframe editor is updated with content from the textarea. Return false
23716              * to cancel the push.
23717              * @param {HtmlEditor} this
23718              * @param {String} html
23719              */
23720             beforepush: true,
23721              /**
23722              * @event sync
23723              * Fires when the textarea is updated with content from the editor iframe.
23724              * @param {HtmlEditor} this
23725              * @param {String} html
23726              */
23727             sync: true,
23728              /**
23729              * @event push
23730              * Fires when the iframe editor is updated with content from the textarea.
23731              * @param {HtmlEditor} this
23732              * @param {String} html
23733              */
23734             push: true,
23735              /**
23736              * @event editmodechange
23737              * Fires when the editor switches edit modes
23738              * @param {HtmlEditor} this
23739              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23740              */
23741             editmodechange: true,
23742             /**
23743              * @event editorevent
23744              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23745              * @param {HtmlEditor} this
23746              */
23747             editorevent: true,
23748             /**
23749              * @event firstfocus
23750              * Fires when on first focus - needed by toolbars..
23751              * @param {HtmlEditor} this
23752              */
23753             firstfocus: true,
23754             /**
23755              * @event autosave
23756              * Auto save the htmlEditor value as a file into Events
23757              * @param {HtmlEditor} this
23758              */
23759             autosave: true,
23760             /**
23761              * @event savedpreview
23762              * preview the saved version of htmlEditor
23763              * @param {HtmlEditor} this
23764              */
23765             savedpreview: true
23766         });
23767 };
23768
23769
23770 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23771     
23772     
23773       /**
23774      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23775      */
23776     toolbars : false,
23777     
23778      /**
23779     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23780     */
23781     btns : [],
23782    
23783      /**
23784      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23785      *                        Roo.resizable.
23786      */
23787     resizable : false,
23788      /**
23789      * @cfg {Number} height (in pixels)
23790      */   
23791     height: 300,
23792    /**
23793      * @cfg {Number} width (in pixels)
23794      */   
23795     width: false,
23796     
23797     /**
23798      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23799      * 
23800      */
23801     stylesheets: false,
23802     
23803     // id of frame..
23804     frameId: false,
23805     
23806     // private properties
23807     validationEvent : false,
23808     deferHeight: true,
23809     initialized : false,
23810     activated : false,
23811     
23812     onFocus : Roo.emptyFn,
23813     iframePad:3,
23814     hideMode:'offsets',
23815     
23816     tbContainer : false,
23817     
23818     bodyCls : '',
23819     
23820     toolbarContainer :function() {
23821         return this.wrap.select('.x-html-editor-tb',true).first();
23822     },
23823
23824     /**
23825      * Protected method that will not generally be called directly. It
23826      * is called when the editor creates its toolbar. Override this method if you need to
23827      * add custom toolbar buttons.
23828      * @param {HtmlEditor} editor
23829      */
23830     createToolbar : function(){
23831         Roo.log('renewing');
23832         Roo.log("create toolbars");
23833         
23834         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23835         this.toolbars[0].render(this.toolbarContainer());
23836         
23837         return;
23838         
23839 //        if (!editor.toolbars || !editor.toolbars.length) {
23840 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23841 //        }
23842 //        
23843 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23844 //            editor.toolbars[i] = Roo.factory(
23845 //                    typeof(editor.toolbars[i]) == 'string' ?
23846 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23847 //                Roo.bootstrap.HtmlEditor);
23848 //            editor.toolbars[i].init(editor);
23849 //        }
23850     },
23851
23852      
23853     // private
23854     onRender : function(ct, position)
23855     {
23856        // Roo.log("Call onRender: " + this.xtype);
23857         var _t = this;
23858         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23859       
23860         this.wrap = this.inputEl().wrap({
23861             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23862         });
23863         
23864         this.editorcore.onRender(ct, position);
23865          
23866         if (this.resizable) {
23867             this.resizeEl = new Roo.Resizable(this.wrap, {
23868                 pinned : true,
23869                 wrap: true,
23870                 dynamic : true,
23871                 minHeight : this.height,
23872                 height: this.height,
23873                 handles : this.resizable,
23874                 width: this.width,
23875                 listeners : {
23876                     resize : function(r, w, h) {
23877                         _t.onResize(w,h); // -something
23878                     }
23879                 }
23880             });
23881             
23882         }
23883         this.createToolbar(this);
23884        
23885         
23886         if(!this.width && this.resizable){
23887             this.setSize(this.wrap.getSize());
23888         }
23889         if (this.resizeEl) {
23890             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23891             // should trigger onReize..
23892         }
23893         
23894     },
23895
23896     // private
23897     onResize : function(w, h)
23898     {
23899         Roo.log('resize: ' +w + ',' + h );
23900         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23901         var ew = false;
23902         var eh = false;
23903         
23904         if(this.inputEl() ){
23905             if(typeof w == 'number'){
23906                 var aw = w - this.wrap.getFrameWidth('lr');
23907                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23908                 ew = aw;
23909             }
23910             if(typeof h == 'number'){
23911                  var tbh = -11;  // fixme it needs to tool bar size!
23912                 for (var i =0; i < this.toolbars.length;i++) {
23913                     // fixme - ask toolbars for heights?
23914                     tbh += this.toolbars[i].el.getHeight();
23915                     //if (this.toolbars[i].footer) {
23916                     //    tbh += this.toolbars[i].footer.el.getHeight();
23917                     //}
23918                 }
23919               
23920                 
23921                 
23922                 
23923                 
23924                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23925                 ah -= 5; // knock a few pixes off for look..
23926                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23927                 var eh = ah;
23928             }
23929         }
23930         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23931         this.editorcore.onResize(ew,eh);
23932         
23933     },
23934
23935     /**
23936      * Toggles the editor between standard and source edit mode.
23937      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23938      */
23939     toggleSourceEdit : function(sourceEditMode)
23940     {
23941         this.editorcore.toggleSourceEdit(sourceEditMode);
23942         
23943         if(this.editorcore.sourceEditMode){
23944             Roo.log('editor - showing textarea');
23945             
23946 //            Roo.log('in');
23947 //            Roo.log(this.syncValue());
23948             this.syncValue();
23949             this.inputEl().removeClass(['hide', 'x-hidden']);
23950             this.inputEl().dom.removeAttribute('tabIndex');
23951             this.inputEl().focus();
23952         }else{
23953             Roo.log('editor - hiding textarea');
23954 //            Roo.log('out')
23955 //            Roo.log(this.pushValue()); 
23956             this.pushValue();
23957             
23958             this.inputEl().addClass(['hide', 'x-hidden']);
23959             this.inputEl().dom.setAttribute('tabIndex', -1);
23960             //this.deferFocus();
23961         }
23962          
23963         if(this.resizable){
23964             this.setSize(this.wrap.getSize());
23965         }
23966         
23967         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23968     },
23969  
23970     // private (for BoxComponent)
23971     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23972
23973     // private (for BoxComponent)
23974     getResizeEl : function(){
23975         return this.wrap;
23976     },
23977
23978     // private (for BoxComponent)
23979     getPositionEl : function(){
23980         return this.wrap;
23981     },
23982
23983     // private
23984     initEvents : function(){
23985         this.originalValue = this.getValue();
23986     },
23987
23988 //    /**
23989 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23990 //     * @method
23991 //     */
23992 //    markInvalid : Roo.emptyFn,
23993 //    /**
23994 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23995 //     * @method
23996 //     */
23997 //    clearInvalid : Roo.emptyFn,
23998
23999     setValue : function(v){
24000         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
24001         this.editorcore.pushValue();
24002     },
24003
24004      
24005     // private
24006     deferFocus : function(){
24007         this.focus.defer(10, this);
24008     },
24009
24010     // doc'ed in Field
24011     focus : function(){
24012         this.editorcore.focus();
24013         
24014     },
24015       
24016
24017     // private
24018     onDestroy : function(){
24019         
24020         
24021         
24022         if(this.rendered){
24023             
24024             for (var i =0; i < this.toolbars.length;i++) {
24025                 // fixme - ask toolbars for heights?
24026                 this.toolbars[i].onDestroy();
24027             }
24028             
24029             this.wrap.dom.innerHTML = '';
24030             this.wrap.remove();
24031         }
24032     },
24033
24034     // private
24035     onFirstFocus : function(){
24036         //Roo.log("onFirstFocus");
24037         this.editorcore.onFirstFocus();
24038          for (var i =0; i < this.toolbars.length;i++) {
24039             this.toolbars[i].onFirstFocus();
24040         }
24041         
24042     },
24043     
24044     // private
24045     syncValue : function()
24046     {   
24047         this.editorcore.syncValue();
24048     },
24049     
24050     pushValue : function()
24051     {   
24052         this.editorcore.pushValue();
24053     }
24054      
24055     
24056     // hide stuff that is not compatible
24057     /**
24058      * @event blur
24059      * @hide
24060      */
24061     /**
24062      * @event change
24063      * @hide
24064      */
24065     /**
24066      * @event focus
24067      * @hide
24068      */
24069     /**
24070      * @event specialkey
24071      * @hide
24072      */
24073     /**
24074      * @cfg {String} fieldClass @hide
24075      */
24076     /**
24077      * @cfg {String} focusClass @hide
24078      */
24079     /**
24080      * @cfg {String} autoCreate @hide
24081      */
24082     /**
24083      * @cfg {String} inputType @hide
24084      */
24085      
24086     /**
24087      * @cfg {String} invalidText @hide
24088      */
24089     /**
24090      * @cfg {String} msgFx @hide
24091      */
24092     /**
24093      * @cfg {String} validateOnBlur @hide
24094      */
24095 });
24096  
24097     
24098    
24099    
24100    
24101       
24102 Roo.namespace('Roo.bootstrap.htmleditor');
24103 /**
24104  * @class Roo.bootstrap.HtmlEditorToolbar1
24105  * Basic Toolbar
24106  * 
24107  * @example
24108  * Usage:
24109  *
24110  new Roo.bootstrap.HtmlEditor({
24111     ....
24112     toolbars : [
24113         new Roo.bootstrap.HtmlEditorToolbar1({
24114             disable : { fonts: 1 , format: 1, ..., ... , ...],
24115             btns : [ .... ]
24116         })
24117     }
24118      
24119  * 
24120  * @cfg {Object} disable List of elements to disable..
24121  * @cfg {Array} btns List of additional buttons.
24122  * 
24123  * 
24124  * NEEDS Extra CSS? 
24125  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24126  */
24127  
24128 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
24129 {
24130     
24131     Roo.apply(this, config);
24132     
24133     // default disabled, based on 'good practice'..
24134     this.disable = this.disable || {};
24135     Roo.applyIf(this.disable, {
24136         fontSize : true,
24137         colors : true,
24138         specialElements : true
24139     });
24140     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
24141     
24142     this.editor = config.editor;
24143     this.editorcore = config.editor.editorcore;
24144     
24145     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
24146     
24147     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24148     // dont call parent... till later.
24149 }
24150 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
24151      
24152     bar : true,
24153     
24154     editor : false,
24155     editorcore : false,
24156     
24157     
24158     formats : [
24159         "p" ,  
24160         "h1","h2","h3","h4","h5","h6", 
24161         "pre", "code", 
24162         "abbr", "acronym", "address", "cite", "samp", "var",
24163         'div','span'
24164     ],
24165     
24166     onRender : function(ct, position)
24167     {
24168        // Roo.log("Call onRender: " + this.xtype);
24169         
24170        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
24171        Roo.log(this.el);
24172        this.el.dom.style.marginBottom = '0';
24173        var _this = this;
24174        var editorcore = this.editorcore;
24175        var editor= this.editor;
24176        
24177        var children = [];
24178        var btn = function(id,cmd , toggle, handler, html){
24179        
24180             var  event = toggle ? 'toggle' : 'click';
24181        
24182             var a = {
24183                 size : 'sm',
24184                 xtype: 'Button',
24185                 xns: Roo.bootstrap,
24186                 //glyphicon : id,
24187                 fa: id,
24188                 cmd : id || cmd,
24189                 enableToggle:toggle !== false,
24190                 html : html || '',
24191                 pressed : toggle ? false : null,
24192                 listeners : {}
24193             };
24194             a.listeners[toggle ? 'toggle' : 'click'] = function() {
24195                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
24196             };
24197             children.push(a);
24198             return a;
24199        }
24200        
24201     //    var cb_box = function...
24202         
24203         var style = {
24204                 xtype: 'Button',
24205                 size : 'sm',
24206                 xns: Roo.bootstrap,
24207                 fa : 'font',
24208                 //html : 'submit'
24209                 menu : {
24210                     xtype: 'Menu',
24211                     xns: Roo.bootstrap,
24212                     items:  []
24213                 }
24214         };
24215         Roo.each(this.formats, function(f) {
24216             style.menu.items.push({
24217                 xtype :'MenuItem',
24218                 xns: Roo.bootstrap,
24219                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24220                 tagname : f,
24221                 listeners : {
24222                     click : function()
24223                     {
24224                         editorcore.insertTag(this.tagname);
24225                         editor.focus();
24226                     }
24227                 }
24228                 
24229             });
24230         });
24231         children.push(style);   
24232         
24233         btn('bold',false,true);
24234         btn('italic',false,true);
24235         btn('align-left', 'justifyleft',true);
24236         btn('align-center', 'justifycenter',true);
24237         btn('align-right' , 'justifyright',true);
24238         btn('link', false, false, function(btn) {
24239             //Roo.log("create link?");
24240             var url = prompt(this.createLinkText, this.defaultLinkValue);
24241             if(url && url != 'http:/'+'/'){
24242                 this.editorcore.relayCmd('createlink', url);
24243             }
24244         }),
24245         btn('list','insertunorderedlist',true);
24246         btn('pencil', false,true, function(btn){
24247                 Roo.log(this);
24248                 this.toggleSourceEdit(btn.pressed);
24249         });
24250         
24251         if (this.editor.btns.length > 0) {
24252             for (var i = 0; i<this.editor.btns.length; i++) {
24253                 children.push(this.editor.btns[i]);
24254             }
24255         }
24256         
24257         /*
24258         var cog = {
24259                 xtype: 'Button',
24260                 size : 'sm',
24261                 xns: Roo.bootstrap,
24262                 glyphicon : 'cog',
24263                 //html : 'submit'
24264                 menu : {
24265                     xtype: 'Menu',
24266                     xns: Roo.bootstrap,
24267                     items:  []
24268                 }
24269         };
24270         
24271         cog.menu.items.push({
24272             xtype :'MenuItem',
24273             xns: Roo.bootstrap,
24274             html : Clean styles,
24275             tagname : f,
24276             listeners : {
24277                 click : function()
24278                 {
24279                     editorcore.insertTag(this.tagname);
24280                     editor.focus();
24281                 }
24282             }
24283             
24284         });
24285        */
24286         
24287          
24288        this.xtype = 'NavSimplebar';
24289         
24290         for(var i=0;i< children.length;i++) {
24291             
24292             this.buttons.add(this.addxtypeChild(children[i]));
24293             
24294         }
24295         
24296         editor.on('editorevent', this.updateToolbar, this);
24297     },
24298     onBtnClick : function(id)
24299     {
24300        this.editorcore.relayCmd(id);
24301        this.editorcore.focus();
24302     },
24303     
24304     /**
24305      * Protected method that will not generally be called directly. It triggers
24306      * a toolbar update by reading the markup state of the current selection in the editor.
24307      */
24308     updateToolbar: function(){
24309
24310         if(!this.editorcore.activated){
24311             this.editor.onFirstFocus(); // is this neeed?
24312             return;
24313         }
24314
24315         var btns = this.buttons; 
24316         var doc = this.editorcore.doc;
24317         btns.get('bold').setActive(doc.queryCommandState('bold'));
24318         btns.get('italic').setActive(doc.queryCommandState('italic'));
24319         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24320         
24321         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24322         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24323         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24324         
24325         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24326         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24327          /*
24328         
24329         var ans = this.editorcore.getAllAncestors();
24330         if (this.formatCombo) {
24331             
24332             
24333             var store = this.formatCombo.store;
24334             this.formatCombo.setValue("");
24335             for (var i =0; i < ans.length;i++) {
24336                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24337                     // select it..
24338                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24339                     break;
24340                 }
24341             }
24342         }
24343         
24344         
24345         
24346         // hides menus... - so this cant be on a menu...
24347         Roo.bootstrap.MenuMgr.hideAll();
24348         */
24349         Roo.bootstrap.MenuMgr.hideAll();
24350         //this.editorsyncValue();
24351     },
24352     onFirstFocus: function() {
24353         this.buttons.each(function(item){
24354            item.enable();
24355         });
24356     },
24357     toggleSourceEdit : function(sourceEditMode){
24358         
24359           
24360         if(sourceEditMode){
24361             Roo.log("disabling buttons");
24362            this.buttons.each( function(item){
24363                 if(item.cmd != 'pencil'){
24364                     item.disable();
24365                 }
24366             });
24367           
24368         }else{
24369             Roo.log("enabling buttons");
24370             if(this.editorcore.initialized){
24371                 this.buttons.each( function(item){
24372                     item.enable();
24373                 });
24374             }
24375             
24376         }
24377         Roo.log("calling toggole on editor");
24378         // tell the editor that it's been pressed..
24379         this.editor.toggleSourceEdit(sourceEditMode);
24380        
24381     }
24382 });
24383
24384
24385
24386
24387
24388 /**
24389  * @class Roo.bootstrap.Table.AbstractSelectionModel
24390  * @extends Roo.util.Observable
24391  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24392  * implemented by descendant classes.  This class should not be directly instantiated.
24393  * @constructor
24394  */
24395 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24396     this.locked = false;
24397     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24398 };
24399
24400
24401 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24402     /** @ignore Called by the grid automatically. Do not call directly. */
24403     init : function(grid){
24404         this.grid = grid;
24405         this.initEvents();
24406     },
24407
24408     /**
24409      * Locks the selections.
24410      */
24411     lock : function(){
24412         this.locked = true;
24413     },
24414
24415     /**
24416      * Unlocks the selections.
24417      */
24418     unlock : function(){
24419         this.locked = false;
24420     },
24421
24422     /**
24423      * Returns true if the selections are locked.
24424      * @return {Boolean}
24425      */
24426     isLocked : function(){
24427         return this.locked;
24428     }
24429 });
24430 /**
24431  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24432  * @class Roo.bootstrap.Table.RowSelectionModel
24433  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24434  * It supports multiple selections and keyboard selection/navigation. 
24435  * @constructor
24436  * @param {Object} config
24437  */
24438
24439 Roo.bootstrap.Table.RowSelectionModel = function(config){
24440     Roo.apply(this, config);
24441     this.selections = new Roo.util.MixedCollection(false, function(o){
24442         return o.id;
24443     });
24444
24445     this.last = false;
24446     this.lastActive = false;
24447
24448     this.addEvents({
24449         /**
24450              * @event selectionchange
24451              * Fires when the selection changes
24452              * @param {SelectionModel} this
24453              */
24454             "selectionchange" : true,
24455         /**
24456              * @event afterselectionchange
24457              * Fires after the selection changes (eg. by key press or clicking)
24458              * @param {SelectionModel} this
24459              */
24460             "afterselectionchange" : true,
24461         /**
24462              * @event beforerowselect
24463              * Fires when a row is selected being selected, return false to cancel.
24464              * @param {SelectionModel} this
24465              * @param {Number} rowIndex The selected index
24466              * @param {Boolean} keepExisting False if other selections will be cleared
24467              */
24468             "beforerowselect" : true,
24469         /**
24470              * @event rowselect
24471              * Fires when a row is selected.
24472              * @param {SelectionModel} this
24473              * @param {Number} rowIndex The selected index
24474              * @param {Roo.data.Record} r The record
24475              */
24476             "rowselect" : true,
24477         /**
24478              * @event rowdeselect
24479              * Fires when a row is deselected.
24480              * @param {SelectionModel} this
24481              * @param {Number} rowIndex The selected index
24482              */
24483         "rowdeselect" : true
24484     });
24485     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24486     this.locked = false;
24487  };
24488
24489 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24490     /**
24491      * @cfg {Boolean} singleSelect
24492      * True to allow selection of only one row at a time (defaults to false)
24493      */
24494     singleSelect : false,
24495
24496     // private
24497     initEvents : function()
24498     {
24499
24500         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24501         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24502         //}else{ // allow click to work like normal
24503          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24504         //}
24505         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24506         this.grid.on("rowclick", this.handleMouseDown, this);
24507         
24508         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24509             "up" : function(e){
24510                 if(!e.shiftKey){
24511                     this.selectPrevious(e.shiftKey);
24512                 }else if(this.last !== false && this.lastActive !== false){
24513                     var last = this.last;
24514                     this.selectRange(this.last,  this.lastActive-1);
24515                     this.grid.getView().focusRow(this.lastActive);
24516                     if(last !== false){
24517                         this.last = last;
24518                     }
24519                 }else{
24520                     this.selectFirstRow();
24521                 }
24522                 this.fireEvent("afterselectionchange", this);
24523             },
24524             "down" : function(e){
24525                 if(!e.shiftKey){
24526                     this.selectNext(e.shiftKey);
24527                 }else if(this.last !== false && this.lastActive !== false){
24528                     var last = this.last;
24529                     this.selectRange(this.last,  this.lastActive+1);
24530                     this.grid.getView().focusRow(this.lastActive);
24531                     if(last !== false){
24532                         this.last = last;
24533                     }
24534                 }else{
24535                     this.selectFirstRow();
24536                 }
24537                 this.fireEvent("afterselectionchange", this);
24538             },
24539             scope: this
24540         });
24541         this.grid.store.on('load', function(){
24542             this.selections.clear();
24543         },this);
24544         /*
24545         var view = this.grid.view;
24546         view.on("refresh", this.onRefresh, this);
24547         view.on("rowupdated", this.onRowUpdated, this);
24548         view.on("rowremoved", this.onRemove, this);
24549         */
24550     },
24551
24552     // private
24553     onRefresh : function()
24554     {
24555         var ds = this.grid.store, i, v = this.grid.view;
24556         var s = this.selections;
24557         s.each(function(r){
24558             if((i = ds.indexOfId(r.id)) != -1){
24559                 v.onRowSelect(i);
24560             }else{
24561                 s.remove(r);
24562             }
24563         });
24564     },
24565
24566     // private
24567     onRemove : function(v, index, r){
24568         this.selections.remove(r);
24569     },
24570
24571     // private
24572     onRowUpdated : function(v, index, r){
24573         if(this.isSelected(r)){
24574             v.onRowSelect(index);
24575         }
24576     },
24577
24578     /**
24579      * Select records.
24580      * @param {Array} records The records to select
24581      * @param {Boolean} keepExisting (optional) True to keep existing selections
24582      */
24583     selectRecords : function(records, keepExisting)
24584     {
24585         if(!keepExisting){
24586             this.clearSelections();
24587         }
24588             var ds = this.grid.store;
24589         for(var i = 0, len = records.length; i < len; i++){
24590             this.selectRow(ds.indexOf(records[i]), true);
24591         }
24592     },
24593
24594     /**
24595      * Gets the number of selected rows.
24596      * @return {Number}
24597      */
24598     getCount : function(){
24599         return this.selections.length;
24600     },
24601
24602     /**
24603      * Selects the first row in the grid.
24604      */
24605     selectFirstRow : function(){
24606         this.selectRow(0);
24607     },
24608
24609     /**
24610      * Select the last row.
24611      * @param {Boolean} keepExisting (optional) True to keep existing selections
24612      */
24613     selectLastRow : function(keepExisting){
24614         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24615         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24616     },
24617
24618     /**
24619      * Selects the row immediately following the last selected row.
24620      * @param {Boolean} keepExisting (optional) True to keep existing selections
24621      */
24622     selectNext : function(keepExisting)
24623     {
24624             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24625             this.selectRow(this.last+1, keepExisting);
24626             this.grid.getView().focusRow(this.last);
24627         }
24628     },
24629
24630     /**
24631      * Selects the row that precedes the last selected row.
24632      * @param {Boolean} keepExisting (optional) True to keep existing selections
24633      */
24634     selectPrevious : function(keepExisting){
24635         if(this.last){
24636             this.selectRow(this.last-1, keepExisting);
24637             this.grid.getView().focusRow(this.last);
24638         }
24639     },
24640
24641     /**
24642      * Returns the selected records
24643      * @return {Array} Array of selected records
24644      */
24645     getSelections : function(){
24646         return [].concat(this.selections.items);
24647     },
24648
24649     /**
24650      * Returns the first selected record.
24651      * @return {Record}
24652      */
24653     getSelected : function(){
24654         return this.selections.itemAt(0);
24655     },
24656
24657
24658     /**
24659      * Clears all selections.
24660      */
24661     clearSelections : function(fast)
24662     {
24663         if(this.locked) {
24664             return;
24665         }
24666         if(fast !== true){
24667                 var ds = this.grid.store;
24668             var s = this.selections;
24669             s.each(function(r){
24670                 this.deselectRow(ds.indexOfId(r.id));
24671             }, this);
24672             s.clear();
24673         }else{
24674             this.selections.clear();
24675         }
24676         this.last = false;
24677     },
24678
24679
24680     /**
24681      * Selects all rows.
24682      */
24683     selectAll : function(){
24684         if(this.locked) {
24685             return;
24686         }
24687         this.selections.clear();
24688         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24689             this.selectRow(i, true);
24690         }
24691     },
24692
24693     /**
24694      * Returns True if there is a selection.
24695      * @return {Boolean}
24696      */
24697     hasSelection : function(){
24698         return this.selections.length > 0;
24699     },
24700
24701     /**
24702      * Returns True if the specified row is selected.
24703      * @param {Number/Record} record The record or index of the record to check
24704      * @return {Boolean}
24705      */
24706     isSelected : function(index){
24707             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24708         return (r && this.selections.key(r.id) ? true : false);
24709     },
24710
24711     /**
24712      * Returns True if the specified record id is selected.
24713      * @param {String} id The id of record to check
24714      * @return {Boolean}
24715      */
24716     isIdSelected : function(id){
24717         return (this.selections.key(id) ? true : false);
24718     },
24719
24720
24721     // private
24722     handleMouseDBClick : function(e, t){
24723         
24724     },
24725     // private
24726     handleMouseDown : function(e, t)
24727     {
24728             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24729         if(this.isLocked() || rowIndex < 0 ){
24730             return;
24731         };
24732         if(e.shiftKey && this.last !== false){
24733             var last = this.last;
24734             this.selectRange(last, rowIndex, e.ctrlKey);
24735             this.last = last; // reset the last
24736             t.focus();
24737     
24738         }else{
24739             var isSelected = this.isSelected(rowIndex);
24740             //Roo.log("select row:" + rowIndex);
24741             if(isSelected){
24742                 this.deselectRow(rowIndex);
24743             } else {
24744                         this.selectRow(rowIndex, true);
24745             }
24746     
24747             /*
24748                 if(e.button !== 0 && isSelected){
24749                 alert('rowIndex 2: ' + rowIndex);
24750                     view.focusRow(rowIndex);
24751                 }else if(e.ctrlKey && isSelected){
24752                     this.deselectRow(rowIndex);
24753                 }else if(!isSelected){
24754                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24755                     view.focusRow(rowIndex);
24756                 }
24757             */
24758         }
24759         this.fireEvent("afterselectionchange", this);
24760     },
24761     // private
24762     handleDragableRowClick :  function(grid, rowIndex, e) 
24763     {
24764         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24765             this.selectRow(rowIndex, false);
24766             grid.view.focusRow(rowIndex);
24767              this.fireEvent("afterselectionchange", this);
24768         }
24769     },
24770     
24771     /**
24772      * Selects multiple rows.
24773      * @param {Array} rows Array of the indexes of the row to select
24774      * @param {Boolean} keepExisting (optional) True to keep existing selections
24775      */
24776     selectRows : function(rows, keepExisting){
24777         if(!keepExisting){
24778             this.clearSelections();
24779         }
24780         for(var i = 0, len = rows.length; i < len; i++){
24781             this.selectRow(rows[i], true);
24782         }
24783     },
24784
24785     /**
24786      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24787      * @param {Number} startRow The index of the first row in the range
24788      * @param {Number} endRow The index of the last row in the range
24789      * @param {Boolean} keepExisting (optional) True to retain existing selections
24790      */
24791     selectRange : function(startRow, endRow, keepExisting){
24792         if(this.locked) {
24793             return;
24794         }
24795         if(!keepExisting){
24796             this.clearSelections();
24797         }
24798         if(startRow <= endRow){
24799             for(var i = startRow; i <= endRow; i++){
24800                 this.selectRow(i, true);
24801             }
24802         }else{
24803             for(var i = startRow; i >= endRow; i--){
24804                 this.selectRow(i, true);
24805             }
24806         }
24807     },
24808
24809     /**
24810      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24811      * @param {Number} startRow The index of the first row in the range
24812      * @param {Number} endRow The index of the last row in the range
24813      */
24814     deselectRange : function(startRow, endRow, preventViewNotify){
24815         if(this.locked) {
24816             return;
24817         }
24818         for(var i = startRow; i <= endRow; i++){
24819             this.deselectRow(i, preventViewNotify);
24820         }
24821     },
24822
24823     /**
24824      * Selects a row.
24825      * @param {Number} row The index of the row to select
24826      * @param {Boolean} keepExisting (optional) True to keep existing selections
24827      */
24828     selectRow : function(index, keepExisting, preventViewNotify)
24829     {
24830             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24831             return;
24832         }
24833         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24834             if(!keepExisting || this.singleSelect){
24835                 this.clearSelections();
24836             }
24837             
24838             var r = this.grid.store.getAt(index);
24839             //console.log('selectRow - record id :' + r.id);
24840             
24841             this.selections.add(r);
24842             this.last = this.lastActive = index;
24843             if(!preventViewNotify){
24844                 var proxy = new Roo.Element(
24845                                 this.grid.getRowDom(index)
24846                 );
24847                 proxy.addClass('bg-info info');
24848             }
24849             this.fireEvent("rowselect", this, index, r);
24850             this.fireEvent("selectionchange", this);
24851         }
24852     },
24853
24854     /**
24855      * Deselects a row.
24856      * @param {Number} row The index of the row to deselect
24857      */
24858     deselectRow : function(index, preventViewNotify)
24859     {
24860         if(this.locked) {
24861             return;
24862         }
24863         if(this.last == index){
24864             this.last = false;
24865         }
24866         if(this.lastActive == index){
24867             this.lastActive = false;
24868         }
24869         
24870         var r = this.grid.store.getAt(index);
24871         if (!r) {
24872             return;
24873         }
24874         
24875         this.selections.remove(r);
24876         //.console.log('deselectRow - record id :' + r.id);
24877         if(!preventViewNotify){
24878         
24879             var proxy = new Roo.Element(
24880                 this.grid.getRowDom(index)
24881             );
24882             proxy.removeClass('bg-info info');
24883         }
24884         this.fireEvent("rowdeselect", this, index);
24885         this.fireEvent("selectionchange", this);
24886     },
24887
24888     // private
24889     restoreLast : function(){
24890         if(this._last){
24891             this.last = this._last;
24892         }
24893     },
24894
24895     // private
24896     acceptsNav : function(row, col, cm){
24897         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24898     },
24899
24900     // private
24901     onEditorKey : function(field, e){
24902         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24903         if(k == e.TAB){
24904             e.stopEvent();
24905             ed.completeEdit();
24906             if(e.shiftKey){
24907                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24908             }else{
24909                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24910             }
24911         }else if(k == e.ENTER && !e.ctrlKey){
24912             e.stopEvent();
24913             ed.completeEdit();
24914             if(e.shiftKey){
24915                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24916             }else{
24917                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24918             }
24919         }else if(k == e.ESC){
24920             ed.cancelEdit();
24921         }
24922         if(newCell){
24923             g.startEditing(newCell[0], newCell[1]);
24924         }
24925     }
24926 });
24927 /*
24928  * Based on:
24929  * Ext JS Library 1.1.1
24930  * Copyright(c) 2006-2007, Ext JS, LLC.
24931  *
24932  * Originally Released Under LGPL - original licence link has changed is not relivant.
24933  *
24934  * Fork - LGPL
24935  * <script type="text/javascript">
24936  */
24937  
24938 /**
24939  * @class Roo.bootstrap.PagingToolbar
24940  * @extends Roo.bootstrap.NavSimplebar
24941  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24942  * @constructor
24943  * Create a new PagingToolbar
24944  * @param {Object} config The config object
24945  * @param {Roo.data.Store} store
24946  */
24947 Roo.bootstrap.PagingToolbar = function(config)
24948 {
24949     // old args format still supported... - xtype is prefered..
24950         // created from xtype...
24951     
24952     this.ds = config.dataSource;
24953     
24954     if (config.store && !this.ds) {
24955         this.store= Roo.factory(config.store, Roo.data);
24956         this.ds = this.store;
24957         this.ds.xmodule = this.xmodule || false;
24958     }
24959     
24960     this.toolbarItems = [];
24961     if (config.items) {
24962         this.toolbarItems = config.items;
24963     }
24964     
24965     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24966     
24967     this.cursor = 0;
24968     
24969     if (this.ds) { 
24970         this.bind(this.ds);
24971     }
24972     
24973     if (Roo.bootstrap.version == 4) {
24974         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24975     } else {
24976         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24977     }
24978     
24979 };
24980
24981 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24982     /**
24983      * @cfg {Roo.data.Store} dataSource
24984      * The underlying data store providing the paged data
24985      */
24986     /**
24987      * @cfg {String/HTMLElement/Element} container
24988      * container The id or element that will contain the toolbar
24989      */
24990     /**
24991      * @cfg {Boolean} displayInfo
24992      * True to display the displayMsg (defaults to false)
24993      */
24994     /**
24995      * @cfg {Number} pageSize
24996      * The number of records to display per page (defaults to 20)
24997      */
24998     pageSize: 20,
24999     /**
25000      * @cfg {String} displayMsg
25001      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
25002      */
25003     displayMsg : 'Displaying {0} - {1} of {2}',
25004     /**
25005      * @cfg {String} emptyMsg
25006      * The message to display when no records are found (defaults to "No data to display")
25007      */
25008     emptyMsg : 'No data to display',
25009     /**
25010      * Customizable piece of the default paging text (defaults to "Page")
25011      * @type String
25012      */
25013     beforePageText : "Page",
25014     /**
25015      * Customizable piece of the default paging text (defaults to "of %0")
25016      * @type String
25017      */
25018     afterPageText : "of {0}",
25019     /**
25020      * Customizable piece of the default paging text (defaults to "First Page")
25021      * @type String
25022      */
25023     firstText : "First Page",
25024     /**
25025      * Customizable piece of the default paging text (defaults to "Previous Page")
25026      * @type String
25027      */
25028     prevText : "Previous Page",
25029     /**
25030      * Customizable piece of the default paging text (defaults to "Next Page")
25031      * @type String
25032      */
25033     nextText : "Next Page",
25034     /**
25035      * Customizable piece of the default paging text (defaults to "Last Page")
25036      * @type String
25037      */
25038     lastText : "Last Page",
25039     /**
25040      * Customizable piece of the default paging text (defaults to "Refresh")
25041      * @type String
25042      */
25043     refreshText : "Refresh",
25044
25045     buttons : false,
25046     // private
25047     onRender : function(ct, position) 
25048     {
25049         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
25050         this.navgroup.parentId = this.id;
25051         this.navgroup.onRender(this.el, null);
25052         // add the buttons to the navgroup
25053         
25054         if(this.displayInfo){
25055             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
25056             this.displayEl = this.el.select('.x-paging-info', true).first();
25057 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
25058 //            this.displayEl = navel.el.select('span',true).first();
25059         }
25060         
25061         var _this = this;
25062         
25063         if(this.buttons){
25064             Roo.each(_this.buttons, function(e){ // this might need to use render????
25065                Roo.factory(e).render(_this.el);
25066             });
25067         }
25068             
25069         Roo.each(_this.toolbarItems, function(e) {
25070             _this.navgroup.addItem(e);
25071         });
25072         
25073         
25074         this.first = this.navgroup.addItem({
25075             tooltip: this.firstText,
25076             cls: "prev btn-outline-secondary",
25077             html : ' <i class="fa fa-step-backward"></i>',
25078             disabled: true,
25079             preventDefault: true,
25080             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
25081         });
25082         
25083         this.prev =  this.navgroup.addItem({
25084             tooltip: this.prevText,
25085             cls: "prev btn-outline-secondary",
25086             html : ' <i class="fa fa-backward"></i>',
25087             disabled: true,
25088             preventDefault: true,
25089             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
25090         });
25091     //this.addSeparator();
25092         
25093         
25094         var field = this.navgroup.addItem( {
25095             tagtype : 'span',
25096             cls : 'x-paging-position  btn-outline-secondary',
25097              disabled: true,
25098             html : this.beforePageText  +
25099                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
25100                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
25101          } ); //?? escaped?
25102         
25103         this.field = field.el.select('input', true).first();
25104         this.field.on("keydown", this.onPagingKeydown, this);
25105         this.field.on("focus", function(){this.dom.select();});
25106     
25107     
25108         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
25109         //this.field.setHeight(18);
25110         //this.addSeparator();
25111         this.next = this.navgroup.addItem({
25112             tooltip: this.nextText,
25113             cls: "next btn-outline-secondary",
25114             html : ' <i class="fa fa-forward"></i>',
25115             disabled: true,
25116             preventDefault: true,
25117             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
25118         });
25119         this.last = this.navgroup.addItem({
25120             tooltip: this.lastText,
25121             html : ' <i class="fa fa-step-forward"></i>',
25122             cls: "next btn-outline-secondary",
25123             disabled: true,
25124             preventDefault: true,
25125             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
25126         });
25127     //this.addSeparator();
25128         this.loading = this.navgroup.addItem({
25129             tooltip: this.refreshText,
25130             cls: "btn-outline-secondary",
25131             html : ' <i class="fa fa-refresh"></i>',
25132             preventDefault: true,
25133             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
25134         });
25135         
25136     },
25137
25138     // private
25139     updateInfo : function(){
25140         if(this.displayEl){
25141             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
25142             var msg = count == 0 ?
25143                 this.emptyMsg :
25144                 String.format(
25145                     this.displayMsg,
25146                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
25147                 );
25148             this.displayEl.update(msg);
25149         }
25150     },
25151
25152     // private
25153     onLoad : function(ds, r, o)
25154     {
25155         this.cursor = o.params.start ? o.params.start : 0;
25156         
25157         var d = this.getPageData(),
25158             ap = d.activePage,
25159             ps = d.pages;
25160         
25161         
25162         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
25163         this.field.dom.value = ap;
25164         this.first.setDisabled(ap == 1);
25165         this.prev.setDisabled(ap == 1);
25166         this.next.setDisabled(ap == ps);
25167         this.last.setDisabled(ap == ps);
25168         this.loading.enable();
25169         this.updateInfo();
25170     },
25171
25172     // private
25173     getPageData : function(){
25174         var total = this.ds.getTotalCount();
25175         return {
25176             total : total,
25177             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
25178             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
25179         };
25180     },
25181
25182     // private
25183     onLoadError : function(){
25184         this.loading.enable();
25185     },
25186
25187     // private
25188     onPagingKeydown : function(e){
25189         var k = e.getKey();
25190         var d = this.getPageData();
25191         if(k == e.RETURN){
25192             var v = this.field.dom.value, pageNum;
25193             if(!v || isNaN(pageNum = parseInt(v, 10))){
25194                 this.field.dom.value = d.activePage;
25195                 return;
25196             }
25197             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
25198             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25199             e.stopEvent();
25200         }
25201         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))
25202         {
25203           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
25204           this.field.dom.value = pageNum;
25205           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
25206           e.stopEvent();
25207         }
25208         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
25209         {
25210           var v = this.field.dom.value, pageNum; 
25211           var increment = (e.shiftKey) ? 10 : 1;
25212           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
25213                 increment *= -1;
25214           }
25215           if(!v || isNaN(pageNum = parseInt(v, 10))) {
25216             this.field.dom.value = d.activePage;
25217             return;
25218           }
25219           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25220           {
25221             this.field.dom.value = parseInt(v, 10) + increment;
25222             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25223             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25224           }
25225           e.stopEvent();
25226         }
25227     },
25228
25229     // private
25230     beforeLoad : function(){
25231         if(this.loading){
25232             this.loading.disable();
25233         }
25234     },
25235
25236     // private
25237     onClick : function(which){
25238         
25239         var ds = this.ds;
25240         if (!ds) {
25241             return;
25242         }
25243         
25244         switch(which){
25245             case "first":
25246                 ds.load({params:{start: 0, limit: this.pageSize}});
25247             break;
25248             case "prev":
25249                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25250             break;
25251             case "next":
25252                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25253             break;
25254             case "last":
25255                 var total = ds.getTotalCount();
25256                 var extra = total % this.pageSize;
25257                 var lastStart = extra ? (total - extra) : total-this.pageSize;
25258                 ds.load({params:{start: lastStart, limit: this.pageSize}});
25259             break;
25260             case "refresh":
25261                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25262             break;
25263         }
25264     },
25265
25266     /**
25267      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25268      * @param {Roo.data.Store} store The data store to unbind
25269      */
25270     unbind : function(ds){
25271         ds.un("beforeload", this.beforeLoad, this);
25272         ds.un("load", this.onLoad, this);
25273         ds.un("loadexception", this.onLoadError, this);
25274         ds.un("remove", this.updateInfo, this);
25275         ds.un("add", this.updateInfo, this);
25276         this.ds = undefined;
25277     },
25278
25279     /**
25280      * Binds the paging toolbar to the specified {@link Roo.data.Store}
25281      * @param {Roo.data.Store} store The data store to bind
25282      */
25283     bind : function(ds){
25284         ds.on("beforeload", this.beforeLoad, this);
25285         ds.on("load", this.onLoad, this);
25286         ds.on("loadexception", this.onLoadError, this);
25287         ds.on("remove", this.updateInfo, this);
25288         ds.on("add", this.updateInfo, this);
25289         this.ds = ds;
25290     }
25291 });/*
25292  * - LGPL
25293  *
25294  * element
25295  * 
25296  */
25297
25298 /**
25299  * @class Roo.bootstrap.MessageBar
25300  * @extends Roo.bootstrap.Component
25301  * Bootstrap MessageBar class
25302  * @cfg {String} html contents of the MessageBar
25303  * @cfg {String} weight (info | success | warning | danger) default info
25304  * @cfg {String} beforeClass insert the bar before the given class
25305  * @cfg {Boolean} closable (true | false) default false
25306  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25307  * 
25308  * @constructor
25309  * Create a new Element
25310  * @param {Object} config The config object
25311  */
25312
25313 Roo.bootstrap.MessageBar = function(config){
25314     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25315 };
25316
25317 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25318     
25319     html: '',
25320     weight: 'info',
25321     closable: false,
25322     fixed: false,
25323     beforeClass: 'bootstrap-sticky-wrap',
25324     
25325     getAutoCreate : function(){
25326         
25327         var cfg = {
25328             tag: 'div',
25329             cls: 'alert alert-dismissable alert-' + this.weight,
25330             cn: [
25331                 {
25332                     tag: 'span',
25333                     cls: 'message',
25334                     html: this.html || ''
25335                 }
25336             ]
25337         };
25338         
25339         if(this.fixed){
25340             cfg.cls += ' alert-messages-fixed';
25341         }
25342         
25343         if(this.closable){
25344             cfg.cn.push({
25345                 tag: 'button',
25346                 cls: 'close',
25347                 html: 'x'
25348             });
25349         }
25350         
25351         return cfg;
25352     },
25353     
25354     onRender : function(ct, position)
25355     {
25356         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25357         
25358         if(!this.el){
25359             var cfg = Roo.apply({},  this.getAutoCreate());
25360             cfg.id = Roo.id();
25361             
25362             if (this.cls) {
25363                 cfg.cls += ' ' + this.cls;
25364             }
25365             if (this.style) {
25366                 cfg.style = this.style;
25367             }
25368             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25369             
25370             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25371         }
25372         
25373         this.el.select('>button.close').on('click', this.hide, this);
25374         
25375     },
25376     
25377     show : function()
25378     {
25379         if (!this.rendered) {
25380             this.render();
25381         }
25382         
25383         this.el.show();
25384         
25385         this.fireEvent('show', this);
25386         
25387     },
25388     
25389     hide : function()
25390     {
25391         if (!this.rendered) {
25392             this.render();
25393         }
25394         
25395         this.el.hide();
25396         
25397         this.fireEvent('hide', this);
25398     },
25399     
25400     update : function()
25401     {
25402 //        var e = this.el.dom.firstChild;
25403 //        
25404 //        if(this.closable){
25405 //            e = e.nextSibling;
25406 //        }
25407 //        
25408 //        e.data = this.html || '';
25409
25410         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25411     }
25412    
25413 });
25414
25415  
25416
25417      /*
25418  * - LGPL
25419  *
25420  * Graph
25421  * 
25422  */
25423
25424
25425 /**
25426  * @class Roo.bootstrap.Graph
25427  * @extends Roo.bootstrap.Component
25428  * Bootstrap Graph class
25429 > Prameters
25430  -sm {number} sm 4
25431  -md {number} md 5
25432  @cfg {String} graphtype  bar | vbar | pie
25433  @cfg {number} g_x coodinator | centre x (pie)
25434  @cfg {number} g_y coodinator | centre y (pie)
25435  @cfg {number} g_r radius (pie)
25436  @cfg {number} g_height height of the chart (respected by all elements in the set)
25437  @cfg {number} g_width width of the chart (respected by all elements in the set)
25438  @cfg {Object} title The title of the chart
25439     
25440  -{Array}  values
25441  -opts (object) options for the chart 
25442      o {
25443      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25444      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25445      o vgutter (number)
25446      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.
25447      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25448      o to
25449      o stretch (boolean)
25450      o }
25451  -opts (object) options for the pie
25452      o{
25453      o cut
25454      o startAngle (number)
25455      o endAngle (number)
25456      } 
25457  *
25458  * @constructor
25459  * Create a new Input
25460  * @param {Object} config The config object
25461  */
25462
25463 Roo.bootstrap.Graph = function(config){
25464     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25465     
25466     this.addEvents({
25467         // img events
25468         /**
25469          * @event click
25470          * The img click event for the img.
25471          * @param {Roo.EventObject} e
25472          */
25473         "click" : true
25474     });
25475 };
25476
25477 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25478     
25479     sm: 4,
25480     md: 5,
25481     graphtype: 'bar',
25482     g_height: 250,
25483     g_width: 400,
25484     g_x: 50,
25485     g_y: 50,
25486     g_r: 30,
25487     opts:{
25488         //g_colors: this.colors,
25489         g_type: 'soft',
25490         g_gutter: '20%'
25491
25492     },
25493     title : false,
25494
25495     getAutoCreate : function(){
25496         
25497         var cfg = {
25498             tag: 'div',
25499             html : null
25500         };
25501         
25502         
25503         return  cfg;
25504     },
25505
25506     onRender : function(ct,position){
25507         
25508         
25509         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25510         
25511         if (typeof(Raphael) == 'undefined') {
25512             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25513             return;
25514         }
25515         
25516         this.raphael = Raphael(this.el.dom);
25517         
25518                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25519                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25520                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25521                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25522                 /*
25523                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25524                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25525                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25526                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25527                 
25528                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25529                 r.barchart(330, 10, 300, 220, data1);
25530                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25531                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25532                 */
25533                 
25534                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25535                 // r.barchart(30, 30, 560, 250,  xdata, {
25536                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25537                 //     axis : "0 0 1 1",
25538                 //     axisxlabels :  xdata
25539                 //     //yvalues : cols,
25540                    
25541                 // });
25542 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25543 //        
25544 //        this.load(null,xdata,{
25545 //                axis : "0 0 1 1",
25546 //                axisxlabels :  xdata
25547 //                });
25548
25549     },
25550
25551     load : function(graphtype,xdata,opts)
25552     {
25553         this.raphael.clear();
25554         if(!graphtype) {
25555             graphtype = this.graphtype;
25556         }
25557         if(!opts){
25558             opts = this.opts;
25559         }
25560         var r = this.raphael,
25561             fin = function () {
25562                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25563             },
25564             fout = function () {
25565                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25566             },
25567             pfin = function() {
25568                 this.sector.stop();
25569                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25570
25571                 if (this.label) {
25572                     this.label[0].stop();
25573                     this.label[0].attr({ r: 7.5 });
25574                     this.label[1].attr({ "font-weight": 800 });
25575                 }
25576             },
25577             pfout = function() {
25578                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25579
25580                 if (this.label) {
25581                     this.label[0].animate({ r: 5 }, 500, "bounce");
25582                     this.label[1].attr({ "font-weight": 400 });
25583                 }
25584             };
25585
25586         switch(graphtype){
25587             case 'bar':
25588                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25589                 break;
25590             case 'hbar':
25591                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25592                 break;
25593             case 'pie':
25594 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25595 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25596 //            
25597                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25598                 
25599                 break;
25600
25601         }
25602         
25603         if(this.title){
25604             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25605         }
25606         
25607     },
25608     
25609     setTitle: function(o)
25610     {
25611         this.title = o;
25612     },
25613     
25614     initEvents: function() {
25615         
25616         if(!this.href){
25617             this.el.on('click', this.onClick, this);
25618         }
25619     },
25620     
25621     onClick : function(e)
25622     {
25623         Roo.log('img onclick');
25624         this.fireEvent('click', this, e);
25625     }
25626    
25627 });
25628
25629  
25630 /*
25631  * - LGPL
25632  *
25633  * numberBox
25634  * 
25635  */
25636 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25637
25638 /**
25639  * @class Roo.bootstrap.dash.NumberBox
25640  * @extends Roo.bootstrap.Component
25641  * Bootstrap NumberBox class
25642  * @cfg {String} headline Box headline
25643  * @cfg {String} content Box content
25644  * @cfg {String} icon Box icon
25645  * @cfg {String} footer Footer text
25646  * @cfg {String} fhref Footer href
25647  * 
25648  * @constructor
25649  * Create a new NumberBox
25650  * @param {Object} config The config object
25651  */
25652
25653
25654 Roo.bootstrap.dash.NumberBox = function(config){
25655     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25656     
25657 };
25658
25659 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25660     
25661     headline : '',
25662     content : '',
25663     icon : '',
25664     footer : '',
25665     fhref : '',
25666     ficon : '',
25667     
25668     getAutoCreate : function(){
25669         
25670         var cfg = {
25671             tag : 'div',
25672             cls : 'small-box ',
25673             cn : [
25674                 {
25675                     tag : 'div',
25676                     cls : 'inner',
25677                     cn :[
25678                         {
25679                             tag : 'h3',
25680                             cls : 'roo-headline',
25681                             html : this.headline
25682                         },
25683                         {
25684                             tag : 'p',
25685                             cls : 'roo-content',
25686                             html : this.content
25687                         }
25688                     ]
25689                 }
25690             ]
25691         };
25692         
25693         if(this.icon){
25694             cfg.cn.push({
25695                 tag : 'div',
25696                 cls : 'icon',
25697                 cn :[
25698                     {
25699                         tag : 'i',
25700                         cls : 'ion ' + this.icon
25701                     }
25702                 ]
25703             });
25704         }
25705         
25706         if(this.footer){
25707             var footer = {
25708                 tag : 'a',
25709                 cls : 'small-box-footer',
25710                 href : this.fhref || '#',
25711                 html : this.footer
25712             };
25713             
25714             cfg.cn.push(footer);
25715             
25716         }
25717         
25718         return  cfg;
25719     },
25720
25721     onRender : function(ct,position){
25722         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25723
25724
25725        
25726                 
25727     },
25728
25729     setHeadline: function (value)
25730     {
25731         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25732     },
25733     
25734     setFooter: function (value, href)
25735     {
25736         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25737         
25738         if(href){
25739             this.el.select('a.small-box-footer',true).first().attr('href', href);
25740         }
25741         
25742     },
25743
25744     setContent: function (value)
25745     {
25746         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25747     },
25748
25749     initEvents: function() 
25750     {   
25751         
25752     }
25753     
25754 });
25755
25756  
25757 /*
25758  * - LGPL
25759  *
25760  * TabBox
25761  * 
25762  */
25763 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25764
25765 /**
25766  * @class Roo.bootstrap.dash.TabBox
25767  * @extends Roo.bootstrap.Component
25768  * Bootstrap TabBox class
25769  * @cfg {String} title Title of the TabBox
25770  * @cfg {String} icon Icon of the TabBox
25771  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25772  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25773  * 
25774  * @constructor
25775  * Create a new TabBox
25776  * @param {Object} config The config object
25777  */
25778
25779
25780 Roo.bootstrap.dash.TabBox = function(config){
25781     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25782     this.addEvents({
25783         // raw events
25784         /**
25785          * @event addpane
25786          * When a pane is added
25787          * @param {Roo.bootstrap.dash.TabPane} pane
25788          */
25789         "addpane" : true,
25790         /**
25791          * @event activatepane
25792          * When a pane is activated
25793          * @param {Roo.bootstrap.dash.TabPane} pane
25794          */
25795         "activatepane" : true
25796         
25797          
25798     });
25799     
25800     this.panes = [];
25801 };
25802
25803 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25804
25805     title : '',
25806     icon : false,
25807     showtabs : true,
25808     tabScrollable : false,
25809     
25810     getChildContainer : function()
25811     {
25812         return this.el.select('.tab-content', true).first();
25813     },
25814     
25815     getAutoCreate : function(){
25816         
25817         var header = {
25818             tag: 'li',
25819             cls: 'pull-left header',
25820             html: this.title,
25821             cn : []
25822         };
25823         
25824         if(this.icon){
25825             header.cn.push({
25826                 tag: 'i',
25827                 cls: 'fa ' + this.icon
25828             });
25829         }
25830         
25831         var h = {
25832             tag: 'ul',
25833             cls: 'nav nav-tabs pull-right',
25834             cn: [
25835                 header
25836             ]
25837         };
25838         
25839         if(this.tabScrollable){
25840             h = {
25841                 tag: 'div',
25842                 cls: 'tab-header',
25843                 cn: [
25844                     {
25845                         tag: 'ul',
25846                         cls: 'nav nav-tabs pull-right',
25847                         cn: [
25848                             header
25849                         ]
25850                     }
25851                 ]
25852             };
25853         }
25854         
25855         var cfg = {
25856             tag: 'div',
25857             cls: 'nav-tabs-custom',
25858             cn: [
25859                 h,
25860                 {
25861                     tag: 'div',
25862                     cls: 'tab-content no-padding',
25863                     cn: []
25864                 }
25865             ]
25866         };
25867
25868         return  cfg;
25869     },
25870     initEvents : function()
25871     {
25872         //Roo.log('add add pane handler');
25873         this.on('addpane', this.onAddPane, this);
25874     },
25875      /**
25876      * Updates the box title
25877      * @param {String} html to set the title to.
25878      */
25879     setTitle : function(value)
25880     {
25881         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25882     },
25883     onAddPane : function(pane)
25884     {
25885         this.panes.push(pane);
25886         //Roo.log('addpane');
25887         //Roo.log(pane);
25888         // tabs are rendere left to right..
25889         if(!this.showtabs){
25890             return;
25891         }
25892         
25893         var ctr = this.el.select('.nav-tabs', true).first();
25894          
25895          
25896         var existing = ctr.select('.nav-tab',true);
25897         var qty = existing.getCount();;
25898         
25899         
25900         var tab = ctr.createChild({
25901             tag : 'li',
25902             cls : 'nav-tab' + (qty ? '' : ' active'),
25903             cn : [
25904                 {
25905                     tag : 'a',
25906                     href:'#',
25907                     html : pane.title
25908                 }
25909             ]
25910         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25911         pane.tab = tab;
25912         
25913         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25914         if (!qty) {
25915             pane.el.addClass('active');
25916         }
25917         
25918                 
25919     },
25920     onTabClick : function(ev,un,ob,pane)
25921     {
25922         //Roo.log('tab - prev default');
25923         ev.preventDefault();
25924         
25925         
25926         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25927         pane.tab.addClass('active');
25928         //Roo.log(pane.title);
25929         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25930         // technically we should have a deactivate event.. but maybe add later.
25931         // and it should not de-activate the selected tab...
25932         this.fireEvent('activatepane', pane);
25933         pane.el.addClass('active');
25934         pane.fireEvent('activate');
25935         
25936         
25937     },
25938     
25939     getActivePane : function()
25940     {
25941         var r = false;
25942         Roo.each(this.panes, function(p) {
25943             if(p.el.hasClass('active')){
25944                 r = p;
25945                 return false;
25946             }
25947             
25948             return;
25949         });
25950         
25951         return r;
25952     }
25953     
25954     
25955 });
25956
25957  
25958 /*
25959  * - LGPL
25960  *
25961  * Tab pane
25962  * 
25963  */
25964 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25965 /**
25966  * @class Roo.bootstrap.TabPane
25967  * @extends Roo.bootstrap.Component
25968  * Bootstrap TabPane class
25969  * @cfg {Boolean} active (false | true) Default false
25970  * @cfg {String} title title of panel
25971
25972  * 
25973  * @constructor
25974  * Create a new TabPane
25975  * @param {Object} config The config object
25976  */
25977
25978 Roo.bootstrap.dash.TabPane = function(config){
25979     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25980     
25981     this.addEvents({
25982         // raw events
25983         /**
25984          * @event activate
25985          * When a pane is activated
25986          * @param {Roo.bootstrap.dash.TabPane} pane
25987          */
25988         "activate" : true
25989          
25990     });
25991 };
25992
25993 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25994     
25995     active : false,
25996     title : '',
25997     
25998     // the tabBox that this is attached to.
25999     tab : false,
26000      
26001     getAutoCreate : function() 
26002     {
26003         var cfg = {
26004             tag: 'div',
26005             cls: 'tab-pane'
26006         };
26007         
26008         if(this.active){
26009             cfg.cls += ' active';
26010         }
26011         
26012         return cfg;
26013     },
26014     initEvents  : function()
26015     {
26016         //Roo.log('trigger add pane handler');
26017         this.parent().fireEvent('addpane', this)
26018     },
26019     
26020      /**
26021      * Updates the tab title 
26022      * @param {String} html to set the title to.
26023      */
26024     setTitle: function(str)
26025     {
26026         if (!this.tab) {
26027             return;
26028         }
26029         this.title = str;
26030         this.tab.select('a', true).first().dom.innerHTML = str;
26031         
26032     }
26033     
26034     
26035     
26036 });
26037
26038  
26039
26040
26041  /*
26042  * - LGPL
26043  *
26044  * menu
26045  * 
26046  */
26047 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26048
26049 /**
26050  * @class Roo.bootstrap.menu.Menu
26051  * @extends Roo.bootstrap.Component
26052  * Bootstrap Menu class - container for Menu
26053  * @cfg {String} html Text of the menu
26054  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
26055  * @cfg {String} icon Font awesome icon
26056  * @cfg {String} pos Menu align to (top | bottom) default bottom
26057  * 
26058  * 
26059  * @constructor
26060  * Create a new Menu
26061  * @param {Object} config The config object
26062  */
26063
26064
26065 Roo.bootstrap.menu.Menu = function(config){
26066     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
26067     
26068     this.addEvents({
26069         /**
26070          * @event beforeshow
26071          * Fires before this menu is displayed
26072          * @param {Roo.bootstrap.menu.Menu} this
26073          */
26074         beforeshow : true,
26075         /**
26076          * @event beforehide
26077          * Fires before this menu is hidden
26078          * @param {Roo.bootstrap.menu.Menu} this
26079          */
26080         beforehide : true,
26081         /**
26082          * @event show
26083          * Fires after this menu is displayed
26084          * @param {Roo.bootstrap.menu.Menu} this
26085          */
26086         show : true,
26087         /**
26088          * @event hide
26089          * Fires after this menu is hidden
26090          * @param {Roo.bootstrap.menu.Menu} this
26091          */
26092         hide : true,
26093         /**
26094          * @event click
26095          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
26096          * @param {Roo.bootstrap.menu.Menu} this
26097          * @param {Roo.EventObject} e
26098          */
26099         click : true
26100     });
26101     
26102 };
26103
26104 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
26105     
26106     submenu : false,
26107     html : '',
26108     weight : 'default',
26109     icon : false,
26110     pos : 'bottom',
26111     
26112     
26113     getChildContainer : function() {
26114         if(this.isSubMenu){
26115             return this.el;
26116         }
26117         
26118         return this.el.select('ul.dropdown-menu', true).first();  
26119     },
26120     
26121     getAutoCreate : function()
26122     {
26123         var text = [
26124             {
26125                 tag : 'span',
26126                 cls : 'roo-menu-text',
26127                 html : this.html
26128             }
26129         ];
26130         
26131         if(this.icon){
26132             text.unshift({
26133                 tag : 'i',
26134                 cls : 'fa ' + this.icon
26135             })
26136         }
26137         
26138         
26139         var cfg = {
26140             tag : 'div',
26141             cls : 'btn-group',
26142             cn : [
26143                 {
26144                     tag : 'button',
26145                     cls : 'dropdown-button btn btn-' + this.weight,
26146                     cn : text
26147                 },
26148                 {
26149                     tag : 'button',
26150                     cls : 'dropdown-toggle btn btn-' + this.weight,
26151                     cn : [
26152                         {
26153                             tag : 'span',
26154                             cls : 'caret'
26155                         }
26156                     ]
26157                 },
26158                 {
26159                     tag : 'ul',
26160                     cls : 'dropdown-menu'
26161                 }
26162             ]
26163             
26164         };
26165         
26166         if(this.pos == 'top'){
26167             cfg.cls += ' dropup';
26168         }
26169         
26170         if(this.isSubMenu){
26171             cfg = {
26172                 tag : 'ul',
26173                 cls : 'dropdown-menu'
26174             }
26175         }
26176         
26177         return cfg;
26178     },
26179     
26180     onRender : function(ct, position)
26181     {
26182         this.isSubMenu = ct.hasClass('dropdown-submenu');
26183         
26184         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
26185     },
26186     
26187     initEvents : function() 
26188     {
26189         if(this.isSubMenu){
26190             return;
26191         }
26192         
26193         this.hidden = true;
26194         
26195         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
26196         this.triggerEl.on('click', this.onTriggerPress, this);
26197         
26198         this.buttonEl = this.el.select('button.dropdown-button', true).first();
26199         this.buttonEl.on('click', this.onClick, this);
26200         
26201     },
26202     
26203     list : function()
26204     {
26205         if(this.isSubMenu){
26206             return this.el;
26207         }
26208         
26209         return this.el.select('ul.dropdown-menu', true).first();
26210     },
26211     
26212     onClick : function(e)
26213     {
26214         this.fireEvent("click", this, e);
26215     },
26216     
26217     onTriggerPress  : function(e)
26218     {   
26219         if (this.isVisible()) {
26220             this.hide();
26221         } else {
26222             this.show();
26223         }
26224     },
26225     
26226     isVisible : function(){
26227         return !this.hidden;
26228     },
26229     
26230     show : function()
26231     {
26232         this.fireEvent("beforeshow", this);
26233         
26234         this.hidden = false;
26235         this.el.addClass('open');
26236         
26237         Roo.get(document).on("mouseup", this.onMouseUp, this);
26238         
26239         this.fireEvent("show", this);
26240         
26241         
26242     },
26243     
26244     hide : function()
26245     {
26246         this.fireEvent("beforehide", this);
26247         
26248         this.hidden = true;
26249         this.el.removeClass('open');
26250         
26251         Roo.get(document).un("mouseup", this.onMouseUp);
26252         
26253         this.fireEvent("hide", this);
26254     },
26255     
26256     onMouseUp : function()
26257     {
26258         this.hide();
26259     }
26260     
26261 });
26262
26263  
26264  /*
26265  * - LGPL
26266  *
26267  * menu item
26268  * 
26269  */
26270 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26271
26272 /**
26273  * @class Roo.bootstrap.menu.Item
26274  * @extends Roo.bootstrap.Component
26275  * Bootstrap MenuItem class
26276  * @cfg {Boolean} submenu (true | false) default false
26277  * @cfg {String} html text of the item
26278  * @cfg {String} href the link
26279  * @cfg {Boolean} disable (true | false) default false
26280  * @cfg {Boolean} preventDefault (true | false) default true
26281  * @cfg {String} icon Font awesome icon
26282  * @cfg {String} pos Submenu align to (left | right) default right 
26283  * 
26284  * 
26285  * @constructor
26286  * Create a new Item
26287  * @param {Object} config The config object
26288  */
26289
26290
26291 Roo.bootstrap.menu.Item = function(config){
26292     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26293     this.addEvents({
26294         /**
26295          * @event mouseover
26296          * Fires when the mouse is hovering over this menu
26297          * @param {Roo.bootstrap.menu.Item} this
26298          * @param {Roo.EventObject} e
26299          */
26300         mouseover : true,
26301         /**
26302          * @event mouseout
26303          * Fires when the mouse exits this menu
26304          * @param {Roo.bootstrap.menu.Item} this
26305          * @param {Roo.EventObject} e
26306          */
26307         mouseout : true,
26308         // raw events
26309         /**
26310          * @event click
26311          * The raw click event for the entire grid.
26312          * @param {Roo.EventObject} e
26313          */
26314         click : true
26315     });
26316 };
26317
26318 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26319     
26320     submenu : false,
26321     href : '',
26322     html : '',
26323     preventDefault: true,
26324     disable : false,
26325     icon : false,
26326     pos : 'right',
26327     
26328     getAutoCreate : function()
26329     {
26330         var text = [
26331             {
26332                 tag : 'span',
26333                 cls : 'roo-menu-item-text',
26334                 html : this.html
26335             }
26336         ];
26337         
26338         if(this.icon){
26339             text.unshift({
26340                 tag : 'i',
26341                 cls : 'fa ' + this.icon
26342             })
26343         }
26344         
26345         var cfg = {
26346             tag : 'li',
26347             cn : [
26348                 {
26349                     tag : 'a',
26350                     href : this.href || '#',
26351                     cn : text
26352                 }
26353             ]
26354         };
26355         
26356         if(this.disable){
26357             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26358         }
26359         
26360         if(this.submenu){
26361             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26362             
26363             if(this.pos == 'left'){
26364                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26365             }
26366         }
26367         
26368         return cfg;
26369     },
26370     
26371     initEvents : function() 
26372     {
26373         this.el.on('mouseover', this.onMouseOver, this);
26374         this.el.on('mouseout', this.onMouseOut, this);
26375         
26376         this.el.select('a', true).first().on('click', this.onClick, this);
26377         
26378     },
26379     
26380     onClick : function(e)
26381     {
26382         if(this.preventDefault){
26383             e.preventDefault();
26384         }
26385         
26386         this.fireEvent("click", this, e);
26387     },
26388     
26389     onMouseOver : function(e)
26390     {
26391         if(this.submenu && this.pos == 'left'){
26392             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26393         }
26394         
26395         this.fireEvent("mouseover", this, e);
26396     },
26397     
26398     onMouseOut : function(e)
26399     {
26400         this.fireEvent("mouseout", this, e);
26401     }
26402 });
26403
26404  
26405
26406  /*
26407  * - LGPL
26408  *
26409  * menu separator
26410  * 
26411  */
26412 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26413
26414 /**
26415  * @class Roo.bootstrap.menu.Separator
26416  * @extends Roo.bootstrap.Component
26417  * Bootstrap Separator class
26418  * 
26419  * @constructor
26420  * Create a new Separator
26421  * @param {Object} config The config object
26422  */
26423
26424
26425 Roo.bootstrap.menu.Separator = function(config){
26426     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26427 };
26428
26429 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26430     
26431     getAutoCreate : function(){
26432         var cfg = {
26433             tag : 'li',
26434             cls: 'divider'
26435         };
26436         
26437         return cfg;
26438     }
26439    
26440 });
26441
26442  
26443
26444  /*
26445  * - LGPL
26446  *
26447  * Tooltip
26448  * 
26449  */
26450
26451 /**
26452  * @class Roo.bootstrap.Tooltip
26453  * Bootstrap Tooltip class
26454  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26455  * to determine which dom element triggers the tooltip.
26456  * 
26457  * It needs to add support for additional attributes like tooltip-position
26458  * 
26459  * @constructor
26460  * Create a new Toolti
26461  * @param {Object} config The config object
26462  */
26463
26464 Roo.bootstrap.Tooltip = function(config){
26465     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26466     
26467     this.alignment = Roo.bootstrap.Tooltip.alignment;
26468     
26469     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26470         this.alignment = config.alignment;
26471     }
26472     
26473 };
26474
26475 Roo.apply(Roo.bootstrap.Tooltip, {
26476     /**
26477      * @function init initialize tooltip monitoring.
26478      * @static
26479      */
26480     currentEl : false,
26481     currentTip : false,
26482     currentRegion : false,
26483     
26484     //  init : delay?
26485     
26486     init : function()
26487     {
26488         Roo.get(document).on('mouseover', this.enter ,this);
26489         Roo.get(document).on('mouseout', this.leave, this);
26490          
26491         
26492         this.currentTip = new Roo.bootstrap.Tooltip();
26493     },
26494     
26495     enter : function(ev)
26496     {
26497         var dom = ev.getTarget();
26498         
26499         //Roo.log(['enter',dom]);
26500         var el = Roo.fly(dom);
26501         if (this.currentEl) {
26502             //Roo.log(dom);
26503             //Roo.log(this.currentEl);
26504             //Roo.log(this.currentEl.contains(dom));
26505             if (this.currentEl == el) {
26506                 return;
26507             }
26508             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26509                 return;
26510             }
26511
26512         }
26513         
26514         if (this.currentTip.el) {
26515             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26516         }    
26517         //Roo.log(ev);
26518         
26519         if(!el || el.dom == document){
26520             return;
26521         }
26522         
26523         var bindEl = el;
26524         
26525         // you can not look for children, as if el is the body.. then everythign is the child..
26526         if (!el.attr('tooltip')) { //
26527             if (!el.select("[tooltip]").elements.length) {
26528                 return;
26529             }
26530             // is the mouse over this child...?
26531             bindEl = el.select("[tooltip]").first();
26532             var xy = ev.getXY();
26533             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26534                 //Roo.log("not in region.");
26535                 return;
26536             }
26537             //Roo.log("child element over..");
26538             
26539         }
26540         this.currentEl = bindEl;
26541         this.currentTip.bind(bindEl);
26542         this.currentRegion = Roo.lib.Region.getRegion(dom);
26543         this.currentTip.enter();
26544         
26545     },
26546     leave : function(ev)
26547     {
26548         var dom = ev.getTarget();
26549         //Roo.log(['leave',dom]);
26550         if (!this.currentEl) {
26551             return;
26552         }
26553         
26554         
26555         if (dom != this.currentEl.dom) {
26556             return;
26557         }
26558         var xy = ev.getXY();
26559         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26560             return;
26561         }
26562         // only activate leave if mouse cursor is outside... bounding box..
26563         
26564         
26565         
26566         
26567         if (this.currentTip) {
26568             this.currentTip.leave();
26569         }
26570         //Roo.log('clear currentEl');
26571         this.currentEl = false;
26572         
26573         
26574     },
26575     alignment : {
26576         'left' : ['r-l', [-2,0], 'right'],
26577         'right' : ['l-r', [2,0], 'left'],
26578         'bottom' : ['t-b', [0,2], 'top'],
26579         'top' : [ 'b-t', [0,-2], 'bottom']
26580     }
26581     
26582 });
26583
26584
26585 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26586     
26587     
26588     bindEl : false,
26589     
26590     delay : null, // can be { show : 300 , hide: 500}
26591     
26592     timeout : null,
26593     
26594     hoverState : null, //???
26595     
26596     placement : 'bottom', 
26597     
26598     alignment : false,
26599     
26600     getAutoCreate : function(){
26601     
26602         var cfg = {
26603            cls : 'tooltip',
26604            role : 'tooltip',
26605            cn : [
26606                 {
26607                     cls : 'tooltip-arrow'
26608                 },
26609                 {
26610                     cls : 'tooltip-inner'
26611                 }
26612            ]
26613         };
26614         
26615         return cfg;
26616     },
26617     bind : function(el)
26618     {
26619         this.bindEl = el;
26620     },
26621       
26622     
26623     enter : function () {
26624        
26625         if (this.timeout != null) {
26626             clearTimeout(this.timeout);
26627         }
26628         
26629         this.hoverState = 'in';
26630          //Roo.log("enter - show");
26631         if (!this.delay || !this.delay.show) {
26632             this.show();
26633             return;
26634         }
26635         var _t = this;
26636         this.timeout = setTimeout(function () {
26637             if (_t.hoverState == 'in') {
26638                 _t.show();
26639             }
26640         }, this.delay.show);
26641     },
26642     leave : function()
26643     {
26644         clearTimeout(this.timeout);
26645     
26646         this.hoverState = 'out';
26647          if (!this.delay || !this.delay.hide) {
26648             this.hide();
26649             return;
26650         }
26651        
26652         var _t = this;
26653         this.timeout = setTimeout(function () {
26654             //Roo.log("leave - timeout");
26655             
26656             if (_t.hoverState == 'out') {
26657                 _t.hide();
26658                 Roo.bootstrap.Tooltip.currentEl = false;
26659             }
26660         }, delay);
26661     },
26662     
26663     show : function (msg)
26664     {
26665         if (!this.el) {
26666             this.render(document.body);
26667         }
26668         // set content.
26669         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26670         
26671         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26672         
26673         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26674         
26675         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26676         
26677         var placement = typeof this.placement == 'function' ?
26678             this.placement.call(this, this.el, on_el) :
26679             this.placement;
26680             
26681         var autoToken = /\s?auto?\s?/i;
26682         var autoPlace = autoToken.test(placement);
26683         if (autoPlace) {
26684             placement = placement.replace(autoToken, '') || 'top';
26685         }
26686         
26687         //this.el.detach()
26688         //this.el.setXY([0,0]);
26689         this.el.show();
26690         //this.el.dom.style.display='block';
26691         
26692         //this.el.appendTo(on_el);
26693         
26694         var p = this.getPosition();
26695         var box = this.el.getBox();
26696         
26697         if (autoPlace) {
26698             // fixme..
26699         }
26700         
26701         var align = this.alignment[placement];
26702         
26703         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26704         
26705         if(placement == 'top' || placement == 'bottom'){
26706             if(xy[0] < 0){
26707                 placement = 'right';
26708             }
26709             
26710             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26711                 placement = 'left';
26712             }
26713             
26714             var scroll = Roo.select('body', true).first().getScroll();
26715             
26716             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26717                 placement = 'top';
26718             }
26719             
26720             align = this.alignment[placement];
26721         }
26722         
26723         this.el.alignTo(this.bindEl, align[0],align[1]);
26724         //var arrow = this.el.select('.arrow',true).first();
26725         //arrow.set(align[2], 
26726         
26727         this.el.addClass(placement);
26728         
26729         this.el.addClass('in fade');
26730         
26731         this.hoverState = null;
26732         
26733         if (this.el.hasClass('fade')) {
26734             // fade it?
26735         }
26736         
26737     },
26738     hide : function()
26739     {
26740          
26741         if (!this.el) {
26742             return;
26743         }
26744         //this.el.setXY([0,0]);
26745         this.el.removeClass('in');
26746         //this.el.hide();
26747         
26748     }
26749     
26750 });
26751  
26752
26753  /*
26754  * - LGPL
26755  *
26756  * Location Picker
26757  * 
26758  */
26759
26760 /**
26761  * @class Roo.bootstrap.LocationPicker
26762  * @extends Roo.bootstrap.Component
26763  * Bootstrap LocationPicker class
26764  * @cfg {Number} latitude Position when init default 0
26765  * @cfg {Number} longitude Position when init default 0
26766  * @cfg {Number} zoom default 15
26767  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26768  * @cfg {Boolean} mapTypeControl default false
26769  * @cfg {Boolean} disableDoubleClickZoom default false
26770  * @cfg {Boolean} scrollwheel default true
26771  * @cfg {Boolean} streetViewControl default false
26772  * @cfg {Number} radius default 0
26773  * @cfg {String} locationName
26774  * @cfg {Boolean} draggable default true
26775  * @cfg {Boolean} enableAutocomplete default false
26776  * @cfg {Boolean} enableReverseGeocode default true
26777  * @cfg {String} markerTitle
26778  * 
26779  * @constructor
26780  * Create a new LocationPicker
26781  * @param {Object} config The config object
26782  */
26783
26784
26785 Roo.bootstrap.LocationPicker = function(config){
26786     
26787     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26788     
26789     this.addEvents({
26790         /**
26791          * @event initial
26792          * Fires when the picker initialized.
26793          * @param {Roo.bootstrap.LocationPicker} this
26794          * @param {Google Location} location
26795          */
26796         initial : true,
26797         /**
26798          * @event positionchanged
26799          * Fires when the picker position changed.
26800          * @param {Roo.bootstrap.LocationPicker} this
26801          * @param {Google Location} location
26802          */
26803         positionchanged : true,
26804         /**
26805          * @event resize
26806          * Fires when the map resize.
26807          * @param {Roo.bootstrap.LocationPicker} this
26808          */
26809         resize : true,
26810         /**
26811          * @event show
26812          * Fires when the map show.
26813          * @param {Roo.bootstrap.LocationPicker} this
26814          */
26815         show : true,
26816         /**
26817          * @event hide
26818          * Fires when the map hide.
26819          * @param {Roo.bootstrap.LocationPicker} this
26820          */
26821         hide : true,
26822         /**
26823          * @event mapClick
26824          * Fires when click the map.
26825          * @param {Roo.bootstrap.LocationPicker} this
26826          * @param {Map event} e
26827          */
26828         mapClick : true,
26829         /**
26830          * @event mapRightClick
26831          * Fires when right click the map.
26832          * @param {Roo.bootstrap.LocationPicker} this
26833          * @param {Map event} e
26834          */
26835         mapRightClick : true,
26836         /**
26837          * @event markerClick
26838          * Fires when click the marker.
26839          * @param {Roo.bootstrap.LocationPicker} this
26840          * @param {Map event} e
26841          */
26842         markerClick : true,
26843         /**
26844          * @event markerRightClick
26845          * Fires when right click the marker.
26846          * @param {Roo.bootstrap.LocationPicker} this
26847          * @param {Map event} e
26848          */
26849         markerRightClick : true,
26850         /**
26851          * @event OverlayViewDraw
26852          * Fires when OverlayView Draw
26853          * @param {Roo.bootstrap.LocationPicker} this
26854          */
26855         OverlayViewDraw : true,
26856         /**
26857          * @event OverlayViewOnAdd
26858          * Fires when OverlayView Draw
26859          * @param {Roo.bootstrap.LocationPicker} this
26860          */
26861         OverlayViewOnAdd : true,
26862         /**
26863          * @event OverlayViewOnRemove
26864          * Fires when OverlayView Draw
26865          * @param {Roo.bootstrap.LocationPicker} this
26866          */
26867         OverlayViewOnRemove : true,
26868         /**
26869          * @event OverlayViewShow
26870          * Fires when OverlayView Draw
26871          * @param {Roo.bootstrap.LocationPicker} this
26872          * @param {Pixel} cpx
26873          */
26874         OverlayViewShow : true,
26875         /**
26876          * @event OverlayViewHide
26877          * Fires when OverlayView Draw
26878          * @param {Roo.bootstrap.LocationPicker} this
26879          */
26880         OverlayViewHide : true,
26881         /**
26882          * @event loadexception
26883          * Fires when load google lib failed.
26884          * @param {Roo.bootstrap.LocationPicker} this
26885          */
26886         loadexception : true
26887     });
26888         
26889 };
26890
26891 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26892     
26893     gMapContext: false,
26894     
26895     latitude: 0,
26896     longitude: 0,
26897     zoom: 15,
26898     mapTypeId: false,
26899     mapTypeControl: false,
26900     disableDoubleClickZoom: false,
26901     scrollwheel: true,
26902     streetViewControl: false,
26903     radius: 0,
26904     locationName: '',
26905     draggable: true,
26906     enableAutocomplete: false,
26907     enableReverseGeocode: true,
26908     markerTitle: '',
26909     
26910     getAutoCreate: function()
26911     {
26912
26913         var cfg = {
26914             tag: 'div',
26915             cls: 'roo-location-picker'
26916         };
26917         
26918         return cfg
26919     },
26920     
26921     initEvents: function(ct, position)
26922     {       
26923         if(!this.el.getWidth() || this.isApplied()){
26924             return;
26925         }
26926         
26927         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26928         
26929         this.initial();
26930     },
26931     
26932     initial: function()
26933     {
26934         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26935             this.fireEvent('loadexception', this);
26936             return;
26937         }
26938         
26939         if(!this.mapTypeId){
26940             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26941         }
26942         
26943         this.gMapContext = this.GMapContext();
26944         
26945         this.initOverlayView();
26946         
26947         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26948         
26949         var _this = this;
26950                 
26951         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26952             _this.setPosition(_this.gMapContext.marker.position);
26953         });
26954         
26955         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26956             _this.fireEvent('mapClick', this, event);
26957             
26958         });
26959
26960         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26961             _this.fireEvent('mapRightClick', this, event);
26962             
26963         });
26964         
26965         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26966             _this.fireEvent('markerClick', this, event);
26967             
26968         });
26969
26970         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26971             _this.fireEvent('markerRightClick', this, event);
26972             
26973         });
26974         
26975         this.setPosition(this.gMapContext.location);
26976         
26977         this.fireEvent('initial', this, this.gMapContext.location);
26978     },
26979     
26980     initOverlayView: function()
26981     {
26982         var _this = this;
26983         
26984         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26985             
26986             draw: function()
26987             {
26988                 _this.fireEvent('OverlayViewDraw', _this);
26989             },
26990             
26991             onAdd: function()
26992             {
26993                 _this.fireEvent('OverlayViewOnAdd', _this);
26994             },
26995             
26996             onRemove: function()
26997             {
26998                 _this.fireEvent('OverlayViewOnRemove', _this);
26999             },
27000             
27001             show: function(cpx)
27002             {
27003                 _this.fireEvent('OverlayViewShow', _this, cpx);
27004             },
27005             
27006             hide: function()
27007             {
27008                 _this.fireEvent('OverlayViewHide', _this);
27009             }
27010             
27011         });
27012     },
27013     
27014     fromLatLngToContainerPixel: function(event)
27015     {
27016         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
27017     },
27018     
27019     isApplied: function() 
27020     {
27021         return this.getGmapContext() == false ? false : true;
27022     },
27023     
27024     getGmapContext: function() 
27025     {
27026         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
27027     },
27028     
27029     GMapContext: function() 
27030     {
27031         var position = new google.maps.LatLng(this.latitude, this.longitude);
27032         
27033         var _map = new google.maps.Map(this.el.dom, {
27034             center: position,
27035             zoom: this.zoom,
27036             mapTypeId: this.mapTypeId,
27037             mapTypeControl: this.mapTypeControl,
27038             disableDoubleClickZoom: this.disableDoubleClickZoom,
27039             scrollwheel: this.scrollwheel,
27040             streetViewControl: this.streetViewControl,
27041             locationName: this.locationName,
27042             draggable: this.draggable,
27043             enableAutocomplete: this.enableAutocomplete,
27044             enableReverseGeocode: this.enableReverseGeocode
27045         });
27046         
27047         var _marker = new google.maps.Marker({
27048             position: position,
27049             map: _map,
27050             title: this.markerTitle,
27051             draggable: this.draggable
27052         });
27053         
27054         return {
27055             map: _map,
27056             marker: _marker,
27057             circle: null,
27058             location: position,
27059             radius: this.radius,
27060             locationName: this.locationName,
27061             addressComponents: {
27062                 formatted_address: null,
27063                 addressLine1: null,
27064                 addressLine2: null,
27065                 streetName: null,
27066                 streetNumber: null,
27067                 city: null,
27068                 district: null,
27069                 state: null,
27070                 stateOrProvince: null
27071             },
27072             settings: this,
27073             domContainer: this.el.dom,
27074             geodecoder: new google.maps.Geocoder()
27075         };
27076     },
27077     
27078     drawCircle: function(center, radius, options) 
27079     {
27080         if (this.gMapContext.circle != null) {
27081             this.gMapContext.circle.setMap(null);
27082         }
27083         if (radius > 0) {
27084             radius *= 1;
27085             options = Roo.apply({}, options, {
27086                 strokeColor: "#0000FF",
27087                 strokeOpacity: .35,
27088                 strokeWeight: 2,
27089                 fillColor: "#0000FF",
27090                 fillOpacity: .2
27091             });
27092             
27093             options.map = this.gMapContext.map;
27094             options.radius = radius;
27095             options.center = center;
27096             this.gMapContext.circle = new google.maps.Circle(options);
27097             return this.gMapContext.circle;
27098         }
27099         
27100         return null;
27101     },
27102     
27103     setPosition: function(location) 
27104     {
27105         this.gMapContext.location = location;
27106         this.gMapContext.marker.setPosition(location);
27107         this.gMapContext.map.panTo(location);
27108         this.drawCircle(location, this.gMapContext.radius, {});
27109         
27110         var _this = this;
27111         
27112         if (this.gMapContext.settings.enableReverseGeocode) {
27113             this.gMapContext.geodecoder.geocode({
27114                 latLng: this.gMapContext.location
27115             }, function(results, status) {
27116                 
27117                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
27118                     _this.gMapContext.locationName = results[0].formatted_address;
27119                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
27120                     
27121                     _this.fireEvent('positionchanged', this, location);
27122                 }
27123             });
27124             
27125             return;
27126         }
27127         
27128         this.fireEvent('positionchanged', this, location);
27129     },
27130     
27131     resize: function()
27132     {
27133         google.maps.event.trigger(this.gMapContext.map, "resize");
27134         
27135         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
27136         
27137         this.fireEvent('resize', this);
27138     },
27139     
27140     setPositionByLatLng: function(latitude, longitude)
27141     {
27142         this.setPosition(new google.maps.LatLng(latitude, longitude));
27143     },
27144     
27145     getCurrentPosition: function() 
27146     {
27147         return {
27148             latitude: this.gMapContext.location.lat(),
27149             longitude: this.gMapContext.location.lng()
27150         };
27151     },
27152     
27153     getAddressName: function() 
27154     {
27155         return this.gMapContext.locationName;
27156     },
27157     
27158     getAddressComponents: function() 
27159     {
27160         return this.gMapContext.addressComponents;
27161     },
27162     
27163     address_component_from_google_geocode: function(address_components) 
27164     {
27165         var result = {};
27166         
27167         for (var i = 0; i < address_components.length; i++) {
27168             var component = address_components[i];
27169             if (component.types.indexOf("postal_code") >= 0) {
27170                 result.postalCode = component.short_name;
27171             } else if (component.types.indexOf("street_number") >= 0) {
27172                 result.streetNumber = component.short_name;
27173             } else if (component.types.indexOf("route") >= 0) {
27174                 result.streetName = component.short_name;
27175             } else if (component.types.indexOf("neighborhood") >= 0) {
27176                 result.city = component.short_name;
27177             } else if (component.types.indexOf("locality") >= 0) {
27178                 result.city = component.short_name;
27179             } else if (component.types.indexOf("sublocality") >= 0) {
27180                 result.district = component.short_name;
27181             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
27182                 result.stateOrProvince = component.short_name;
27183             } else if (component.types.indexOf("country") >= 0) {
27184                 result.country = component.short_name;
27185             }
27186         }
27187         
27188         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
27189         result.addressLine2 = "";
27190         return result;
27191     },
27192     
27193     setZoomLevel: function(zoom)
27194     {
27195         this.gMapContext.map.setZoom(zoom);
27196     },
27197     
27198     show: function()
27199     {
27200         if(!this.el){
27201             return;
27202         }
27203         
27204         this.el.show();
27205         
27206         this.resize();
27207         
27208         this.fireEvent('show', this);
27209     },
27210     
27211     hide: function()
27212     {
27213         if(!this.el){
27214             return;
27215         }
27216         
27217         this.el.hide();
27218         
27219         this.fireEvent('hide', this);
27220     }
27221     
27222 });
27223
27224 Roo.apply(Roo.bootstrap.LocationPicker, {
27225     
27226     OverlayView : function(map, options)
27227     {
27228         options = options || {};
27229         
27230         this.setMap(map);
27231     }
27232     
27233     
27234 });/**
27235  * @class Roo.bootstrap.Alert
27236  * @extends Roo.bootstrap.Component
27237  * Bootstrap Alert class - shows an alert area box
27238  * eg
27239  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
27240   Enter a valid email address
27241 </div>
27242  * @licence LGPL
27243  * @cfg {String} title The title of alert
27244  * @cfg {String} html The content of alert
27245  * @cfg {String} weight (  success | info | warning | danger )
27246  * @cfg {String} faicon font-awesomeicon
27247  * 
27248  * @constructor
27249  * Create a new alert
27250  * @param {Object} config The config object
27251  */
27252
27253
27254 Roo.bootstrap.Alert = function(config){
27255     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27256     
27257 };
27258
27259 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
27260     
27261     title: '',
27262     html: '',
27263     weight: false,
27264     faicon: false,
27265     
27266     getAutoCreate : function()
27267     {
27268         
27269         var cfg = {
27270             tag : 'div',
27271             cls : 'alert',
27272             cn : [
27273                 {
27274                     tag : 'i',
27275                     cls : 'roo-alert-icon'
27276                     
27277                 },
27278                 {
27279                     tag : 'b',
27280                     cls : 'roo-alert-title',
27281                     html : this.title
27282                 },
27283                 {
27284                     tag : 'span',
27285                     cls : 'roo-alert-text',
27286                     html : this.html
27287                 }
27288             ]
27289         };
27290         
27291         if(this.faicon){
27292             cfg.cn[0].cls += ' fa ' + this.faicon;
27293         }
27294         
27295         if(this.weight){
27296             cfg.cls += ' alert-' + this.weight;
27297         }
27298         
27299         return cfg;
27300     },
27301     
27302     initEvents: function() 
27303     {
27304         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27305     },
27306     
27307     setTitle : function(str)
27308     {
27309         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27310     },
27311     
27312     setText : function(str)
27313     {
27314         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27315     },
27316     
27317     setWeight : function(weight)
27318     {
27319         if(this.weight){
27320             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27321         }
27322         
27323         this.weight = weight;
27324         
27325         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27326     },
27327     
27328     setIcon : function(icon)
27329     {
27330         if(this.faicon){
27331             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27332         }
27333         
27334         this.faicon = icon;
27335         
27336         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27337     },
27338     
27339     hide: function() 
27340     {
27341         this.el.hide();   
27342     },
27343     
27344     show: function() 
27345     {  
27346         this.el.show();   
27347     }
27348     
27349 });
27350
27351  
27352 /*
27353 * Licence: LGPL
27354 */
27355
27356 /**
27357  * @class Roo.bootstrap.UploadCropbox
27358  * @extends Roo.bootstrap.Component
27359  * Bootstrap UploadCropbox class
27360  * @cfg {String} emptyText show when image has been loaded
27361  * @cfg {String} rotateNotify show when image too small to rotate
27362  * @cfg {Number} errorTimeout default 3000
27363  * @cfg {Number} minWidth default 300
27364  * @cfg {Number} minHeight default 300
27365  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27366  * @cfg {Boolean} isDocument (true|false) default false
27367  * @cfg {String} url action url
27368  * @cfg {String} paramName default 'imageUpload'
27369  * @cfg {String} method default POST
27370  * @cfg {Boolean} loadMask (true|false) default true
27371  * @cfg {Boolean} loadingText default 'Loading...'
27372  * 
27373  * @constructor
27374  * Create a new UploadCropbox
27375  * @param {Object} config The config object
27376  */
27377
27378 Roo.bootstrap.UploadCropbox = function(config){
27379     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27380     
27381     this.addEvents({
27382         /**
27383          * @event beforeselectfile
27384          * Fire before select file
27385          * @param {Roo.bootstrap.UploadCropbox} this
27386          */
27387         "beforeselectfile" : true,
27388         /**
27389          * @event initial
27390          * Fire after initEvent
27391          * @param {Roo.bootstrap.UploadCropbox} this
27392          */
27393         "initial" : true,
27394         /**
27395          * @event crop
27396          * Fire after initEvent
27397          * @param {Roo.bootstrap.UploadCropbox} this
27398          * @param {String} data
27399          */
27400         "crop" : true,
27401         /**
27402          * @event prepare
27403          * Fire when preparing the file data
27404          * @param {Roo.bootstrap.UploadCropbox} this
27405          * @param {Object} file
27406          */
27407         "prepare" : true,
27408         /**
27409          * @event exception
27410          * Fire when get exception
27411          * @param {Roo.bootstrap.UploadCropbox} this
27412          * @param {XMLHttpRequest} xhr
27413          */
27414         "exception" : true,
27415         /**
27416          * @event beforeloadcanvas
27417          * Fire before load the canvas
27418          * @param {Roo.bootstrap.UploadCropbox} this
27419          * @param {String} src
27420          */
27421         "beforeloadcanvas" : true,
27422         /**
27423          * @event trash
27424          * Fire when trash image
27425          * @param {Roo.bootstrap.UploadCropbox} this
27426          */
27427         "trash" : true,
27428         /**
27429          * @event download
27430          * Fire when download the image
27431          * @param {Roo.bootstrap.UploadCropbox} this
27432          */
27433         "download" : true,
27434         /**
27435          * @event footerbuttonclick
27436          * Fire when footerbuttonclick
27437          * @param {Roo.bootstrap.UploadCropbox} this
27438          * @param {String} type
27439          */
27440         "footerbuttonclick" : true,
27441         /**
27442          * @event resize
27443          * Fire when resize
27444          * @param {Roo.bootstrap.UploadCropbox} this
27445          */
27446         "resize" : true,
27447         /**
27448          * @event rotate
27449          * Fire when rotate the image
27450          * @param {Roo.bootstrap.UploadCropbox} this
27451          * @param {String} pos
27452          */
27453         "rotate" : true,
27454         /**
27455          * @event inspect
27456          * Fire when inspect the file
27457          * @param {Roo.bootstrap.UploadCropbox} this
27458          * @param {Object} file
27459          */
27460         "inspect" : true,
27461         /**
27462          * @event upload
27463          * Fire when xhr upload the file
27464          * @param {Roo.bootstrap.UploadCropbox} this
27465          * @param {Object} data
27466          */
27467         "upload" : true,
27468         /**
27469          * @event arrange
27470          * Fire when arrange the file data
27471          * @param {Roo.bootstrap.UploadCropbox} this
27472          * @param {Object} formData
27473          */
27474         "arrange" : true
27475     });
27476     
27477     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27478 };
27479
27480 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27481     
27482     emptyText : 'Click to upload image',
27483     rotateNotify : 'Image is too small to rotate',
27484     errorTimeout : 3000,
27485     scale : 0,
27486     baseScale : 1,
27487     rotate : 0,
27488     dragable : false,
27489     pinching : false,
27490     mouseX : 0,
27491     mouseY : 0,
27492     cropData : false,
27493     minWidth : 300,
27494     minHeight : 300,
27495     file : false,
27496     exif : {},
27497     baseRotate : 1,
27498     cropType : 'image/jpeg',
27499     buttons : false,
27500     canvasLoaded : false,
27501     isDocument : false,
27502     method : 'POST',
27503     paramName : 'imageUpload',
27504     loadMask : true,
27505     loadingText : 'Loading...',
27506     maskEl : false,
27507     
27508     getAutoCreate : function()
27509     {
27510         var cfg = {
27511             tag : 'div',
27512             cls : 'roo-upload-cropbox',
27513             cn : [
27514                 {
27515                     tag : 'input',
27516                     cls : 'roo-upload-cropbox-selector',
27517                     type : 'file'
27518                 },
27519                 {
27520                     tag : 'div',
27521                     cls : 'roo-upload-cropbox-body',
27522                     style : 'cursor:pointer',
27523                     cn : [
27524                         {
27525                             tag : 'div',
27526                             cls : 'roo-upload-cropbox-preview'
27527                         },
27528                         {
27529                             tag : 'div',
27530                             cls : 'roo-upload-cropbox-thumb'
27531                         },
27532                         {
27533                             tag : 'div',
27534                             cls : 'roo-upload-cropbox-empty-notify',
27535                             html : this.emptyText
27536                         },
27537                         {
27538                             tag : 'div',
27539                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27540                             html : this.rotateNotify
27541                         }
27542                     ]
27543                 },
27544                 {
27545                     tag : 'div',
27546                     cls : 'roo-upload-cropbox-footer',
27547                     cn : {
27548                         tag : 'div',
27549                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27550                         cn : []
27551                     }
27552                 }
27553             ]
27554         };
27555         
27556         return cfg;
27557     },
27558     
27559     onRender : function(ct, position)
27560     {
27561         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27562         
27563         if (this.buttons.length) {
27564             
27565             Roo.each(this.buttons, function(bb) {
27566                 
27567                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27568                 
27569                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27570                 
27571             }, this);
27572         }
27573         
27574         if(this.loadMask){
27575             this.maskEl = this.el;
27576         }
27577     },
27578     
27579     initEvents : function()
27580     {
27581         this.urlAPI = (window.createObjectURL && window) || 
27582                                 (window.URL && URL.revokeObjectURL && URL) || 
27583                                 (window.webkitURL && webkitURL);
27584                         
27585         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27586         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27587         
27588         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27589         this.selectorEl.hide();
27590         
27591         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27592         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27593         
27594         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27595         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27596         this.thumbEl.hide();
27597         
27598         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27599         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27600         
27601         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27602         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27603         this.errorEl.hide();
27604         
27605         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27606         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27607         this.footerEl.hide();
27608         
27609         this.setThumbBoxSize();
27610         
27611         this.bind();
27612         
27613         this.resize();
27614         
27615         this.fireEvent('initial', this);
27616     },
27617
27618     bind : function()
27619     {
27620         var _this = this;
27621         
27622         window.addEventListener("resize", function() { _this.resize(); } );
27623         
27624         this.bodyEl.on('click', this.beforeSelectFile, this);
27625         
27626         if(Roo.isTouch){
27627             this.bodyEl.on('touchstart', this.onTouchStart, this);
27628             this.bodyEl.on('touchmove', this.onTouchMove, this);
27629             this.bodyEl.on('touchend', this.onTouchEnd, this);
27630         }
27631         
27632         if(!Roo.isTouch){
27633             this.bodyEl.on('mousedown', this.onMouseDown, this);
27634             this.bodyEl.on('mousemove', this.onMouseMove, this);
27635             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27636             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27637             Roo.get(document).on('mouseup', this.onMouseUp, this);
27638         }
27639         
27640         this.selectorEl.on('change', this.onFileSelected, this);
27641     },
27642     
27643     reset : function()
27644     {    
27645         this.scale = 0;
27646         this.baseScale = 1;
27647         this.rotate = 0;
27648         this.baseRotate = 1;
27649         this.dragable = false;
27650         this.pinching = false;
27651         this.mouseX = 0;
27652         this.mouseY = 0;
27653         this.cropData = false;
27654         this.notifyEl.dom.innerHTML = this.emptyText;
27655         
27656         this.selectorEl.dom.value = '';
27657         
27658     },
27659     
27660     resize : function()
27661     {
27662         if(this.fireEvent('resize', this) != false){
27663             this.setThumbBoxPosition();
27664             this.setCanvasPosition();
27665         }
27666     },
27667     
27668     onFooterButtonClick : function(e, el, o, type)
27669     {
27670         switch (type) {
27671             case 'rotate-left' :
27672                 this.onRotateLeft(e);
27673                 break;
27674             case 'rotate-right' :
27675                 this.onRotateRight(e);
27676                 break;
27677             case 'picture' :
27678                 this.beforeSelectFile(e);
27679                 break;
27680             case 'trash' :
27681                 this.trash(e);
27682                 break;
27683             case 'crop' :
27684                 this.crop(e);
27685                 break;
27686             case 'download' :
27687                 this.download(e);
27688                 break;
27689             default :
27690                 break;
27691         }
27692         
27693         this.fireEvent('footerbuttonclick', this, type);
27694     },
27695     
27696     beforeSelectFile : function(e)
27697     {
27698         e.preventDefault();
27699         
27700         if(this.fireEvent('beforeselectfile', this) != false){
27701             this.selectorEl.dom.click();
27702         }
27703     },
27704     
27705     onFileSelected : function(e)
27706     {
27707         e.preventDefault();
27708         
27709         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27710             return;
27711         }
27712         
27713         var file = this.selectorEl.dom.files[0];
27714         
27715         if(this.fireEvent('inspect', this, file) != false){
27716             this.prepare(file);
27717         }
27718         
27719     },
27720     
27721     trash : function(e)
27722     {
27723         this.fireEvent('trash', this);
27724     },
27725     
27726     download : function(e)
27727     {
27728         this.fireEvent('download', this);
27729     },
27730     
27731     loadCanvas : function(src)
27732     {   
27733         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27734             
27735             this.reset();
27736             
27737             this.imageEl = document.createElement('img');
27738             
27739             var _this = this;
27740             
27741             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27742             
27743             this.imageEl.src = src;
27744         }
27745     },
27746     
27747     onLoadCanvas : function()
27748     {   
27749         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27750         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27751         
27752         this.bodyEl.un('click', this.beforeSelectFile, this);
27753         
27754         this.notifyEl.hide();
27755         this.thumbEl.show();
27756         this.footerEl.show();
27757         
27758         this.baseRotateLevel();
27759         
27760         if(this.isDocument){
27761             this.setThumbBoxSize();
27762         }
27763         
27764         this.setThumbBoxPosition();
27765         
27766         this.baseScaleLevel();
27767         
27768         this.draw();
27769         
27770         this.resize();
27771         
27772         this.canvasLoaded = true;
27773         
27774         if(this.loadMask){
27775             this.maskEl.unmask();
27776         }
27777         
27778     },
27779     
27780     setCanvasPosition : function()
27781     {   
27782         if(!this.canvasEl){
27783             return;
27784         }
27785         
27786         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27787         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27788         
27789         this.previewEl.setLeft(pw);
27790         this.previewEl.setTop(ph);
27791         
27792     },
27793     
27794     onMouseDown : function(e)
27795     {   
27796         e.stopEvent();
27797         
27798         this.dragable = true;
27799         this.pinching = false;
27800         
27801         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27802             this.dragable = false;
27803             return;
27804         }
27805         
27806         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27807         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27808         
27809     },
27810     
27811     onMouseMove : function(e)
27812     {   
27813         e.stopEvent();
27814         
27815         if(!this.canvasLoaded){
27816             return;
27817         }
27818         
27819         if (!this.dragable){
27820             return;
27821         }
27822         
27823         var minX = Math.ceil(this.thumbEl.getLeft(true));
27824         var minY = Math.ceil(this.thumbEl.getTop(true));
27825         
27826         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27827         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27828         
27829         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27830         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27831         
27832         x = x - this.mouseX;
27833         y = y - this.mouseY;
27834         
27835         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27836         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27837         
27838         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27839         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27840         
27841         this.previewEl.setLeft(bgX);
27842         this.previewEl.setTop(bgY);
27843         
27844         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27845         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27846     },
27847     
27848     onMouseUp : function(e)
27849     {   
27850         e.stopEvent();
27851         
27852         this.dragable = false;
27853     },
27854     
27855     onMouseWheel : function(e)
27856     {   
27857         e.stopEvent();
27858         
27859         this.startScale = this.scale;
27860         
27861         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27862         
27863         if(!this.zoomable()){
27864             this.scale = this.startScale;
27865             return;
27866         }
27867         
27868         this.draw();
27869         
27870         return;
27871     },
27872     
27873     zoomable : function()
27874     {
27875         var minScale = this.thumbEl.getWidth() / this.minWidth;
27876         
27877         if(this.minWidth < this.minHeight){
27878             minScale = this.thumbEl.getHeight() / this.minHeight;
27879         }
27880         
27881         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27882         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27883         
27884         if(
27885                 this.isDocument &&
27886                 (this.rotate == 0 || this.rotate == 180) && 
27887                 (
27888                     width > this.imageEl.OriginWidth || 
27889                     height > this.imageEl.OriginHeight ||
27890                     (width < this.minWidth && height < this.minHeight)
27891                 )
27892         ){
27893             return false;
27894         }
27895         
27896         if(
27897                 this.isDocument &&
27898                 (this.rotate == 90 || this.rotate == 270) && 
27899                 (
27900                     width > this.imageEl.OriginWidth || 
27901                     height > this.imageEl.OriginHeight ||
27902                     (width < this.minHeight && height < this.minWidth)
27903                 )
27904         ){
27905             return false;
27906         }
27907         
27908         if(
27909                 !this.isDocument &&
27910                 (this.rotate == 0 || this.rotate == 180) && 
27911                 (
27912                     width < this.minWidth || 
27913                     width > this.imageEl.OriginWidth || 
27914                     height < this.minHeight || 
27915                     height > this.imageEl.OriginHeight
27916                 )
27917         ){
27918             return false;
27919         }
27920         
27921         if(
27922                 !this.isDocument &&
27923                 (this.rotate == 90 || this.rotate == 270) && 
27924                 (
27925                     width < this.minHeight || 
27926                     width > this.imageEl.OriginWidth || 
27927                     height < this.minWidth || 
27928                     height > this.imageEl.OriginHeight
27929                 )
27930         ){
27931             return false;
27932         }
27933         
27934         return true;
27935         
27936     },
27937     
27938     onRotateLeft : function(e)
27939     {   
27940         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27941             
27942             var minScale = this.thumbEl.getWidth() / this.minWidth;
27943             
27944             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27945             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27946             
27947             this.startScale = this.scale;
27948             
27949             while (this.getScaleLevel() < minScale){
27950             
27951                 this.scale = this.scale + 1;
27952                 
27953                 if(!this.zoomable()){
27954                     break;
27955                 }
27956                 
27957                 if(
27958                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27959                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27960                 ){
27961                     continue;
27962                 }
27963                 
27964                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27965
27966                 this.draw();
27967                 
27968                 return;
27969             }
27970             
27971             this.scale = this.startScale;
27972             
27973             this.onRotateFail();
27974             
27975             return false;
27976         }
27977         
27978         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27979
27980         if(this.isDocument){
27981             this.setThumbBoxSize();
27982             this.setThumbBoxPosition();
27983             this.setCanvasPosition();
27984         }
27985         
27986         this.draw();
27987         
27988         this.fireEvent('rotate', this, 'left');
27989         
27990     },
27991     
27992     onRotateRight : function(e)
27993     {
27994         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27995             
27996             var minScale = this.thumbEl.getWidth() / this.minWidth;
27997         
27998             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27999             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
28000             
28001             this.startScale = this.scale;
28002             
28003             while (this.getScaleLevel() < minScale){
28004             
28005                 this.scale = this.scale + 1;
28006                 
28007                 if(!this.zoomable()){
28008                     break;
28009                 }
28010                 
28011                 if(
28012                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28013                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28014                 ){
28015                     continue;
28016                 }
28017                 
28018                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28019
28020                 this.draw();
28021                 
28022                 return;
28023             }
28024             
28025             this.scale = this.startScale;
28026             
28027             this.onRotateFail();
28028             
28029             return false;
28030         }
28031         
28032         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28033
28034         if(this.isDocument){
28035             this.setThumbBoxSize();
28036             this.setThumbBoxPosition();
28037             this.setCanvasPosition();
28038         }
28039         
28040         this.draw();
28041         
28042         this.fireEvent('rotate', this, 'right');
28043     },
28044     
28045     onRotateFail : function()
28046     {
28047         this.errorEl.show(true);
28048         
28049         var _this = this;
28050         
28051         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
28052     },
28053     
28054     draw : function()
28055     {
28056         this.previewEl.dom.innerHTML = '';
28057         
28058         var canvasEl = document.createElement("canvas");
28059         
28060         var contextEl = canvasEl.getContext("2d");
28061         
28062         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28063         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28064         var center = this.imageEl.OriginWidth / 2;
28065         
28066         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
28067             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28068             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28069             center = this.imageEl.OriginHeight / 2;
28070         }
28071         
28072         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
28073         
28074         contextEl.translate(center, center);
28075         contextEl.rotate(this.rotate * Math.PI / 180);
28076
28077         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28078         
28079         this.canvasEl = document.createElement("canvas");
28080         
28081         this.contextEl = this.canvasEl.getContext("2d");
28082         
28083         switch (this.rotate) {
28084             case 0 :
28085                 
28086                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28087                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28088                 
28089                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28090                 
28091                 break;
28092             case 90 : 
28093                 
28094                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28095                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28096                 
28097                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28098                     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);
28099                     break;
28100                 }
28101                 
28102                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28103                 
28104                 break;
28105             case 180 :
28106                 
28107                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28108                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28109                 
28110                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28111                     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);
28112                     break;
28113                 }
28114                 
28115                 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);
28116                 
28117                 break;
28118             case 270 :
28119                 
28120                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28121                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28122         
28123                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28124                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28125                     break;
28126                 }
28127                 
28128                 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);
28129                 
28130                 break;
28131             default : 
28132                 break;
28133         }
28134         
28135         this.previewEl.appendChild(this.canvasEl);
28136         
28137         this.setCanvasPosition();
28138     },
28139     
28140     crop : function()
28141     {
28142         if(!this.canvasLoaded){
28143             return;
28144         }
28145         
28146         var imageCanvas = document.createElement("canvas");
28147         
28148         var imageContext = imageCanvas.getContext("2d");
28149         
28150         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28151         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28152         
28153         var center = imageCanvas.width / 2;
28154         
28155         imageContext.translate(center, center);
28156         
28157         imageContext.rotate(this.rotate * Math.PI / 180);
28158         
28159         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28160         
28161         var canvas = document.createElement("canvas");
28162         
28163         var context = canvas.getContext("2d");
28164                 
28165         canvas.width = this.minWidth;
28166         canvas.height = this.minHeight;
28167
28168         switch (this.rotate) {
28169             case 0 :
28170                 
28171                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28172                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28173                 
28174                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28175                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28176                 
28177                 var targetWidth = this.minWidth - 2 * x;
28178                 var targetHeight = this.minHeight - 2 * y;
28179                 
28180                 var scale = 1;
28181                 
28182                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28183                     scale = targetWidth / width;
28184                 }
28185                 
28186                 if(x > 0 && y == 0){
28187                     scale = targetHeight / height;
28188                 }
28189                 
28190                 if(x > 0 && y > 0){
28191                     scale = targetWidth / width;
28192                     
28193                     if(width < height){
28194                         scale = targetHeight / height;
28195                     }
28196                 }
28197                 
28198                 context.scale(scale, scale);
28199                 
28200                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28201                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28202
28203                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28204                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28205
28206                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28207                 
28208                 break;
28209             case 90 : 
28210                 
28211                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28212                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28213                 
28214                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28215                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28216                 
28217                 var targetWidth = this.minWidth - 2 * x;
28218                 var targetHeight = this.minHeight - 2 * y;
28219                 
28220                 var scale = 1;
28221                 
28222                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28223                     scale = targetWidth / width;
28224                 }
28225                 
28226                 if(x > 0 && y == 0){
28227                     scale = targetHeight / height;
28228                 }
28229                 
28230                 if(x > 0 && y > 0){
28231                     scale = targetWidth / width;
28232                     
28233                     if(width < height){
28234                         scale = targetHeight / height;
28235                     }
28236                 }
28237                 
28238                 context.scale(scale, scale);
28239                 
28240                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28241                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28242
28243                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28244                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28245                 
28246                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28247                 
28248                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28249                 
28250                 break;
28251             case 180 :
28252                 
28253                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28254                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28255                 
28256                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28257                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28258                 
28259                 var targetWidth = this.minWidth - 2 * x;
28260                 var targetHeight = this.minHeight - 2 * y;
28261                 
28262                 var scale = 1;
28263                 
28264                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28265                     scale = targetWidth / width;
28266                 }
28267                 
28268                 if(x > 0 && y == 0){
28269                     scale = targetHeight / height;
28270                 }
28271                 
28272                 if(x > 0 && y > 0){
28273                     scale = targetWidth / width;
28274                     
28275                     if(width < height){
28276                         scale = targetHeight / height;
28277                     }
28278                 }
28279                 
28280                 context.scale(scale, scale);
28281                 
28282                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28283                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28284
28285                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28286                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28287
28288                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28289                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28290                 
28291                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28292                 
28293                 break;
28294             case 270 :
28295                 
28296                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28297                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28298                 
28299                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28300                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28301                 
28302                 var targetWidth = this.minWidth - 2 * x;
28303                 var targetHeight = this.minHeight - 2 * y;
28304                 
28305                 var scale = 1;
28306                 
28307                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28308                     scale = targetWidth / width;
28309                 }
28310                 
28311                 if(x > 0 && y == 0){
28312                     scale = targetHeight / height;
28313                 }
28314                 
28315                 if(x > 0 && y > 0){
28316                     scale = targetWidth / width;
28317                     
28318                     if(width < height){
28319                         scale = targetHeight / height;
28320                     }
28321                 }
28322                 
28323                 context.scale(scale, scale);
28324                 
28325                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28326                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28327
28328                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28329                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28330                 
28331                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28332                 
28333                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28334                 
28335                 break;
28336             default : 
28337                 break;
28338         }
28339         
28340         this.cropData = canvas.toDataURL(this.cropType);
28341         
28342         if(this.fireEvent('crop', this, this.cropData) !== false){
28343             this.process(this.file, this.cropData);
28344         }
28345         
28346         return;
28347         
28348     },
28349     
28350     setThumbBoxSize : function()
28351     {
28352         var width, height;
28353         
28354         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28355             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28356             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28357             
28358             this.minWidth = width;
28359             this.minHeight = height;
28360             
28361             if(this.rotate == 90 || this.rotate == 270){
28362                 this.minWidth = height;
28363                 this.minHeight = width;
28364             }
28365         }
28366         
28367         height = 300;
28368         width = Math.ceil(this.minWidth * height / this.minHeight);
28369         
28370         if(this.minWidth > this.minHeight){
28371             width = 300;
28372             height = Math.ceil(this.minHeight * width / this.minWidth);
28373         }
28374         
28375         this.thumbEl.setStyle({
28376             width : width + 'px',
28377             height : height + 'px'
28378         });
28379
28380         return;
28381             
28382     },
28383     
28384     setThumbBoxPosition : function()
28385     {
28386         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28387         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28388         
28389         this.thumbEl.setLeft(x);
28390         this.thumbEl.setTop(y);
28391         
28392     },
28393     
28394     baseRotateLevel : function()
28395     {
28396         this.baseRotate = 1;
28397         
28398         if(
28399                 typeof(this.exif) != 'undefined' &&
28400                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28401                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28402         ){
28403             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28404         }
28405         
28406         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28407         
28408     },
28409     
28410     baseScaleLevel : function()
28411     {
28412         var width, height;
28413         
28414         if(this.isDocument){
28415             
28416             if(this.baseRotate == 6 || this.baseRotate == 8){
28417             
28418                 height = this.thumbEl.getHeight();
28419                 this.baseScale = height / this.imageEl.OriginWidth;
28420
28421                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28422                     width = this.thumbEl.getWidth();
28423                     this.baseScale = width / this.imageEl.OriginHeight;
28424                 }
28425
28426                 return;
28427             }
28428
28429             height = this.thumbEl.getHeight();
28430             this.baseScale = height / this.imageEl.OriginHeight;
28431
28432             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28433                 width = this.thumbEl.getWidth();
28434                 this.baseScale = width / this.imageEl.OriginWidth;
28435             }
28436
28437             return;
28438         }
28439         
28440         if(this.baseRotate == 6 || this.baseRotate == 8){
28441             
28442             width = this.thumbEl.getHeight();
28443             this.baseScale = width / this.imageEl.OriginHeight;
28444             
28445             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28446                 height = this.thumbEl.getWidth();
28447                 this.baseScale = height / this.imageEl.OriginHeight;
28448             }
28449             
28450             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28451                 height = this.thumbEl.getWidth();
28452                 this.baseScale = height / this.imageEl.OriginHeight;
28453                 
28454                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28455                     width = this.thumbEl.getHeight();
28456                     this.baseScale = width / this.imageEl.OriginWidth;
28457                 }
28458             }
28459             
28460             return;
28461         }
28462         
28463         width = this.thumbEl.getWidth();
28464         this.baseScale = width / this.imageEl.OriginWidth;
28465         
28466         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28467             height = this.thumbEl.getHeight();
28468             this.baseScale = height / this.imageEl.OriginHeight;
28469         }
28470         
28471         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28472             
28473             height = this.thumbEl.getHeight();
28474             this.baseScale = height / this.imageEl.OriginHeight;
28475             
28476             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28477                 width = this.thumbEl.getWidth();
28478                 this.baseScale = width / this.imageEl.OriginWidth;
28479             }
28480             
28481         }
28482         
28483         return;
28484     },
28485     
28486     getScaleLevel : function()
28487     {
28488         return this.baseScale * Math.pow(1.1, this.scale);
28489     },
28490     
28491     onTouchStart : function(e)
28492     {
28493         if(!this.canvasLoaded){
28494             this.beforeSelectFile(e);
28495             return;
28496         }
28497         
28498         var touches = e.browserEvent.touches;
28499         
28500         if(!touches){
28501             return;
28502         }
28503         
28504         if(touches.length == 1){
28505             this.onMouseDown(e);
28506             return;
28507         }
28508         
28509         if(touches.length != 2){
28510             return;
28511         }
28512         
28513         var coords = [];
28514         
28515         for(var i = 0, finger; finger = touches[i]; i++){
28516             coords.push(finger.pageX, finger.pageY);
28517         }
28518         
28519         var x = Math.pow(coords[0] - coords[2], 2);
28520         var y = Math.pow(coords[1] - coords[3], 2);
28521         
28522         this.startDistance = Math.sqrt(x + y);
28523         
28524         this.startScale = this.scale;
28525         
28526         this.pinching = true;
28527         this.dragable = false;
28528         
28529     },
28530     
28531     onTouchMove : function(e)
28532     {
28533         if(!this.pinching && !this.dragable){
28534             return;
28535         }
28536         
28537         var touches = e.browserEvent.touches;
28538         
28539         if(!touches){
28540             return;
28541         }
28542         
28543         if(this.dragable){
28544             this.onMouseMove(e);
28545             return;
28546         }
28547         
28548         var coords = [];
28549         
28550         for(var i = 0, finger; finger = touches[i]; i++){
28551             coords.push(finger.pageX, finger.pageY);
28552         }
28553         
28554         var x = Math.pow(coords[0] - coords[2], 2);
28555         var y = Math.pow(coords[1] - coords[3], 2);
28556         
28557         this.endDistance = Math.sqrt(x + y);
28558         
28559         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28560         
28561         if(!this.zoomable()){
28562             this.scale = this.startScale;
28563             return;
28564         }
28565         
28566         this.draw();
28567         
28568     },
28569     
28570     onTouchEnd : function(e)
28571     {
28572         this.pinching = false;
28573         this.dragable = false;
28574         
28575     },
28576     
28577     process : function(file, crop)
28578     {
28579         if(this.loadMask){
28580             this.maskEl.mask(this.loadingText);
28581         }
28582         
28583         this.xhr = new XMLHttpRequest();
28584         
28585         file.xhr = this.xhr;
28586
28587         this.xhr.open(this.method, this.url, true);
28588         
28589         var headers = {
28590             "Accept": "application/json",
28591             "Cache-Control": "no-cache",
28592             "X-Requested-With": "XMLHttpRequest"
28593         };
28594         
28595         for (var headerName in headers) {
28596             var headerValue = headers[headerName];
28597             if (headerValue) {
28598                 this.xhr.setRequestHeader(headerName, headerValue);
28599             }
28600         }
28601         
28602         var _this = this;
28603         
28604         this.xhr.onload = function()
28605         {
28606             _this.xhrOnLoad(_this.xhr);
28607         }
28608         
28609         this.xhr.onerror = function()
28610         {
28611             _this.xhrOnError(_this.xhr);
28612         }
28613         
28614         var formData = new FormData();
28615
28616         formData.append('returnHTML', 'NO');
28617         
28618         if(crop){
28619             formData.append('crop', crop);
28620         }
28621         
28622         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28623             formData.append(this.paramName, file, file.name);
28624         }
28625         
28626         if(typeof(file.filename) != 'undefined'){
28627             formData.append('filename', file.filename);
28628         }
28629         
28630         if(typeof(file.mimetype) != 'undefined'){
28631             formData.append('mimetype', file.mimetype);
28632         }
28633         
28634         if(this.fireEvent('arrange', this, formData) != false){
28635             this.xhr.send(formData);
28636         };
28637     },
28638     
28639     xhrOnLoad : function(xhr)
28640     {
28641         if(this.loadMask){
28642             this.maskEl.unmask();
28643         }
28644         
28645         if (xhr.readyState !== 4) {
28646             this.fireEvent('exception', this, xhr);
28647             return;
28648         }
28649
28650         var response = Roo.decode(xhr.responseText);
28651         
28652         if(!response.success){
28653             this.fireEvent('exception', this, xhr);
28654             return;
28655         }
28656         
28657         var response = Roo.decode(xhr.responseText);
28658         
28659         this.fireEvent('upload', this, response);
28660         
28661     },
28662     
28663     xhrOnError : function()
28664     {
28665         if(this.loadMask){
28666             this.maskEl.unmask();
28667         }
28668         
28669         Roo.log('xhr on error');
28670         
28671         var response = Roo.decode(xhr.responseText);
28672           
28673         Roo.log(response);
28674         
28675     },
28676     
28677     prepare : function(file)
28678     {   
28679         if(this.loadMask){
28680             this.maskEl.mask(this.loadingText);
28681         }
28682         
28683         this.file = false;
28684         this.exif = {};
28685         
28686         if(typeof(file) === 'string'){
28687             this.loadCanvas(file);
28688             return;
28689         }
28690         
28691         if(!file || !this.urlAPI){
28692             return;
28693         }
28694         
28695         this.file = file;
28696         this.cropType = file.type;
28697         
28698         var _this = this;
28699         
28700         if(this.fireEvent('prepare', this, this.file) != false){
28701             
28702             var reader = new FileReader();
28703             
28704             reader.onload = function (e) {
28705                 if (e.target.error) {
28706                     Roo.log(e.target.error);
28707                     return;
28708                 }
28709                 
28710                 var buffer = e.target.result,
28711                     dataView = new DataView(buffer),
28712                     offset = 2,
28713                     maxOffset = dataView.byteLength - 4,
28714                     markerBytes,
28715                     markerLength;
28716                 
28717                 if (dataView.getUint16(0) === 0xffd8) {
28718                     while (offset < maxOffset) {
28719                         markerBytes = dataView.getUint16(offset);
28720                         
28721                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28722                             markerLength = dataView.getUint16(offset + 2) + 2;
28723                             if (offset + markerLength > dataView.byteLength) {
28724                                 Roo.log('Invalid meta data: Invalid segment size.');
28725                                 break;
28726                             }
28727                             
28728                             if(markerBytes == 0xffe1){
28729                                 _this.parseExifData(
28730                                     dataView,
28731                                     offset,
28732                                     markerLength
28733                                 );
28734                             }
28735                             
28736                             offset += markerLength;
28737                             
28738                             continue;
28739                         }
28740                         
28741                         break;
28742                     }
28743                     
28744                 }
28745                 
28746                 var url = _this.urlAPI.createObjectURL(_this.file);
28747                 
28748                 _this.loadCanvas(url);
28749                 
28750                 return;
28751             }
28752             
28753             reader.readAsArrayBuffer(this.file);
28754             
28755         }
28756         
28757     },
28758     
28759     parseExifData : function(dataView, offset, length)
28760     {
28761         var tiffOffset = offset + 10,
28762             littleEndian,
28763             dirOffset;
28764     
28765         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28766             // No Exif data, might be XMP data instead
28767             return;
28768         }
28769         
28770         // Check for the ASCII code for "Exif" (0x45786966):
28771         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28772             // No Exif data, might be XMP data instead
28773             return;
28774         }
28775         if (tiffOffset + 8 > dataView.byteLength) {
28776             Roo.log('Invalid Exif data: Invalid segment size.');
28777             return;
28778         }
28779         // Check for the two null bytes:
28780         if (dataView.getUint16(offset + 8) !== 0x0000) {
28781             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28782             return;
28783         }
28784         // Check the byte alignment:
28785         switch (dataView.getUint16(tiffOffset)) {
28786         case 0x4949:
28787             littleEndian = true;
28788             break;
28789         case 0x4D4D:
28790             littleEndian = false;
28791             break;
28792         default:
28793             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28794             return;
28795         }
28796         // Check for the TIFF tag marker (0x002A):
28797         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28798             Roo.log('Invalid Exif data: Missing TIFF marker.');
28799             return;
28800         }
28801         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28802         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28803         
28804         this.parseExifTags(
28805             dataView,
28806             tiffOffset,
28807             tiffOffset + dirOffset,
28808             littleEndian
28809         );
28810     },
28811     
28812     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28813     {
28814         var tagsNumber,
28815             dirEndOffset,
28816             i;
28817         if (dirOffset + 6 > dataView.byteLength) {
28818             Roo.log('Invalid Exif data: Invalid directory offset.');
28819             return;
28820         }
28821         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28822         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28823         if (dirEndOffset + 4 > dataView.byteLength) {
28824             Roo.log('Invalid Exif data: Invalid directory size.');
28825             return;
28826         }
28827         for (i = 0; i < tagsNumber; i += 1) {
28828             this.parseExifTag(
28829                 dataView,
28830                 tiffOffset,
28831                 dirOffset + 2 + 12 * i, // tag offset
28832                 littleEndian
28833             );
28834         }
28835         // Return the offset to the next directory:
28836         return dataView.getUint32(dirEndOffset, littleEndian);
28837     },
28838     
28839     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28840     {
28841         var tag = dataView.getUint16(offset, littleEndian);
28842         
28843         this.exif[tag] = this.getExifValue(
28844             dataView,
28845             tiffOffset,
28846             offset,
28847             dataView.getUint16(offset + 2, littleEndian), // tag type
28848             dataView.getUint32(offset + 4, littleEndian), // tag length
28849             littleEndian
28850         );
28851     },
28852     
28853     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28854     {
28855         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28856             tagSize,
28857             dataOffset,
28858             values,
28859             i,
28860             str,
28861             c;
28862     
28863         if (!tagType) {
28864             Roo.log('Invalid Exif data: Invalid tag type.');
28865             return;
28866         }
28867         
28868         tagSize = tagType.size * length;
28869         // Determine if the value is contained in the dataOffset bytes,
28870         // or if the value at the dataOffset is a pointer to the actual data:
28871         dataOffset = tagSize > 4 ?
28872                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28873         if (dataOffset + tagSize > dataView.byteLength) {
28874             Roo.log('Invalid Exif data: Invalid data offset.');
28875             return;
28876         }
28877         if (length === 1) {
28878             return tagType.getValue(dataView, dataOffset, littleEndian);
28879         }
28880         values = [];
28881         for (i = 0; i < length; i += 1) {
28882             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28883         }
28884         
28885         if (tagType.ascii) {
28886             str = '';
28887             // Concatenate the chars:
28888             for (i = 0; i < values.length; i += 1) {
28889                 c = values[i];
28890                 // Ignore the terminating NULL byte(s):
28891                 if (c === '\u0000') {
28892                     break;
28893                 }
28894                 str += c;
28895             }
28896             return str;
28897         }
28898         return values;
28899     }
28900     
28901 });
28902
28903 Roo.apply(Roo.bootstrap.UploadCropbox, {
28904     tags : {
28905         'Orientation': 0x0112
28906     },
28907     
28908     Orientation: {
28909             1: 0, //'top-left',
28910 //            2: 'top-right',
28911             3: 180, //'bottom-right',
28912 //            4: 'bottom-left',
28913 //            5: 'left-top',
28914             6: 90, //'right-top',
28915 //            7: 'right-bottom',
28916             8: 270 //'left-bottom'
28917     },
28918     
28919     exifTagTypes : {
28920         // byte, 8-bit unsigned int:
28921         1: {
28922             getValue: function (dataView, dataOffset) {
28923                 return dataView.getUint8(dataOffset);
28924             },
28925             size: 1
28926         },
28927         // ascii, 8-bit byte:
28928         2: {
28929             getValue: function (dataView, dataOffset) {
28930                 return String.fromCharCode(dataView.getUint8(dataOffset));
28931             },
28932             size: 1,
28933             ascii: true
28934         },
28935         // short, 16 bit int:
28936         3: {
28937             getValue: function (dataView, dataOffset, littleEndian) {
28938                 return dataView.getUint16(dataOffset, littleEndian);
28939             },
28940             size: 2
28941         },
28942         // long, 32 bit int:
28943         4: {
28944             getValue: function (dataView, dataOffset, littleEndian) {
28945                 return dataView.getUint32(dataOffset, littleEndian);
28946             },
28947             size: 4
28948         },
28949         // rational = two long values, first is numerator, second is denominator:
28950         5: {
28951             getValue: function (dataView, dataOffset, littleEndian) {
28952                 return dataView.getUint32(dataOffset, littleEndian) /
28953                     dataView.getUint32(dataOffset + 4, littleEndian);
28954             },
28955             size: 8
28956         },
28957         // slong, 32 bit signed int:
28958         9: {
28959             getValue: function (dataView, dataOffset, littleEndian) {
28960                 return dataView.getInt32(dataOffset, littleEndian);
28961             },
28962             size: 4
28963         },
28964         // srational, two slongs, first is numerator, second is denominator:
28965         10: {
28966             getValue: function (dataView, dataOffset, littleEndian) {
28967                 return dataView.getInt32(dataOffset, littleEndian) /
28968                     dataView.getInt32(dataOffset + 4, littleEndian);
28969             },
28970             size: 8
28971         }
28972     },
28973     
28974     footer : {
28975         STANDARD : [
28976             {
28977                 tag : 'div',
28978                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28979                 action : 'rotate-left',
28980                 cn : [
28981                     {
28982                         tag : 'button',
28983                         cls : 'btn btn-default',
28984                         html : '<i class="fa fa-undo"></i>'
28985                     }
28986                 ]
28987             },
28988             {
28989                 tag : 'div',
28990                 cls : 'btn-group roo-upload-cropbox-picture',
28991                 action : 'picture',
28992                 cn : [
28993                     {
28994                         tag : 'button',
28995                         cls : 'btn btn-default',
28996                         html : '<i class="fa fa-picture-o"></i>'
28997                     }
28998                 ]
28999             },
29000             {
29001                 tag : 'div',
29002                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29003                 action : 'rotate-right',
29004                 cn : [
29005                     {
29006                         tag : 'button',
29007                         cls : 'btn btn-default',
29008                         html : '<i class="fa fa-repeat"></i>'
29009                     }
29010                 ]
29011             }
29012         ],
29013         DOCUMENT : [
29014             {
29015                 tag : 'div',
29016                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29017                 action : 'rotate-left',
29018                 cn : [
29019                     {
29020                         tag : 'button',
29021                         cls : 'btn btn-default',
29022                         html : '<i class="fa fa-undo"></i>'
29023                     }
29024                 ]
29025             },
29026             {
29027                 tag : 'div',
29028                 cls : 'btn-group roo-upload-cropbox-download',
29029                 action : 'download',
29030                 cn : [
29031                     {
29032                         tag : 'button',
29033                         cls : 'btn btn-default',
29034                         html : '<i class="fa fa-download"></i>'
29035                     }
29036                 ]
29037             },
29038             {
29039                 tag : 'div',
29040                 cls : 'btn-group roo-upload-cropbox-crop',
29041                 action : 'crop',
29042                 cn : [
29043                     {
29044                         tag : 'button',
29045                         cls : 'btn btn-default',
29046                         html : '<i class="fa fa-crop"></i>'
29047                     }
29048                 ]
29049             },
29050             {
29051                 tag : 'div',
29052                 cls : 'btn-group roo-upload-cropbox-trash',
29053                 action : 'trash',
29054                 cn : [
29055                     {
29056                         tag : 'button',
29057                         cls : 'btn btn-default',
29058                         html : '<i class="fa fa-trash"></i>'
29059                     }
29060                 ]
29061             },
29062             {
29063                 tag : 'div',
29064                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29065                 action : 'rotate-right',
29066                 cn : [
29067                     {
29068                         tag : 'button',
29069                         cls : 'btn btn-default',
29070                         html : '<i class="fa fa-repeat"></i>'
29071                     }
29072                 ]
29073             }
29074         ],
29075         ROTATOR : [
29076             {
29077                 tag : 'div',
29078                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29079                 action : 'rotate-left',
29080                 cn : [
29081                     {
29082                         tag : 'button',
29083                         cls : 'btn btn-default',
29084                         html : '<i class="fa fa-undo"></i>'
29085                     }
29086                 ]
29087             },
29088             {
29089                 tag : 'div',
29090                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29091                 action : 'rotate-right',
29092                 cn : [
29093                     {
29094                         tag : 'button',
29095                         cls : 'btn btn-default',
29096                         html : '<i class="fa fa-repeat"></i>'
29097                     }
29098                 ]
29099             }
29100         ]
29101     }
29102 });
29103
29104 /*
29105 * Licence: LGPL
29106 */
29107
29108 /**
29109  * @class Roo.bootstrap.DocumentManager
29110  * @extends Roo.bootstrap.Component
29111  * Bootstrap DocumentManager class
29112  * @cfg {String} paramName default 'imageUpload'
29113  * @cfg {String} toolTipName default 'filename'
29114  * @cfg {String} method default POST
29115  * @cfg {String} url action url
29116  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
29117  * @cfg {Boolean} multiple multiple upload default true
29118  * @cfg {Number} thumbSize default 300
29119  * @cfg {String} fieldLabel
29120  * @cfg {Number} labelWidth default 4
29121  * @cfg {String} labelAlign (left|top) default left
29122  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
29123 * @cfg {Number} labellg set the width of label (1-12)
29124  * @cfg {Number} labelmd set the width of label (1-12)
29125  * @cfg {Number} labelsm set the width of label (1-12)
29126  * @cfg {Number} labelxs set the width of label (1-12)
29127  * 
29128  * @constructor
29129  * Create a new DocumentManager
29130  * @param {Object} config The config object
29131  */
29132
29133 Roo.bootstrap.DocumentManager = function(config){
29134     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
29135     
29136     this.files = [];
29137     this.delegates = [];
29138     
29139     this.addEvents({
29140         /**
29141          * @event initial
29142          * Fire when initial the DocumentManager
29143          * @param {Roo.bootstrap.DocumentManager} this
29144          */
29145         "initial" : true,
29146         /**
29147          * @event inspect
29148          * inspect selected file
29149          * @param {Roo.bootstrap.DocumentManager} this
29150          * @param {File} file
29151          */
29152         "inspect" : true,
29153         /**
29154          * @event exception
29155          * Fire when xhr load exception
29156          * @param {Roo.bootstrap.DocumentManager} this
29157          * @param {XMLHttpRequest} xhr
29158          */
29159         "exception" : true,
29160         /**
29161          * @event afterupload
29162          * Fire when xhr load exception
29163          * @param {Roo.bootstrap.DocumentManager} this
29164          * @param {XMLHttpRequest} xhr
29165          */
29166         "afterupload" : true,
29167         /**
29168          * @event prepare
29169          * prepare the form data
29170          * @param {Roo.bootstrap.DocumentManager} this
29171          * @param {Object} formData
29172          */
29173         "prepare" : true,
29174         /**
29175          * @event remove
29176          * Fire when remove the file
29177          * @param {Roo.bootstrap.DocumentManager} this
29178          * @param {Object} file
29179          */
29180         "remove" : true,
29181         /**
29182          * @event refresh
29183          * Fire after refresh the file
29184          * @param {Roo.bootstrap.DocumentManager} this
29185          */
29186         "refresh" : true,
29187         /**
29188          * @event click
29189          * Fire after click the image
29190          * @param {Roo.bootstrap.DocumentManager} this
29191          * @param {Object} file
29192          */
29193         "click" : true,
29194         /**
29195          * @event edit
29196          * Fire when upload a image and editable set to true
29197          * @param {Roo.bootstrap.DocumentManager} this
29198          * @param {Object} file
29199          */
29200         "edit" : true,
29201         /**
29202          * @event beforeselectfile
29203          * Fire before select file
29204          * @param {Roo.bootstrap.DocumentManager} this
29205          */
29206         "beforeselectfile" : true,
29207         /**
29208          * @event process
29209          * Fire before process file
29210          * @param {Roo.bootstrap.DocumentManager} this
29211          * @param {Object} file
29212          */
29213         "process" : true,
29214         /**
29215          * @event previewrendered
29216          * Fire when preview rendered
29217          * @param {Roo.bootstrap.DocumentManager} this
29218          * @param {Object} file
29219          */
29220         "previewrendered" : true,
29221         /**
29222          */
29223         "previewResize" : true
29224         
29225     });
29226 };
29227
29228 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
29229     
29230     boxes : 0,
29231     inputName : '',
29232     thumbSize : 300,
29233     multiple : true,
29234     files : false,
29235     method : 'POST',
29236     url : '',
29237     paramName : 'imageUpload',
29238     toolTipName : 'filename',
29239     fieldLabel : '',
29240     labelWidth : 4,
29241     labelAlign : 'left',
29242     editable : true,
29243     delegates : false,
29244     xhr : false, 
29245     
29246     labellg : 0,
29247     labelmd : 0,
29248     labelsm : 0,
29249     labelxs : 0,
29250     
29251     getAutoCreate : function()
29252     {   
29253         var managerWidget = {
29254             tag : 'div',
29255             cls : 'roo-document-manager',
29256             cn : [
29257                 {
29258                     tag : 'input',
29259                     cls : 'roo-document-manager-selector',
29260                     type : 'file'
29261                 },
29262                 {
29263                     tag : 'div',
29264                     cls : 'roo-document-manager-uploader',
29265                     cn : [
29266                         {
29267                             tag : 'div',
29268                             cls : 'roo-document-manager-upload-btn',
29269                             html : '<i class="fa fa-plus"></i>'
29270                         }
29271                     ]
29272                     
29273                 }
29274             ]
29275         };
29276         
29277         var content = [
29278             {
29279                 tag : 'div',
29280                 cls : 'column col-md-12',
29281                 cn : managerWidget
29282             }
29283         ];
29284         
29285         if(this.fieldLabel.length){
29286             
29287             content = [
29288                 {
29289                     tag : 'div',
29290                     cls : 'column col-md-12',
29291                     html : this.fieldLabel
29292                 },
29293                 {
29294                     tag : 'div',
29295                     cls : 'column col-md-12',
29296                     cn : managerWidget
29297                 }
29298             ];
29299
29300             if(this.labelAlign == 'left'){
29301                 content = [
29302                     {
29303                         tag : 'div',
29304                         cls : 'column',
29305                         html : this.fieldLabel
29306                     },
29307                     {
29308                         tag : 'div',
29309                         cls : 'column',
29310                         cn : managerWidget
29311                     }
29312                 ];
29313                 
29314                 if(this.labelWidth > 12){
29315                     content[0].style = "width: " + this.labelWidth + 'px';
29316                 }
29317
29318                 if(this.labelWidth < 13 && this.labelmd == 0){
29319                     this.labelmd = this.labelWidth;
29320                 }
29321
29322                 if(this.labellg > 0){
29323                     content[0].cls += ' col-lg-' + this.labellg;
29324                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29325                 }
29326
29327                 if(this.labelmd > 0){
29328                     content[0].cls += ' col-md-' + this.labelmd;
29329                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29330                 }
29331
29332                 if(this.labelsm > 0){
29333                     content[0].cls += ' col-sm-' + this.labelsm;
29334                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29335                 }
29336
29337                 if(this.labelxs > 0){
29338                     content[0].cls += ' col-xs-' + this.labelxs;
29339                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29340                 }
29341                 
29342             }
29343         }
29344         
29345         var cfg = {
29346             tag : 'div',
29347             cls : 'row clearfix',
29348             cn : content
29349         };
29350         
29351         return cfg;
29352         
29353     },
29354     
29355     initEvents : function()
29356     {
29357         this.managerEl = this.el.select('.roo-document-manager', true).first();
29358         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29359         
29360         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29361         this.selectorEl.hide();
29362         
29363         if(this.multiple){
29364             this.selectorEl.attr('multiple', 'multiple');
29365         }
29366         
29367         this.selectorEl.on('change', this.onFileSelected, this);
29368         
29369         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29370         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29371         
29372         this.uploader.on('click', this.onUploaderClick, this);
29373         
29374         this.renderProgressDialog();
29375         
29376         var _this = this;
29377         
29378         window.addEventListener("resize", function() { _this.refresh(); } );
29379         
29380         this.fireEvent('initial', this);
29381     },
29382     
29383     renderProgressDialog : function()
29384     {
29385         var _this = this;
29386         
29387         this.progressDialog = new Roo.bootstrap.Modal({
29388             cls : 'roo-document-manager-progress-dialog',
29389             allow_close : false,
29390             animate : false,
29391             title : '',
29392             buttons : [
29393                 {
29394                     name  :'cancel',
29395                     weight : 'danger',
29396                     html : 'Cancel'
29397                 }
29398             ], 
29399             listeners : { 
29400                 btnclick : function() {
29401                     _this.uploadCancel();
29402                     this.hide();
29403                 }
29404             }
29405         });
29406          
29407         this.progressDialog.render(Roo.get(document.body));
29408          
29409         this.progress = new Roo.bootstrap.Progress({
29410             cls : 'roo-document-manager-progress',
29411             active : true,
29412             striped : true
29413         });
29414         
29415         this.progress.render(this.progressDialog.getChildContainer());
29416         
29417         this.progressBar = new Roo.bootstrap.ProgressBar({
29418             cls : 'roo-document-manager-progress-bar',
29419             aria_valuenow : 0,
29420             aria_valuemin : 0,
29421             aria_valuemax : 12,
29422             panel : 'success'
29423         });
29424         
29425         this.progressBar.render(this.progress.getChildContainer());
29426     },
29427     
29428     onUploaderClick : function(e)
29429     {
29430         e.preventDefault();
29431      
29432         if(this.fireEvent('beforeselectfile', this) != false){
29433             this.selectorEl.dom.click();
29434         }
29435         
29436     },
29437     
29438     onFileSelected : function(e)
29439     {
29440         e.preventDefault();
29441         
29442         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29443             return;
29444         }
29445         
29446         Roo.each(this.selectorEl.dom.files, function(file){
29447             if(this.fireEvent('inspect', this, file) != false){
29448                 this.files.push(file);
29449             }
29450         }, this);
29451         
29452         this.queue();
29453         
29454     },
29455     
29456     queue : function()
29457     {
29458         this.selectorEl.dom.value = '';
29459         
29460         if(!this.files || !this.files.length){
29461             return;
29462         }
29463         
29464         if(this.boxes > 0 && this.files.length > this.boxes){
29465             this.files = this.files.slice(0, this.boxes);
29466         }
29467         
29468         this.uploader.show();
29469         
29470         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29471             this.uploader.hide();
29472         }
29473         
29474         var _this = this;
29475         
29476         var files = [];
29477         
29478         var docs = [];
29479         
29480         Roo.each(this.files, function(file){
29481             
29482             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29483                 var f = this.renderPreview(file);
29484                 files.push(f);
29485                 return;
29486             }
29487             
29488             if(file.type.indexOf('image') != -1){
29489                 this.delegates.push(
29490                     (function(){
29491                         _this.process(file);
29492                     }).createDelegate(this)
29493                 );
29494         
29495                 return;
29496             }
29497             
29498             docs.push(
29499                 (function(){
29500                     _this.process(file);
29501                 }).createDelegate(this)
29502             );
29503             
29504         }, this);
29505         
29506         this.files = files;
29507         
29508         this.delegates = this.delegates.concat(docs);
29509         
29510         if(!this.delegates.length){
29511             this.refresh();
29512             return;
29513         }
29514         
29515         this.progressBar.aria_valuemax = this.delegates.length;
29516         
29517         this.arrange();
29518         
29519         return;
29520     },
29521     
29522     arrange : function()
29523     {
29524         if(!this.delegates.length){
29525             this.progressDialog.hide();
29526             this.refresh();
29527             return;
29528         }
29529         
29530         var delegate = this.delegates.shift();
29531         
29532         this.progressDialog.show();
29533         
29534         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29535         
29536         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29537         
29538         delegate();
29539     },
29540     
29541     refresh : function()
29542     {
29543         this.uploader.show();
29544         
29545         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29546             this.uploader.hide();
29547         }
29548         
29549         Roo.isTouch ? this.closable(false) : this.closable(true);
29550         
29551         this.fireEvent('refresh', this);
29552     },
29553     
29554     onRemove : function(e, el, o)
29555     {
29556         e.preventDefault();
29557         
29558         this.fireEvent('remove', this, o);
29559         
29560     },
29561     
29562     remove : function(o)
29563     {
29564         var files = [];
29565         
29566         Roo.each(this.files, function(file){
29567             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29568                 files.push(file);
29569                 return;
29570             }
29571
29572             o.target.remove();
29573
29574         }, this);
29575         
29576         this.files = files;
29577         
29578         this.refresh();
29579     },
29580     
29581     clear : function()
29582     {
29583         Roo.each(this.files, function(file){
29584             if(!file.target){
29585                 return;
29586             }
29587             
29588             file.target.remove();
29589
29590         }, this);
29591         
29592         this.files = [];
29593         
29594         this.refresh();
29595     },
29596     
29597     onClick : function(e, el, o)
29598     {
29599         e.preventDefault();
29600         
29601         this.fireEvent('click', this, o);
29602         
29603     },
29604     
29605     closable : function(closable)
29606     {
29607         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29608             
29609             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29610             
29611             if(closable){
29612                 el.show();
29613                 return;
29614             }
29615             
29616             el.hide();
29617             
29618         }, this);
29619     },
29620     
29621     xhrOnLoad : function(xhr)
29622     {
29623         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29624             el.remove();
29625         }, this);
29626         
29627         if (xhr.readyState !== 4) {
29628             this.arrange();
29629             this.fireEvent('exception', this, xhr);
29630             return;
29631         }
29632
29633         var response = Roo.decode(xhr.responseText);
29634         
29635         if(!response.success){
29636             this.arrange();
29637             this.fireEvent('exception', this, xhr);
29638             return;
29639         }
29640         
29641         var file = this.renderPreview(response.data);
29642         
29643         this.files.push(file);
29644         
29645         this.arrange();
29646         
29647         this.fireEvent('afterupload', this, xhr);
29648         
29649     },
29650     
29651     xhrOnError : function(xhr)
29652     {
29653         Roo.log('xhr on error');
29654         
29655         var response = Roo.decode(xhr.responseText);
29656           
29657         Roo.log(response);
29658         
29659         this.arrange();
29660     },
29661     
29662     process : function(file)
29663     {
29664         if(this.fireEvent('process', this, file) !== false){
29665             if(this.editable && file.type.indexOf('image') != -1){
29666                 this.fireEvent('edit', this, file);
29667                 return;
29668             }
29669
29670             this.uploadStart(file, false);
29671
29672             return;
29673         }
29674         
29675     },
29676     
29677     uploadStart : function(file, crop)
29678     {
29679         this.xhr = new XMLHttpRequest();
29680         
29681         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29682             this.arrange();
29683             return;
29684         }
29685         
29686         file.xhr = this.xhr;
29687             
29688         this.managerEl.createChild({
29689             tag : 'div',
29690             cls : 'roo-document-manager-loading',
29691             cn : [
29692                 {
29693                     tag : 'div',
29694                     tooltip : file.name,
29695                     cls : 'roo-document-manager-thumb',
29696                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29697                 }
29698             ]
29699
29700         });
29701
29702         this.xhr.open(this.method, this.url, true);
29703         
29704         var headers = {
29705             "Accept": "application/json",
29706             "Cache-Control": "no-cache",
29707             "X-Requested-With": "XMLHttpRequest"
29708         };
29709         
29710         for (var headerName in headers) {
29711             var headerValue = headers[headerName];
29712             if (headerValue) {
29713                 this.xhr.setRequestHeader(headerName, headerValue);
29714             }
29715         }
29716         
29717         var _this = this;
29718         
29719         this.xhr.onload = function()
29720         {
29721             _this.xhrOnLoad(_this.xhr);
29722         }
29723         
29724         this.xhr.onerror = function()
29725         {
29726             _this.xhrOnError(_this.xhr);
29727         }
29728         
29729         var formData = new FormData();
29730
29731         formData.append('returnHTML', 'NO');
29732         
29733         if(crop){
29734             formData.append('crop', crop);
29735         }
29736         
29737         formData.append(this.paramName, file, file.name);
29738         
29739         var options = {
29740             file : file, 
29741             manually : false
29742         };
29743         
29744         if(this.fireEvent('prepare', this, formData, options) != false){
29745             
29746             if(options.manually){
29747                 return;
29748             }
29749             
29750             this.xhr.send(formData);
29751             return;
29752         };
29753         
29754         this.uploadCancel();
29755     },
29756     
29757     uploadCancel : function()
29758     {
29759         if (this.xhr) {
29760             this.xhr.abort();
29761         }
29762         
29763         this.delegates = [];
29764         
29765         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29766             el.remove();
29767         }, this);
29768         
29769         this.arrange();
29770     },
29771     
29772     renderPreview : function(file)
29773     {
29774         if(typeof(file.target) != 'undefined' && file.target){
29775             return file;
29776         }
29777         
29778         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29779         
29780         var previewEl = this.managerEl.createChild({
29781             tag : 'div',
29782             cls : 'roo-document-manager-preview',
29783             cn : [
29784                 {
29785                     tag : 'div',
29786                     tooltip : file[this.toolTipName],
29787                     cls : 'roo-document-manager-thumb',
29788                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29789                 },
29790                 {
29791                     tag : 'button',
29792                     cls : 'close',
29793                     html : '<i class="fa fa-times-circle"></i>'
29794                 }
29795             ]
29796         });
29797
29798         var close = previewEl.select('button.close', true).first();
29799
29800         close.on('click', this.onRemove, this, file);
29801
29802         file.target = previewEl;
29803
29804         var image = previewEl.select('img', true).first();
29805         
29806         var _this = this;
29807         
29808         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29809         
29810         image.on('click', this.onClick, this, file);
29811         
29812         this.fireEvent('previewrendered', this, file);
29813         
29814         return file;
29815         
29816     },
29817     
29818     onPreviewLoad : function(file, image)
29819     {
29820         if(typeof(file.target) == 'undefined' || !file.target){
29821             return;
29822         }
29823         
29824         var width = image.dom.naturalWidth || image.dom.width;
29825         var height = image.dom.naturalHeight || image.dom.height;
29826         
29827         if(!this.previewResize) {
29828             return;
29829         }
29830         
29831         if(width > height){
29832             file.target.addClass('wide');
29833             return;
29834         }
29835         
29836         file.target.addClass('tall');
29837         return;
29838         
29839     },
29840     
29841     uploadFromSource : function(file, crop)
29842     {
29843         this.xhr = new XMLHttpRequest();
29844         
29845         this.managerEl.createChild({
29846             tag : 'div',
29847             cls : 'roo-document-manager-loading',
29848             cn : [
29849                 {
29850                     tag : 'div',
29851                     tooltip : file.name,
29852                     cls : 'roo-document-manager-thumb',
29853                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29854                 }
29855             ]
29856
29857         });
29858
29859         this.xhr.open(this.method, this.url, true);
29860         
29861         var headers = {
29862             "Accept": "application/json",
29863             "Cache-Control": "no-cache",
29864             "X-Requested-With": "XMLHttpRequest"
29865         };
29866         
29867         for (var headerName in headers) {
29868             var headerValue = headers[headerName];
29869             if (headerValue) {
29870                 this.xhr.setRequestHeader(headerName, headerValue);
29871             }
29872         }
29873         
29874         var _this = this;
29875         
29876         this.xhr.onload = function()
29877         {
29878             _this.xhrOnLoad(_this.xhr);
29879         }
29880         
29881         this.xhr.onerror = function()
29882         {
29883             _this.xhrOnError(_this.xhr);
29884         }
29885         
29886         var formData = new FormData();
29887
29888         formData.append('returnHTML', 'NO');
29889         
29890         formData.append('crop', crop);
29891         
29892         if(typeof(file.filename) != 'undefined'){
29893             formData.append('filename', file.filename);
29894         }
29895         
29896         if(typeof(file.mimetype) != 'undefined'){
29897             formData.append('mimetype', file.mimetype);
29898         }
29899         
29900         Roo.log(formData);
29901         
29902         if(this.fireEvent('prepare', this, formData) != false){
29903             this.xhr.send(formData);
29904         };
29905     }
29906 });
29907
29908 /*
29909 * Licence: LGPL
29910 */
29911
29912 /**
29913  * @class Roo.bootstrap.DocumentViewer
29914  * @extends Roo.bootstrap.Component
29915  * Bootstrap DocumentViewer class
29916  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29917  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29918  * 
29919  * @constructor
29920  * Create a new DocumentViewer
29921  * @param {Object} config The config object
29922  */
29923
29924 Roo.bootstrap.DocumentViewer = function(config){
29925     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29926     
29927     this.addEvents({
29928         /**
29929          * @event initial
29930          * Fire after initEvent
29931          * @param {Roo.bootstrap.DocumentViewer} this
29932          */
29933         "initial" : true,
29934         /**
29935          * @event click
29936          * Fire after click
29937          * @param {Roo.bootstrap.DocumentViewer} this
29938          */
29939         "click" : true,
29940         /**
29941          * @event download
29942          * Fire after download button
29943          * @param {Roo.bootstrap.DocumentViewer} this
29944          */
29945         "download" : true,
29946         /**
29947          * @event trash
29948          * Fire after trash button
29949          * @param {Roo.bootstrap.DocumentViewer} this
29950          */
29951         "trash" : true
29952         
29953     });
29954 };
29955
29956 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29957     
29958     showDownload : true,
29959     
29960     showTrash : true,
29961     
29962     getAutoCreate : function()
29963     {
29964         var cfg = {
29965             tag : 'div',
29966             cls : 'roo-document-viewer',
29967             cn : [
29968                 {
29969                     tag : 'div',
29970                     cls : 'roo-document-viewer-body',
29971                     cn : [
29972                         {
29973                             tag : 'div',
29974                             cls : 'roo-document-viewer-thumb',
29975                             cn : [
29976                                 {
29977                                     tag : 'img',
29978                                     cls : 'roo-document-viewer-image'
29979                                 }
29980                             ]
29981                         }
29982                     ]
29983                 },
29984                 {
29985                     tag : 'div',
29986                     cls : 'roo-document-viewer-footer',
29987                     cn : {
29988                         tag : 'div',
29989                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29990                         cn : [
29991                             {
29992                                 tag : 'div',
29993                                 cls : 'btn-group roo-document-viewer-download',
29994                                 cn : [
29995                                     {
29996                                         tag : 'button',
29997                                         cls : 'btn btn-default',
29998                                         html : '<i class="fa fa-download"></i>'
29999                                     }
30000                                 ]
30001                             },
30002                             {
30003                                 tag : 'div',
30004                                 cls : 'btn-group roo-document-viewer-trash',
30005                                 cn : [
30006                                     {
30007                                         tag : 'button',
30008                                         cls : 'btn btn-default',
30009                                         html : '<i class="fa fa-trash"></i>'
30010                                     }
30011                                 ]
30012                             }
30013                         ]
30014                     }
30015                 }
30016             ]
30017         };
30018         
30019         return cfg;
30020     },
30021     
30022     initEvents : function()
30023     {
30024         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
30025         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
30026         
30027         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
30028         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
30029         
30030         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
30031         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
30032         
30033         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
30034         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
30035         
30036         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
30037         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
30038         
30039         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
30040         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
30041         
30042         this.bodyEl.on('click', this.onClick, this);
30043         this.downloadBtn.on('click', this.onDownload, this);
30044         this.trashBtn.on('click', this.onTrash, this);
30045         
30046         this.downloadBtn.hide();
30047         this.trashBtn.hide();
30048         
30049         if(this.showDownload){
30050             this.downloadBtn.show();
30051         }
30052         
30053         if(this.showTrash){
30054             this.trashBtn.show();
30055         }
30056         
30057         if(!this.showDownload && !this.showTrash) {
30058             this.footerEl.hide();
30059         }
30060         
30061     },
30062     
30063     initial : function()
30064     {
30065         this.fireEvent('initial', this);
30066         
30067     },
30068     
30069     onClick : function(e)
30070     {
30071         e.preventDefault();
30072         
30073         this.fireEvent('click', this);
30074     },
30075     
30076     onDownload : function(e)
30077     {
30078         e.preventDefault();
30079         
30080         this.fireEvent('download', this);
30081     },
30082     
30083     onTrash : function(e)
30084     {
30085         e.preventDefault();
30086         
30087         this.fireEvent('trash', this);
30088     }
30089     
30090 });
30091 /*
30092  * - LGPL
30093  *
30094  * nav progress bar
30095  * 
30096  */
30097
30098 /**
30099  * @class Roo.bootstrap.NavProgressBar
30100  * @extends Roo.bootstrap.Component
30101  * Bootstrap NavProgressBar class
30102  * 
30103  * @constructor
30104  * Create a new nav progress bar
30105  * @param {Object} config The config object
30106  */
30107
30108 Roo.bootstrap.NavProgressBar = function(config){
30109     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
30110
30111     this.bullets = this.bullets || [];
30112    
30113 //    Roo.bootstrap.NavProgressBar.register(this);
30114      this.addEvents({
30115         /**
30116              * @event changed
30117              * Fires when the active item changes
30118              * @param {Roo.bootstrap.NavProgressBar} this
30119              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
30120              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
30121          */
30122         'changed': true
30123      });
30124     
30125 };
30126
30127 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
30128     
30129     bullets : [],
30130     barItems : [],
30131     
30132     getAutoCreate : function()
30133     {
30134         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
30135         
30136         cfg = {
30137             tag : 'div',
30138             cls : 'roo-navigation-bar-group',
30139             cn : [
30140                 {
30141                     tag : 'div',
30142                     cls : 'roo-navigation-top-bar'
30143                 },
30144                 {
30145                     tag : 'div',
30146                     cls : 'roo-navigation-bullets-bar',
30147                     cn : [
30148                         {
30149                             tag : 'ul',
30150                             cls : 'roo-navigation-bar'
30151                         }
30152                     ]
30153                 },
30154                 
30155                 {
30156                     tag : 'div',
30157                     cls : 'roo-navigation-bottom-bar'
30158                 }
30159             ]
30160             
30161         };
30162         
30163         return cfg;
30164         
30165     },
30166     
30167     initEvents: function() 
30168     {
30169         
30170     },
30171     
30172     onRender : function(ct, position) 
30173     {
30174         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30175         
30176         if(this.bullets.length){
30177             Roo.each(this.bullets, function(b){
30178                this.addItem(b);
30179             }, this);
30180         }
30181         
30182         this.format();
30183         
30184     },
30185     
30186     addItem : function(cfg)
30187     {
30188         var item = new Roo.bootstrap.NavProgressItem(cfg);
30189         
30190         item.parentId = this.id;
30191         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
30192         
30193         if(cfg.html){
30194             var top = new Roo.bootstrap.Element({
30195                 tag : 'div',
30196                 cls : 'roo-navigation-bar-text'
30197             });
30198             
30199             var bottom = new Roo.bootstrap.Element({
30200                 tag : 'div',
30201                 cls : 'roo-navigation-bar-text'
30202             });
30203             
30204             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
30205             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
30206             
30207             var topText = new Roo.bootstrap.Element({
30208                 tag : 'span',
30209                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
30210             });
30211             
30212             var bottomText = new Roo.bootstrap.Element({
30213                 tag : 'span',
30214                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30215             });
30216             
30217             topText.onRender(top.el, null);
30218             bottomText.onRender(bottom.el, null);
30219             
30220             item.topEl = top;
30221             item.bottomEl = bottom;
30222         }
30223         
30224         this.barItems.push(item);
30225         
30226         return item;
30227     },
30228     
30229     getActive : function()
30230     {
30231         var active = false;
30232         
30233         Roo.each(this.barItems, function(v){
30234             
30235             if (!v.isActive()) {
30236                 return;
30237             }
30238             
30239             active = v;
30240             return false;
30241             
30242         });
30243         
30244         return active;
30245     },
30246     
30247     setActiveItem : function(item)
30248     {
30249         var prev = false;
30250         
30251         Roo.each(this.barItems, function(v){
30252             if (v.rid == item.rid) {
30253                 return ;
30254             }
30255             
30256             if (v.isActive()) {
30257                 v.setActive(false);
30258                 prev = v;
30259             }
30260         });
30261
30262         item.setActive(true);
30263         
30264         this.fireEvent('changed', this, item, prev);
30265     },
30266     
30267     getBarItem: function(rid)
30268     {
30269         var ret = false;
30270         
30271         Roo.each(this.barItems, function(e) {
30272             if (e.rid != rid) {
30273                 return;
30274             }
30275             
30276             ret =  e;
30277             return false;
30278         });
30279         
30280         return ret;
30281     },
30282     
30283     indexOfItem : function(item)
30284     {
30285         var index = false;
30286         
30287         Roo.each(this.barItems, function(v, i){
30288             
30289             if (v.rid != item.rid) {
30290                 return;
30291             }
30292             
30293             index = i;
30294             return false
30295         });
30296         
30297         return index;
30298     },
30299     
30300     setActiveNext : function()
30301     {
30302         var i = this.indexOfItem(this.getActive());
30303         
30304         if (i > this.barItems.length) {
30305             return;
30306         }
30307         
30308         this.setActiveItem(this.barItems[i+1]);
30309     },
30310     
30311     setActivePrev : function()
30312     {
30313         var i = this.indexOfItem(this.getActive());
30314         
30315         if (i  < 1) {
30316             return;
30317         }
30318         
30319         this.setActiveItem(this.barItems[i-1]);
30320     },
30321     
30322     format : function()
30323     {
30324         if(!this.barItems.length){
30325             return;
30326         }
30327      
30328         var width = 100 / this.barItems.length;
30329         
30330         Roo.each(this.barItems, function(i){
30331             i.el.setStyle('width', width + '%');
30332             i.topEl.el.setStyle('width', width + '%');
30333             i.bottomEl.el.setStyle('width', width + '%');
30334         }, this);
30335         
30336     }
30337     
30338 });
30339 /*
30340  * - LGPL
30341  *
30342  * Nav Progress Item
30343  * 
30344  */
30345
30346 /**
30347  * @class Roo.bootstrap.NavProgressItem
30348  * @extends Roo.bootstrap.Component
30349  * Bootstrap NavProgressItem class
30350  * @cfg {String} rid the reference id
30351  * @cfg {Boolean} active (true|false) Is item active default false
30352  * @cfg {Boolean} disabled (true|false) Is item active default false
30353  * @cfg {String} html
30354  * @cfg {String} position (top|bottom) text position default bottom
30355  * @cfg {String} icon show icon instead of number
30356  * 
30357  * @constructor
30358  * Create a new NavProgressItem
30359  * @param {Object} config The config object
30360  */
30361 Roo.bootstrap.NavProgressItem = function(config){
30362     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30363     this.addEvents({
30364         // raw events
30365         /**
30366          * @event click
30367          * The raw click event for the entire grid.
30368          * @param {Roo.bootstrap.NavProgressItem} this
30369          * @param {Roo.EventObject} e
30370          */
30371         "click" : true
30372     });
30373    
30374 };
30375
30376 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30377     
30378     rid : '',
30379     active : false,
30380     disabled : false,
30381     html : '',
30382     position : 'bottom',
30383     icon : false,
30384     
30385     getAutoCreate : function()
30386     {
30387         var iconCls = 'roo-navigation-bar-item-icon';
30388         
30389         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30390         
30391         var cfg = {
30392             tag: 'li',
30393             cls: 'roo-navigation-bar-item',
30394             cn : [
30395                 {
30396                     tag : 'i',
30397                     cls : iconCls
30398                 }
30399             ]
30400         };
30401         
30402         if(this.active){
30403             cfg.cls += ' active';
30404         }
30405         if(this.disabled){
30406             cfg.cls += ' disabled';
30407         }
30408         
30409         return cfg;
30410     },
30411     
30412     disable : function()
30413     {
30414         this.setDisabled(true);
30415     },
30416     
30417     enable : function()
30418     {
30419         this.setDisabled(false);
30420     },
30421     
30422     initEvents: function() 
30423     {
30424         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30425         
30426         this.iconEl.on('click', this.onClick, this);
30427     },
30428     
30429     onClick : function(e)
30430     {
30431         e.preventDefault();
30432         
30433         if(this.disabled){
30434             return;
30435         }
30436         
30437         if(this.fireEvent('click', this, e) === false){
30438             return;
30439         };
30440         
30441         this.parent().setActiveItem(this);
30442     },
30443     
30444     isActive: function () 
30445     {
30446         return this.active;
30447     },
30448     
30449     setActive : function(state)
30450     {
30451         if(this.active == state){
30452             return;
30453         }
30454         
30455         this.active = state;
30456         
30457         if (state) {
30458             this.el.addClass('active');
30459             return;
30460         }
30461         
30462         this.el.removeClass('active');
30463         
30464         return;
30465     },
30466     
30467     setDisabled : function(state)
30468     {
30469         if(this.disabled == state){
30470             return;
30471         }
30472         
30473         this.disabled = state;
30474         
30475         if (state) {
30476             this.el.addClass('disabled');
30477             return;
30478         }
30479         
30480         this.el.removeClass('disabled');
30481     },
30482     
30483     tooltipEl : function()
30484     {
30485         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30486     }
30487 });
30488  
30489
30490  /*
30491  * - LGPL
30492  *
30493  * FieldLabel
30494  * 
30495  */
30496
30497 /**
30498  * @class Roo.bootstrap.FieldLabel
30499  * @extends Roo.bootstrap.Component
30500  * Bootstrap FieldLabel class
30501  * @cfg {String} html contents of the element
30502  * @cfg {String} tag tag of the element default label
30503  * @cfg {String} cls class of the element
30504  * @cfg {String} target label target 
30505  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30506  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
30507  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
30508  * @cfg {String} iconTooltip default "This field is required"
30509  * @cfg {String} indicatorpos (left|right) default left
30510  * 
30511  * @constructor
30512  * Create a new FieldLabel
30513  * @param {Object} config The config object
30514  */
30515
30516 Roo.bootstrap.FieldLabel = function(config){
30517     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30518     
30519     this.addEvents({
30520             /**
30521              * @event invalid
30522              * Fires after the field has been marked as invalid.
30523              * @param {Roo.form.FieldLabel} this
30524              * @param {String} msg The validation message
30525              */
30526             invalid : true,
30527             /**
30528              * @event valid
30529              * Fires after the field has been validated with no errors.
30530              * @param {Roo.form.FieldLabel} this
30531              */
30532             valid : true
30533         });
30534 };
30535
30536 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30537     
30538     tag: 'label',
30539     cls: '',
30540     html: '',
30541     target: '',
30542     allowBlank : true,
30543     invalidClass : 'has-warning',
30544     validClass : 'has-success',
30545     iconTooltip : 'This field is required',
30546     indicatorpos : 'left',
30547     
30548     getAutoCreate : function(){
30549         
30550         var cls = "";
30551         if (!this.allowBlank) {
30552             cls  = "visible";
30553         }
30554         
30555         var cfg = {
30556             tag : this.tag,
30557             cls : 'roo-bootstrap-field-label ' + this.cls,
30558             for : this.target,
30559             cn : [
30560                 {
30561                     tag : 'i',
30562                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30563                     tooltip : this.iconTooltip
30564                 },
30565                 {
30566                     tag : 'span',
30567                     html : this.html
30568                 }
30569             ] 
30570         };
30571         
30572         if(this.indicatorpos == 'right'){
30573             var cfg = {
30574                 tag : this.tag,
30575                 cls : 'roo-bootstrap-field-label ' + this.cls,
30576                 for : this.target,
30577                 cn : [
30578                     {
30579                         tag : 'span',
30580                         html : this.html
30581                     },
30582                     {
30583                         tag : 'i',
30584                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30585                         tooltip : this.iconTooltip
30586                     }
30587                 ] 
30588             };
30589         }
30590         
30591         return cfg;
30592     },
30593     
30594     initEvents: function() 
30595     {
30596         Roo.bootstrap.Element.superclass.initEvents.call(this);
30597         
30598         this.indicator = this.indicatorEl();
30599         
30600         if(this.indicator){
30601             this.indicator.removeClass('visible');
30602             this.indicator.addClass('invisible');
30603         }
30604         
30605         Roo.bootstrap.FieldLabel.register(this);
30606     },
30607     
30608     indicatorEl : function()
30609     {
30610         var indicator = this.el.select('i.roo-required-indicator',true).first();
30611         
30612         if(!indicator){
30613             return false;
30614         }
30615         
30616         return indicator;
30617         
30618     },
30619     
30620     /**
30621      * Mark this field as valid
30622      */
30623     markValid : function()
30624     {
30625         if(this.indicator){
30626             this.indicator.removeClass('visible');
30627             this.indicator.addClass('invisible');
30628         }
30629         if (Roo.bootstrap.version == 3) {
30630             this.el.removeClass(this.invalidClass);
30631             this.el.addClass(this.validClass);
30632         } else {
30633             this.el.removeClass('is-invalid');
30634             this.el.addClass('is-valid');
30635         }
30636         
30637         
30638         this.fireEvent('valid', this);
30639     },
30640     
30641     /**
30642      * Mark this field as invalid
30643      * @param {String} msg The validation message
30644      */
30645     markInvalid : function(msg)
30646     {
30647         if(this.indicator){
30648             this.indicator.removeClass('invisible');
30649             this.indicator.addClass('visible');
30650         }
30651           if (Roo.bootstrap.version == 3) {
30652             this.el.removeClass(this.validClass);
30653             this.el.addClass(this.invalidClass);
30654         } else {
30655             this.el.removeClass('is-valid');
30656             this.el.addClass('is-invalid');
30657         }
30658         
30659         
30660         this.fireEvent('invalid', this, msg);
30661     }
30662     
30663    
30664 });
30665
30666 Roo.apply(Roo.bootstrap.FieldLabel, {
30667     
30668     groups: {},
30669     
30670      /**
30671     * register a FieldLabel Group
30672     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30673     */
30674     register : function(label)
30675     {
30676         if(this.groups.hasOwnProperty(label.target)){
30677             return;
30678         }
30679      
30680         this.groups[label.target] = label;
30681         
30682     },
30683     /**
30684     * fetch a FieldLabel Group based on the target
30685     * @param {string} target
30686     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30687     */
30688     get: function(target) {
30689         if (typeof(this.groups[target]) == 'undefined') {
30690             return false;
30691         }
30692         
30693         return this.groups[target] ;
30694     }
30695 });
30696
30697  
30698
30699  /*
30700  * - LGPL
30701  *
30702  * page DateSplitField.
30703  * 
30704  */
30705
30706
30707 /**
30708  * @class Roo.bootstrap.DateSplitField
30709  * @extends Roo.bootstrap.Component
30710  * Bootstrap DateSplitField class
30711  * @cfg {string} fieldLabel - the label associated
30712  * @cfg {Number} labelWidth set the width of label (0-12)
30713  * @cfg {String} labelAlign (top|left)
30714  * @cfg {Boolean} dayAllowBlank (true|false) default false
30715  * @cfg {Boolean} monthAllowBlank (true|false) default false
30716  * @cfg {Boolean} yearAllowBlank (true|false) default false
30717  * @cfg {string} dayPlaceholder 
30718  * @cfg {string} monthPlaceholder
30719  * @cfg {string} yearPlaceholder
30720  * @cfg {string} dayFormat default 'd'
30721  * @cfg {string} monthFormat default 'm'
30722  * @cfg {string} yearFormat default 'Y'
30723  * @cfg {Number} labellg set the width of label (1-12)
30724  * @cfg {Number} labelmd set the width of label (1-12)
30725  * @cfg {Number} labelsm set the width of label (1-12)
30726  * @cfg {Number} labelxs set the width of label (1-12)
30727
30728  *     
30729  * @constructor
30730  * Create a new DateSplitField
30731  * @param {Object} config The config object
30732  */
30733
30734 Roo.bootstrap.DateSplitField = function(config){
30735     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30736     
30737     this.addEvents({
30738         // raw events
30739          /**
30740          * @event years
30741          * getting the data of years
30742          * @param {Roo.bootstrap.DateSplitField} this
30743          * @param {Object} years
30744          */
30745         "years" : true,
30746         /**
30747          * @event days
30748          * getting the data of days
30749          * @param {Roo.bootstrap.DateSplitField} this
30750          * @param {Object} days
30751          */
30752         "days" : true,
30753         /**
30754          * @event invalid
30755          * Fires after the field has been marked as invalid.
30756          * @param {Roo.form.Field} this
30757          * @param {String} msg The validation message
30758          */
30759         invalid : true,
30760        /**
30761          * @event valid
30762          * Fires after the field has been validated with no errors.
30763          * @param {Roo.form.Field} this
30764          */
30765         valid : true
30766     });
30767 };
30768
30769 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30770     
30771     fieldLabel : '',
30772     labelAlign : 'top',
30773     labelWidth : 3,
30774     dayAllowBlank : false,
30775     monthAllowBlank : false,
30776     yearAllowBlank : false,
30777     dayPlaceholder : '',
30778     monthPlaceholder : '',
30779     yearPlaceholder : '',
30780     dayFormat : 'd',
30781     monthFormat : 'm',
30782     yearFormat : 'Y',
30783     isFormField : true,
30784     labellg : 0,
30785     labelmd : 0,
30786     labelsm : 0,
30787     labelxs : 0,
30788     
30789     getAutoCreate : function()
30790     {
30791         var cfg = {
30792             tag : 'div',
30793             cls : 'row roo-date-split-field-group',
30794             cn : [
30795                 {
30796                     tag : 'input',
30797                     type : 'hidden',
30798                     cls : 'form-hidden-field roo-date-split-field-group-value',
30799                     name : this.name
30800                 }
30801             ]
30802         };
30803         
30804         var labelCls = 'col-md-12';
30805         var contentCls = 'col-md-4';
30806         
30807         if(this.fieldLabel){
30808             
30809             var label = {
30810                 tag : 'div',
30811                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30812                 cn : [
30813                     {
30814                         tag : 'label',
30815                         html : this.fieldLabel
30816                     }
30817                 ]
30818             };
30819             
30820             if(this.labelAlign == 'left'){
30821             
30822                 if(this.labelWidth > 12){
30823                     label.style = "width: " + this.labelWidth + 'px';
30824                 }
30825
30826                 if(this.labelWidth < 13 && this.labelmd == 0){
30827                     this.labelmd = this.labelWidth;
30828                 }
30829
30830                 if(this.labellg > 0){
30831                     labelCls = ' col-lg-' + this.labellg;
30832                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30833                 }
30834
30835                 if(this.labelmd > 0){
30836                     labelCls = ' col-md-' + this.labelmd;
30837                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30838                 }
30839
30840                 if(this.labelsm > 0){
30841                     labelCls = ' col-sm-' + this.labelsm;
30842                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30843                 }
30844
30845                 if(this.labelxs > 0){
30846                     labelCls = ' col-xs-' + this.labelxs;
30847                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30848                 }
30849             }
30850             
30851             label.cls += ' ' + labelCls;
30852             
30853             cfg.cn.push(label);
30854         }
30855         
30856         Roo.each(['day', 'month', 'year'], function(t){
30857             cfg.cn.push({
30858                 tag : 'div',
30859                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30860             });
30861         }, this);
30862         
30863         return cfg;
30864     },
30865     
30866     inputEl: function ()
30867     {
30868         return this.el.select('.roo-date-split-field-group-value', true).first();
30869     },
30870     
30871     onRender : function(ct, position) 
30872     {
30873         var _this = this;
30874         
30875         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30876         
30877         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30878         
30879         this.dayField = new Roo.bootstrap.ComboBox({
30880             allowBlank : this.dayAllowBlank,
30881             alwaysQuery : true,
30882             displayField : 'value',
30883             editable : false,
30884             fieldLabel : '',
30885             forceSelection : true,
30886             mode : 'local',
30887             placeholder : this.dayPlaceholder,
30888             selectOnFocus : true,
30889             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30890             triggerAction : 'all',
30891             typeAhead : true,
30892             valueField : 'value',
30893             store : new Roo.data.SimpleStore({
30894                 data : (function() {    
30895                     var days = [];
30896                     _this.fireEvent('days', _this, days);
30897                     return days;
30898                 })(),
30899                 fields : [ 'value' ]
30900             }),
30901             listeners : {
30902                 select : function (_self, record, index)
30903                 {
30904                     _this.setValue(_this.getValue());
30905                 }
30906             }
30907         });
30908
30909         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30910         
30911         this.monthField = new Roo.bootstrap.MonthField({
30912             after : '<i class=\"fa fa-calendar\"></i>',
30913             allowBlank : this.monthAllowBlank,
30914             placeholder : this.monthPlaceholder,
30915             readOnly : true,
30916             listeners : {
30917                 render : function (_self)
30918                 {
30919                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30920                         e.preventDefault();
30921                         _self.focus();
30922                     });
30923                 },
30924                 select : function (_self, oldvalue, newvalue)
30925                 {
30926                     _this.setValue(_this.getValue());
30927                 }
30928             }
30929         });
30930         
30931         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30932         
30933         this.yearField = new Roo.bootstrap.ComboBox({
30934             allowBlank : this.yearAllowBlank,
30935             alwaysQuery : true,
30936             displayField : 'value',
30937             editable : false,
30938             fieldLabel : '',
30939             forceSelection : true,
30940             mode : 'local',
30941             placeholder : this.yearPlaceholder,
30942             selectOnFocus : true,
30943             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30944             triggerAction : 'all',
30945             typeAhead : true,
30946             valueField : 'value',
30947             store : new Roo.data.SimpleStore({
30948                 data : (function() {
30949                     var years = [];
30950                     _this.fireEvent('years', _this, years);
30951                     return years;
30952                 })(),
30953                 fields : [ 'value' ]
30954             }),
30955             listeners : {
30956                 select : function (_self, record, index)
30957                 {
30958                     _this.setValue(_this.getValue());
30959                 }
30960             }
30961         });
30962
30963         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30964     },
30965     
30966     setValue : function(v, format)
30967     {
30968         this.inputEl.dom.value = v;
30969         
30970         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30971         
30972         var d = Date.parseDate(v, f);
30973         
30974         if(!d){
30975             this.validate();
30976             return;
30977         }
30978         
30979         this.setDay(d.format(this.dayFormat));
30980         this.setMonth(d.format(this.monthFormat));
30981         this.setYear(d.format(this.yearFormat));
30982         
30983         this.validate();
30984         
30985         return;
30986     },
30987     
30988     setDay : function(v)
30989     {
30990         this.dayField.setValue(v);
30991         this.inputEl.dom.value = this.getValue();
30992         this.validate();
30993         return;
30994     },
30995     
30996     setMonth : function(v)
30997     {
30998         this.monthField.setValue(v, true);
30999         this.inputEl.dom.value = this.getValue();
31000         this.validate();
31001         return;
31002     },
31003     
31004     setYear : function(v)
31005     {
31006         this.yearField.setValue(v);
31007         this.inputEl.dom.value = this.getValue();
31008         this.validate();
31009         return;
31010     },
31011     
31012     getDay : function()
31013     {
31014         return this.dayField.getValue();
31015     },
31016     
31017     getMonth : function()
31018     {
31019         return this.monthField.getValue();
31020     },
31021     
31022     getYear : function()
31023     {
31024         return this.yearField.getValue();
31025     },
31026     
31027     getValue : function()
31028     {
31029         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
31030         
31031         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
31032         
31033         return date;
31034     },
31035     
31036     reset : function()
31037     {
31038         this.setDay('');
31039         this.setMonth('');
31040         this.setYear('');
31041         this.inputEl.dom.value = '';
31042         this.validate();
31043         return;
31044     },
31045     
31046     validate : function()
31047     {
31048         var d = this.dayField.validate();
31049         var m = this.monthField.validate();
31050         var y = this.yearField.validate();
31051         
31052         var valid = true;
31053         
31054         if(
31055                 (!this.dayAllowBlank && !d) ||
31056                 (!this.monthAllowBlank && !m) ||
31057                 (!this.yearAllowBlank && !y)
31058         ){
31059             valid = false;
31060         }
31061         
31062         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
31063             return valid;
31064         }
31065         
31066         if(valid){
31067             this.markValid();
31068             return valid;
31069         }
31070         
31071         this.markInvalid();
31072         
31073         return valid;
31074     },
31075     
31076     markValid : function()
31077     {
31078         
31079         var label = this.el.select('label', true).first();
31080         var icon = this.el.select('i.fa-star', true).first();
31081
31082         if(label && icon){
31083             icon.remove();
31084         }
31085         
31086         this.fireEvent('valid', this);
31087     },
31088     
31089      /**
31090      * Mark this field as invalid
31091      * @param {String} msg The validation message
31092      */
31093     markInvalid : function(msg)
31094     {
31095         
31096         var label = this.el.select('label', true).first();
31097         var icon = this.el.select('i.fa-star', true).first();
31098
31099         if(label && !icon){
31100             this.el.select('.roo-date-split-field-label', true).createChild({
31101                 tag : 'i',
31102                 cls : 'text-danger fa fa-lg fa-star',
31103                 tooltip : 'This field is required',
31104                 style : 'margin-right:5px;'
31105             }, label, true);
31106         }
31107         
31108         this.fireEvent('invalid', this, msg);
31109     },
31110     
31111     clearInvalid : function()
31112     {
31113         var label = this.el.select('label', true).first();
31114         var icon = this.el.select('i.fa-star', true).first();
31115
31116         if(label && icon){
31117             icon.remove();
31118         }
31119         
31120         this.fireEvent('valid', this);
31121     },
31122     
31123     getName: function()
31124     {
31125         return this.name;
31126     }
31127     
31128 });
31129
31130  /**
31131  *
31132  * This is based on 
31133  * http://masonry.desandro.com
31134  *
31135  * The idea is to render all the bricks based on vertical width...
31136  *
31137  * The original code extends 'outlayer' - we might need to use that....
31138  * 
31139  */
31140
31141
31142 /**
31143  * @class Roo.bootstrap.LayoutMasonry
31144  * @extends Roo.bootstrap.Component
31145  * Bootstrap Layout Masonry class
31146  * 
31147  * @constructor
31148  * Create a new Element
31149  * @param {Object} config The config object
31150  */
31151
31152 Roo.bootstrap.LayoutMasonry = function(config){
31153     
31154     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
31155     
31156     this.bricks = [];
31157     
31158     Roo.bootstrap.LayoutMasonry.register(this);
31159     
31160     this.addEvents({
31161         // raw events
31162         /**
31163          * @event layout
31164          * Fire after layout the items
31165          * @param {Roo.bootstrap.LayoutMasonry} this
31166          * @param {Roo.EventObject} e
31167          */
31168         "layout" : true
31169     });
31170     
31171 };
31172
31173 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
31174     
31175     /**
31176      * @cfg {Boolean} isLayoutInstant = no animation?
31177      */   
31178     isLayoutInstant : false, // needed?
31179    
31180     /**
31181      * @cfg {Number} boxWidth  width of the columns
31182      */   
31183     boxWidth : 450,
31184     
31185       /**
31186      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
31187      */   
31188     boxHeight : 0,
31189     
31190     /**
31191      * @cfg {Number} padWidth padding below box..
31192      */   
31193     padWidth : 10, 
31194     
31195     /**
31196      * @cfg {Number} gutter gutter width..
31197      */   
31198     gutter : 10,
31199     
31200      /**
31201      * @cfg {Number} maxCols maximum number of columns
31202      */   
31203     
31204     maxCols: 0,
31205     
31206     /**
31207      * @cfg {Boolean} isAutoInitial defalut true
31208      */   
31209     isAutoInitial : true, 
31210     
31211     containerWidth: 0,
31212     
31213     /**
31214      * @cfg {Boolean} isHorizontal defalut false
31215      */   
31216     isHorizontal : false, 
31217
31218     currentSize : null,
31219     
31220     tag: 'div',
31221     
31222     cls: '',
31223     
31224     bricks: null, //CompositeElement
31225     
31226     cols : 1,
31227     
31228     _isLayoutInited : false,
31229     
31230 //    isAlternative : false, // only use for vertical layout...
31231     
31232     /**
31233      * @cfg {Number} alternativePadWidth padding below box..
31234      */   
31235     alternativePadWidth : 50,
31236     
31237     selectedBrick : [],
31238     
31239     getAutoCreate : function(){
31240         
31241         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31242         
31243         var cfg = {
31244             tag: this.tag,
31245             cls: 'blog-masonary-wrapper ' + this.cls,
31246             cn : {
31247                 cls : 'mas-boxes masonary'
31248             }
31249         };
31250         
31251         return cfg;
31252     },
31253     
31254     getChildContainer: function( )
31255     {
31256         if (this.boxesEl) {
31257             return this.boxesEl;
31258         }
31259         
31260         this.boxesEl = this.el.select('.mas-boxes').first();
31261         
31262         return this.boxesEl;
31263     },
31264     
31265     
31266     initEvents : function()
31267     {
31268         var _this = this;
31269         
31270         if(this.isAutoInitial){
31271             Roo.log('hook children rendered');
31272             this.on('childrenrendered', function() {
31273                 Roo.log('children rendered');
31274                 _this.initial();
31275             } ,this);
31276         }
31277     },
31278     
31279     initial : function()
31280     {
31281         this.selectedBrick = [];
31282         
31283         this.currentSize = this.el.getBox(true);
31284         
31285         Roo.EventManager.onWindowResize(this.resize, this); 
31286
31287         if(!this.isAutoInitial){
31288             this.layout();
31289             return;
31290         }
31291         
31292         this.layout();
31293         
31294         return;
31295         //this.layout.defer(500,this);
31296         
31297     },
31298     
31299     resize : function()
31300     {
31301         var cs = this.el.getBox(true);
31302         
31303         if (
31304                 this.currentSize.width == cs.width && 
31305                 this.currentSize.x == cs.x && 
31306                 this.currentSize.height == cs.height && 
31307                 this.currentSize.y == cs.y 
31308         ) {
31309             Roo.log("no change in with or X or Y");
31310             return;
31311         }
31312         
31313         this.currentSize = cs;
31314         
31315         this.layout();
31316         
31317     },
31318     
31319     layout : function()
31320     {   
31321         this._resetLayout();
31322         
31323         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31324         
31325         this.layoutItems( isInstant );
31326       
31327         this._isLayoutInited = true;
31328         
31329         this.fireEvent('layout', this);
31330         
31331     },
31332     
31333     _resetLayout : function()
31334     {
31335         if(this.isHorizontal){
31336             this.horizontalMeasureColumns();
31337             return;
31338         }
31339         
31340         this.verticalMeasureColumns();
31341         
31342     },
31343     
31344     verticalMeasureColumns : function()
31345     {
31346         this.getContainerWidth();
31347         
31348 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31349 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31350 //            return;
31351 //        }
31352         
31353         var boxWidth = this.boxWidth + this.padWidth;
31354         
31355         if(this.containerWidth < this.boxWidth){
31356             boxWidth = this.containerWidth
31357         }
31358         
31359         var containerWidth = this.containerWidth;
31360         
31361         var cols = Math.floor(containerWidth / boxWidth);
31362         
31363         this.cols = Math.max( cols, 1 );
31364         
31365         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31366         
31367         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31368         
31369         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31370         
31371         this.colWidth = boxWidth + avail - this.padWidth;
31372         
31373         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31374         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31375     },
31376     
31377     horizontalMeasureColumns : function()
31378     {
31379         this.getContainerWidth();
31380         
31381         var boxWidth = this.boxWidth;
31382         
31383         if(this.containerWidth < boxWidth){
31384             boxWidth = this.containerWidth;
31385         }
31386         
31387         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31388         
31389         this.el.setHeight(boxWidth);
31390         
31391     },
31392     
31393     getContainerWidth : function()
31394     {
31395         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31396     },
31397     
31398     layoutItems : function( isInstant )
31399     {
31400         Roo.log(this.bricks);
31401         
31402         var items = Roo.apply([], this.bricks);
31403         
31404         if(this.isHorizontal){
31405             this._horizontalLayoutItems( items , isInstant );
31406             return;
31407         }
31408         
31409 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31410 //            this._verticalAlternativeLayoutItems( items , isInstant );
31411 //            return;
31412 //        }
31413         
31414         this._verticalLayoutItems( items , isInstant );
31415         
31416     },
31417     
31418     _verticalLayoutItems : function ( items , isInstant)
31419     {
31420         if ( !items || !items.length ) {
31421             return;
31422         }
31423         
31424         var standard = [
31425             ['xs', 'xs', 'xs', 'tall'],
31426             ['xs', 'xs', 'tall'],
31427             ['xs', 'xs', 'sm'],
31428             ['xs', 'xs', 'xs'],
31429             ['xs', 'tall'],
31430             ['xs', 'sm'],
31431             ['xs', 'xs'],
31432             ['xs'],
31433             
31434             ['sm', 'xs', 'xs'],
31435             ['sm', 'xs'],
31436             ['sm'],
31437             
31438             ['tall', 'xs', 'xs', 'xs'],
31439             ['tall', 'xs', 'xs'],
31440             ['tall', 'xs'],
31441             ['tall']
31442             
31443         ];
31444         
31445         var queue = [];
31446         
31447         var boxes = [];
31448         
31449         var box = [];
31450         
31451         Roo.each(items, function(item, k){
31452             
31453             switch (item.size) {
31454                 // these layouts take up a full box,
31455                 case 'md' :
31456                 case 'md-left' :
31457                 case 'md-right' :
31458                 case 'wide' :
31459                     
31460                     if(box.length){
31461                         boxes.push(box);
31462                         box = [];
31463                     }
31464                     
31465                     boxes.push([item]);
31466                     
31467                     break;
31468                     
31469                 case 'xs' :
31470                 case 'sm' :
31471                 case 'tall' :
31472                     
31473                     box.push(item);
31474                     
31475                     break;
31476                 default :
31477                     break;
31478                     
31479             }
31480             
31481         }, this);
31482         
31483         if(box.length){
31484             boxes.push(box);
31485             box = [];
31486         }
31487         
31488         var filterPattern = function(box, length)
31489         {
31490             if(!box.length){
31491                 return;
31492             }
31493             
31494             var match = false;
31495             
31496             var pattern = box.slice(0, length);
31497             
31498             var format = [];
31499             
31500             Roo.each(pattern, function(i){
31501                 format.push(i.size);
31502             }, this);
31503             
31504             Roo.each(standard, function(s){
31505                 
31506                 if(String(s) != String(format)){
31507                     return;
31508                 }
31509                 
31510                 match = true;
31511                 return false;
31512                 
31513             }, this);
31514             
31515             if(!match && length == 1){
31516                 return;
31517             }
31518             
31519             if(!match){
31520                 filterPattern(box, length - 1);
31521                 return;
31522             }
31523                 
31524             queue.push(pattern);
31525
31526             box = box.slice(length, box.length);
31527
31528             filterPattern(box, 4);
31529
31530             return;
31531             
31532         }
31533         
31534         Roo.each(boxes, function(box, k){
31535             
31536             if(!box.length){
31537                 return;
31538             }
31539             
31540             if(box.length == 1){
31541                 queue.push(box);
31542                 return;
31543             }
31544             
31545             filterPattern(box, 4);
31546             
31547         }, this);
31548         
31549         this._processVerticalLayoutQueue( queue, isInstant );
31550         
31551     },
31552     
31553 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31554 //    {
31555 //        if ( !items || !items.length ) {
31556 //            return;
31557 //        }
31558 //
31559 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31560 //        
31561 //    },
31562     
31563     _horizontalLayoutItems : function ( items , isInstant)
31564     {
31565         if ( !items || !items.length || items.length < 3) {
31566             return;
31567         }
31568         
31569         items.reverse();
31570         
31571         var eItems = items.slice(0, 3);
31572         
31573         items = items.slice(3, items.length);
31574         
31575         var standard = [
31576             ['xs', 'xs', 'xs', 'wide'],
31577             ['xs', 'xs', 'wide'],
31578             ['xs', 'xs', 'sm'],
31579             ['xs', 'xs', 'xs'],
31580             ['xs', 'wide'],
31581             ['xs', 'sm'],
31582             ['xs', 'xs'],
31583             ['xs'],
31584             
31585             ['sm', 'xs', 'xs'],
31586             ['sm', 'xs'],
31587             ['sm'],
31588             
31589             ['wide', 'xs', 'xs', 'xs'],
31590             ['wide', 'xs', 'xs'],
31591             ['wide', 'xs'],
31592             ['wide'],
31593             
31594             ['wide-thin']
31595         ];
31596         
31597         var queue = [];
31598         
31599         var boxes = [];
31600         
31601         var box = [];
31602         
31603         Roo.each(items, function(item, k){
31604             
31605             switch (item.size) {
31606                 case 'md' :
31607                 case 'md-left' :
31608                 case 'md-right' :
31609                 case 'tall' :
31610                     
31611                     if(box.length){
31612                         boxes.push(box);
31613                         box = [];
31614                     }
31615                     
31616                     boxes.push([item]);
31617                     
31618                     break;
31619                     
31620                 case 'xs' :
31621                 case 'sm' :
31622                 case 'wide' :
31623                 case 'wide-thin' :
31624                     
31625                     box.push(item);
31626                     
31627                     break;
31628                 default :
31629                     break;
31630                     
31631             }
31632             
31633         }, this);
31634         
31635         if(box.length){
31636             boxes.push(box);
31637             box = [];
31638         }
31639         
31640         var filterPattern = function(box, length)
31641         {
31642             if(!box.length){
31643                 return;
31644             }
31645             
31646             var match = false;
31647             
31648             var pattern = box.slice(0, length);
31649             
31650             var format = [];
31651             
31652             Roo.each(pattern, function(i){
31653                 format.push(i.size);
31654             }, this);
31655             
31656             Roo.each(standard, function(s){
31657                 
31658                 if(String(s) != String(format)){
31659                     return;
31660                 }
31661                 
31662                 match = true;
31663                 return false;
31664                 
31665             }, this);
31666             
31667             if(!match && length == 1){
31668                 return;
31669             }
31670             
31671             if(!match){
31672                 filterPattern(box, length - 1);
31673                 return;
31674             }
31675                 
31676             queue.push(pattern);
31677
31678             box = box.slice(length, box.length);
31679
31680             filterPattern(box, 4);
31681
31682             return;
31683             
31684         }
31685         
31686         Roo.each(boxes, function(box, k){
31687             
31688             if(!box.length){
31689                 return;
31690             }
31691             
31692             if(box.length == 1){
31693                 queue.push(box);
31694                 return;
31695             }
31696             
31697             filterPattern(box, 4);
31698             
31699         }, this);
31700         
31701         
31702         var prune = [];
31703         
31704         var pos = this.el.getBox(true);
31705         
31706         var minX = pos.x;
31707         
31708         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31709         
31710         var hit_end = false;
31711         
31712         Roo.each(queue, function(box){
31713             
31714             if(hit_end){
31715                 
31716                 Roo.each(box, function(b){
31717                 
31718                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31719                     b.el.hide();
31720
31721                 }, this);
31722
31723                 return;
31724             }
31725             
31726             var mx = 0;
31727             
31728             Roo.each(box, function(b){
31729                 
31730                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31731                 b.el.show();
31732
31733                 mx = Math.max(mx, b.x);
31734                 
31735             }, this);
31736             
31737             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31738             
31739             if(maxX < minX){
31740                 
31741                 Roo.each(box, function(b){
31742                 
31743                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31744                     b.el.hide();
31745                     
31746                 }, this);
31747                 
31748                 hit_end = true;
31749                 
31750                 return;
31751             }
31752             
31753             prune.push(box);
31754             
31755         }, this);
31756         
31757         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31758     },
31759     
31760     /** Sets position of item in DOM
31761     * @param {Element} item
31762     * @param {Number} x - horizontal position
31763     * @param {Number} y - vertical position
31764     * @param {Boolean} isInstant - disables transitions
31765     */
31766     _processVerticalLayoutQueue : function( queue, isInstant )
31767     {
31768         var pos = this.el.getBox(true);
31769         var x = pos.x;
31770         var y = pos.y;
31771         var maxY = [];
31772         
31773         for (var i = 0; i < this.cols; i++){
31774             maxY[i] = pos.y;
31775         }
31776         
31777         Roo.each(queue, function(box, k){
31778             
31779             var col = k % this.cols;
31780             
31781             Roo.each(box, function(b,kk){
31782                 
31783                 b.el.position('absolute');
31784                 
31785                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31786                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31787                 
31788                 if(b.size == 'md-left' || b.size == 'md-right'){
31789                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31790                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31791                 }
31792                 
31793                 b.el.setWidth(width);
31794                 b.el.setHeight(height);
31795                 // iframe?
31796                 b.el.select('iframe',true).setSize(width,height);
31797                 
31798             }, this);
31799             
31800             for (var i = 0; i < this.cols; i++){
31801                 
31802                 if(maxY[i] < maxY[col]){
31803                     col = i;
31804                     continue;
31805                 }
31806                 
31807                 col = Math.min(col, i);
31808                 
31809             }
31810             
31811             x = pos.x + col * (this.colWidth + this.padWidth);
31812             
31813             y = maxY[col];
31814             
31815             var positions = [];
31816             
31817             switch (box.length){
31818                 case 1 :
31819                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31820                     break;
31821                 case 2 :
31822                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31823                     break;
31824                 case 3 :
31825                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31826                     break;
31827                 case 4 :
31828                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31829                     break;
31830                 default :
31831                     break;
31832             }
31833             
31834             Roo.each(box, function(b,kk){
31835                 
31836                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31837                 
31838                 var sz = b.el.getSize();
31839                 
31840                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31841                 
31842             }, this);
31843             
31844         }, this);
31845         
31846         var mY = 0;
31847         
31848         for (var i = 0; i < this.cols; i++){
31849             mY = Math.max(mY, maxY[i]);
31850         }
31851         
31852         this.el.setHeight(mY - pos.y);
31853         
31854     },
31855     
31856 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31857 //    {
31858 //        var pos = this.el.getBox(true);
31859 //        var x = pos.x;
31860 //        var y = pos.y;
31861 //        var maxX = pos.right;
31862 //        
31863 //        var maxHeight = 0;
31864 //        
31865 //        Roo.each(items, function(item, k){
31866 //            
31867 //            var c = k % 2;
31868 //            
31869 //            item.el.position('absolute');
31870 //                
31871 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31872 //
31873 //            item.el.setWidth(width);
31874 //
31875 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31876 //
31877 //            item.el.setHeight(height);
31878 //            
31879 //            if(c == 0){
31880 //                item.el.setXY([x, y], isInstant ? false : true);
31881 //            } else {
31882 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31883 //            }
31884 //            
31885 //            y = y + height + this.alternativePadWidth;
31886 //            
31887 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31888 //            
31889 //        }, this);
31890 //        
31891 //        this.el.setHeight(maxHeight);
31892 //        
31893 //    },
31894     
31895     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31896     {
31897         var pos = this.el.getBox(true);
31898         
31899         var minX = pos.x;
31900         var minY = pos.y;
31901         
31902         var maxX = pos.right;
31903         
31904         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31905         
31906         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31907         
31908         Roo.each(queue, function(box, k){
31909             
31910             Roo.each(box, function(b, kk){
31911                 
31912                 b.el.position('absolute');
31913                 
31914                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31915                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31916                 
31917                 if(b.size == 'md-left' || b.size == 'md-right'){
31918                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31919                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31920                 }
31921                 
31922                 b.el.setWidth(width);
31923                 b.el.setHeight(height);
31924                 
31925             }, this);
31926             
31927             if(!box.length){
31928                 return;
31929             }
31930             
31931             var positions = [];
31932             
31933             switch (box.length){
31934                 case 1 :
31935                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31936                     break;
31937                 case 2 :
31938                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31939                     break;
31940                 case 3 :
31941                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31942                     break;
31943                 case 4 :
31944                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31945                     break;
31946                 default :
31947                     break;
31948             }
31949             
31950             Roo.each(box, function(b,kk){
31951                 
31952                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31953                 
31954                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31955                 
31956             }, this);
31957             
31958         }, this);
31959         
31960     },
31961     
31962     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31963     {
31964         Roo.each(eItems, function(b,k){
31965             
31966             b.size = (k == 0) ? 'sm' : 'xs';
31967             b.x = (k == 0) ? 2 : 1;
31968             b.y = (k == 0) ? 2 : 1;
31969             
31970             b.el.position('absolute');
31971             
31972             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31973                 
31974             b.el.setWidth(width);
31975             
31976             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31977             
31978             b.el.setHeight(height);
31979             
31980         }, this);
31981
31982         var positions = [];
31983         
31984         positions.push({
31985             x : maxX - this.unitWidth * 2 - this.gutter,
31986             y : minY
31987         });
31988         
31989         positions.push({
31990             x : maxX - this.unitWidth,
31991             y : minY + (this.unitWidth + this.gutter) * 2
31992         });
31993         
31994         positions.push({
31995             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31996             y : minY
31997         });
31998         
31999         Roo.each(eItems, function(b,k){
32000             
32001             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
32002
32003         }, this);
32004         
32005     },
32006     
32007     getVerticalOneBoxColPositions : function(x, y, box)
32008     {
32009         var pos = [];
32010         
32011         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
32012         
32013         if(box[0].size == 'md-left'){
32014             rand = 0;
32015         }
32016         
32017         if(box[0].size == 'md-right'){
32018             rand = 1;
32019         }
32020         
32021         pos.push({
32022             x : x + (this.unitWidth + this.gutter) * rand,
32023             y : y
32024         });
32025         
32026         return pos;
32027     },
32028     
32029     getVerticalTwoBoxColPositions : function(x, y, box)
32030     {
32031         var pos = [];
32032         
32033         if(box[0].size == 'xs'){
32034             
32035             pos.push({
32036                 x : x,
32037                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
32038             });
32039
32040             pos.push({
32041                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
32042                 y : y
32043             });
32044             
32045             return pos;
32046             
32047         }
32048         
32049         pos.push({
32050             x : x,
32051             y : y
32052         });
32053
32054         pos.push({
32055             x : x + (this.unitWidth + this.gutter) * 2,
32056             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
32057         });
32058         
32059         return pos;
32060         
32061     },
32062     
32063     getVerticalThreeBoxColPositions : function(x, y, box)
32064     {
32065         var pos = [];
32066         
32067         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32068             
32069             pos.push({
32070                 x : x,
32071                 y : y
32072             });
32073
32074             pos.push({
32075                 x : x + (this.unitWidth + this.gutter) * 1,
32076                 y : y
32077             });
32078             
32079             pos.push({
32080                 x : x + (this.unitWidth + this.gutter) * 2,
32081                 y : y
32082             });
32083             
32084             return pos;
32085             
32086         }
32087         
32088         if(box[0].size == 'xs' && box[1].size == 'xs'){
32089             
32090             pos.push({
32091                 x : x,
32092                 y : y
32093             });
32094
32095             pos.push({
32096                 x : x,
32097                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
32098             });
32099             
32100             pos.push({
32101                 x : x + (this.unitWidth + this.gutter) * 1,
32102                 y : y
32103             });
32104             
32105             return pos;
32106             
32107         }
32108         
32109         pos.push({
32110             x : x,
32111             y : y
32112         });
32113
32114         pos.push({
32115             x : x + (this.unitWidth + this.gutter) * 2,
32116             y : y
32117         });
32118
32119         pos.push({
32120             x : x + (this.unitWidth + this.gutter) * 2,
32121             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
32122         });
32123             
32124         return pos;
32125         
32126     },
32127     
32128     getVerticalFourBoxColPositions : function(x, y, box)
32129     {
32130         var pos = [];
32131         
32132         if(box[0].size == 'xs'){
32133             
32134             pos.push({
32135                 x : x,
32136                 y : y
32137             });
32138
32139             pos.push({
32140                 x : x,
32141                 y : y + (this.unitHeight + this.gutter) * 1
32142             });
32143             
32144             pos.push({
32145                 x : x,
32146                 y : y + (this.unitHeight + this.gutter) * 2
32147             });
32148             
32149             pos.push({
32150                 x : x + (this.unitWidth + this.gutter) * 1,
32151                 y : y
32152             });
32153             
32154             return pos;
32155             
32156         }
32157         
32158         pos.push({
32159             x : x,
32160             y : y
32161         });
32162
32163         pos.push({
32164             x : x + (this.unitWidth + this.gutter) * 2,
32165             y : y
32166         });
32167
32168         pos.push({
32169             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
32170             y : y + (this.unitHeight + this.gutter) * 1
32171         });
32172
32173         pos.push({
32174             x : x + (this.unitWidth + this.gutter) * 2,
32175             y : y + (this.unitWidth + this.gutter) * 2
32176         });
32177
32178         return pos;
32179         
32180     },
32181     
32182     getHorizontalOneBoxColPositions : function(maxX, minY, box)
32183     {
32184         var pos = [];
32185         
32186         if(box[0].size == 'md-left'){
32187             pos.push({
32188                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32189                 y : minY
32190             });
32191             
32192             return pos;
32193         }
32194         
32195         if(box[0].size == 'md-right'){
32196             pos.push({
32197                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32198                 y : minY + (this.unitWidth + this.gutter) * 1
32199             });
32200             
32201             return pos;
32202         }
32203         
32204         var rand = Math.floor(Math.random() * (4 - box[0].y));
32205         
32206         pos.push({
32207             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32208             y : minY + (this.unitWidth + this.gutter) * rand
32209         });
32210         
32211         return pos;
32212         
32213     },
32214     
32215     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
32216     {
32217         var pos = [];
32218         
32219         if(box[0].size == 'xs'){
32220             
32221             pos.push({
32222                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32223                 y : minY
32224             });
32225
32226             pos.push({
32227                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32228                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32229             });
32230             
32231             return pos;
32232             
32233         }
32234         
32235         pos.push({
32236             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32237             y : minY
32238         });
32239
32240         pos.push({
32241             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32242             y : minY + (this.unitWidth + this.gutter) * 2
32243         });
32244         
32245         return pos;
32246         
32247     },
32248     
32249     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32250     {
32251         var pos = [];
32252         
32253         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32254             
32255             pos.push({
32256                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32257                 y : minY
32258             });
32259
32260             pos.push({
32261                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32262                 y : minY + (this.unitWidth + this.gutter) * 1
32263             });
32264             
32265             pos.push({
32266                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32267                 y : minY + (this.unitWidth + this.gutter) * 2
32268             });
32269             
32270             return pos;
32271             
32272         }
32273         
32274         if(box[0].size == 'xs' && box[1].size == 'xs'){
32275             
32276             pos.push({
32277                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32278                 y : minY
32279             });
32280
32281             pos.push({
32282                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32283                 y : minY
32284             });
32285             
32286             pos.push({
32287                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32288                 y : minY + (this.unitWidth + this.gutter) * 1
32289             });
32290             
32291             return pos;
32292             
32293         }
32294         
32295         pos.push({
32296             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32297             y : minY
32298         });
32299
32300         pos.push({
32301             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32302             y : minY + (this.unitWidth + this.gutter) * 2
32303         });
32304
32305         pos.push({
32306             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32307             y : minY + (this.unitWidth + this.gutter) * 2
32308         });
32309             
32310         return pos;
32311         
32312     },
32313     
32314     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32315     {
32316         var pos = [];
32317         
32318         if(box[0].size == 'xs'){
32319             
32320             pos.push({
32321                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32322                 y : minY
32323             });
32324
32325             pos.push({
32326                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32327                 y : minY
32328             });
32329             
32330             pos.push({
32331                 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),
32332                 y : minY
32333             });
32334             
32335             pos.push({
32336                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32337                 y : minY + (this.unitWidth + this.gutter) * 1
32338             });
32339             
32340             return pos;
32341             
32342         }
32343         
32344         pos.push({
32345             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32346             y : minY
32347         });
32348         
32349         pos.push({
32350             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32351             y : minY + (this.unitWidth + this.gutter) * 2
32352         });
32353         
32354         pos.push({
32355             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32356             y : minY + (this.unitWidth + this.gutter) * 2
32357         });
32358         
32359         pos.push({
32360             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),
32361             y : minY + (this.unitWidth + this.gutter) * 2
32362         });
32363
32364         return pos;
32365         
32366     },
32367     
32368     /**
32369     * remove a Masonry Brick
32370     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32371     */
32372     removeBrick : function(brick_id)
32373     {
32374         if (!brick_id) {
32375             return;
32376         }
32377         
32378         for (var i = 0; i<this.bricks.length; i++) {
32379             if (this.bricks[i].id == brick_id) {
32380                 this.bricks.splice(i,1);
32381                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32382                 this.initial();
32383             }
32384         }
32385     },
32386     
32387     /**
32388     * adds a Masonry Brick
32389     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32390     */
32391     addBrick : function(cfg)
32392     {
32393         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32394         //this.register(cn);
32395         cn.parentId = this.id;
32396         cn.render(this.el);
32397         return cn;
32398     },
32399     
32400     /**
32401     * register a Masonry Brick
32402     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32403     */
32404     
32405     register : function(brick)
32406     {
32407         this.bricks.push(brick);
32408         brick.masonryId = this.id;
32409     },
32410     
32411     /**
32412     * clear all the Masonry Brick
32413     */
32414     clearAll : function()
32415     {
32416         this.bricks = [];
32417         //this.getChildContainer().dom.innerHTML = "";
32418         this.el.dom.innerHTML = '';
32419     },
32420     
32421     getSelected : function()
32422     {
32423         if (!this.selectedBrick) {
32424             return false;
32425         }
32426         
32427         return this.selectedBrick;
32428     }
32429 });
32430
32431 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32432     
32433     groups: {},
32434      /**
32435     * register a Masonry Layout
32436     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32437     */
32438     
32439     register : function(layout)
32440     {
32441         this.groups[layout.id] = layout;
32442     },
32443     /**
32444     * fetch a  Masonry Layout based on the masonry layout ID
32445     * @param {string} the masonry layout to add
32446     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32447     */
32448     
32449     get: function(layout_id) {
32450         if (typeof(this.groups[layout_id]) == 'undefined') {
32451             return false;
32452         }
32453         return this.groups[layout_id] ;
32454     }
32455     
32456     
32457     
32458 });
32459
32460  
32461
32462  /**
32463  *
32464  * This is based on 
32465  * http://masonry.desandro.com
32466  *
32467  * The idea is to render all the bricks based on vertical width...
32468  *
32469  * The original code extends 'outlayer' - we might need to use that....
32470  * 
32471  */
32472
32473
32474 /**
32475  * @class Roo.bootstrap.LayoutMasonryAuto
32476  * @extends Roo.bootstrap.Component
32477  * Bootstrap Layout Masonry class
32478  * 
32479  * @constructor
32480  * Create a new Element
32481  * @param {Object} config The config object
32482  */
32483
32484 Roo.bootstrap.LayoutMasonryAuto = function(config){
32485     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32486 };
32487
32488 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32489     
32490       /**
32491      * @cfg {Boolean} isFitWidth  - resize the width..
32492      */   
32493     isFitWidth : false,  // options..
32494     /**
32495      * @cfg {Boolean} isOriginLeft = left align?
32496      */   
32497     isOriginLeft : true,
32498     /**
32499      * @cfg {Boolean} isOriginTop = top align?
32500      */   
32501     isOriginTop : false,
32502     /**
32503      * @cfg {Boolean} isLayoutInstant = no animation?
32504      */   
32505     isLayoutInstant : false, // needed?
32506     /**
32507      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32508      */   
32509     isResizingContainer : true,
32510     /**
32511      * @cfg {Number} columnWidth  width of the columns 
32512      */   
32513     
32514     columnWidth : 0,
32515     
32516     /**
32517      * @cfg {Number} maxCols maximum number of columns
32518      */   
32519     
32520     maxCols: 0,
32521     /**
32522      * @cfg {Number} padHeight padding below box..
32523      */   
32524     
32525     padHeight : 10, 
32526     
32527     /**
32528      * @cfg {Boolean} isAutoInitial defalut true
32529      */   
32530     
32531     isAutoInitial : true, 
32532     
32533     // private?
32534     gutter : 0,
32535     
32536     containerWidth: 0,
32537     initialColumnWidth : 0,
32538     currentSize : null,
32539     
32540     colYs : null, // array.
32541     maxY : 0,
32542     padWidth: 10,
32543     
32544     
32545     tag: 'div',
32546     cls: '',
32547     bricks: null, //CompositeElement
32548     cols : 0, // array?
32549     // element : null, // wrapped now this.el
32550     _isLayoutInited : null, 
32551     
32552     
32553     getAutoCreate : function(){
32554         
32555         var cfg = {
32556             tag: this.tag,
32557             cls: 'blog-masonary-wrapper ' + this.cls,
32558             cn : {
32559                 cls : 'mas-boxes masonary'
32560             }
32561         };
32562         
32563         return cfg;
32564     },
32565     
32566     getChildContainer: function( )
32567     {
32568         if (this.boxesEl) {
32569             return this.boxesEl;
32570         }
32571         
32572         this.boxesEl = this.el.select('.mas-boxes').first();
32573         
32574         return this.boxesEl;
32575     },
32576     
32577     
32578     initEvents : function()
32579     {
32580         var _this = this;
32581         
32582         if(this.isAutoInitial){
32583             Roo.log('hook children rendered');
32584             this.on('childrenrendered', function() {
32585                 Roo.log('children rendered');
32586                 _this.initial();
32587             } ,this);
32588         }
32589         
32590     },
32591     
32592     initial : function()
32593     {
32594         this.reloadItems();
32595
32596         this.currentSize = this.el.getBox(true);
32597
32598         /// was window resize... - let's see if this works..
32599         Roo.EventManager.onWindowResize(this.resize, this); 
32600
32601         if(!this.isAutoInitial){
32602             this.layout();
32603             return;
32604         }
32605         
32606         this.layout.defer(500,this);
32607     },
32608     
32609     reloadItems: function()
32610     {
32611         this.bricks = this.el.select('.masonry-brick', true);
32612         
32613         this.bricks.each(function(b) {
32614             //Roo.log(b.getSize());
32615             if (!b.attr('originalwidth')) {
32616                 b.attr('originalwidth',  b.getSize().width);
32617             }
32618             
32619         });
32620         
32621         Roo.log(this.bricks.elements.length);
32622     },
32623     
32624     resize : function()
32625     {
32626         Roo.log('resize');
32627         var cs = this.el.getBox(true);
32628         
32629         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32630             Roo.log("no change in with or X");
32631             return;
32632         }
32633         this.currentSize = cs;
32634         this.layout();
32635     },
32636     
32637     layout : function()
32638     {
32639          Roo.log('layout');
32640         this._resetLayout();
32641         //this._manageStamps();
32642       
32643         // don't animate first layout
32644         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32645         this.layoutItems( isInstant );
32646       
32647         // flag for initalized
32648         this._isLayoutInited = true;
32649     },
32650     
32651     layoutItems : function( isInstant )
32652     {
32653         //var items = this._getItemsForLayout( this.items );
32654         // original code supports filtering layout items.. we just ignore it..
32655         
32656         this._layoutItems( this.bricks , isInstant );
32657       
32658         this._postLayout();
32659     },
32660     _layoutItems : function ( items , isInstant)
32661     {
32662        //this.fireEvent( 'layout', this, items );
32663     
32664
32665         if ( !items || !items.elements.length ) {
32666           // no items, emit event with empty array
32667             return;
32668         }
32669
32670         var queue = [];
32671         items.each(function(item) {
32672             Roo.log("layout item");
32673             Roo.log(item);
32674             // get x/y object from method
32675             var position = this._getItemLayoutPosition( item );
32676             // enqueue
32677             position.item = item;
32678             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32679             queue.push( position );
32680         }, this);
32681       
32682         this._processLayoutQueue( queue );
32683     },
32684     /** Sets position of item in DOM
32685     * @param {Element} item
32686     * @param {Number} x - horizontal position
32687     * @param {Number} y - vertical position
32688     * @param {Boolean} isInstant - disables transitions
32689     */
32690     _processLayoutQueue : function( queue )
32691     {
32692         for ( var i=0, len = queue.length; i < len; i++ ) {
32693             var obj = queue[i];
32694             obj.item.position('absolute');
32695             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32696         }
32697     },
32698       
32699     
32700     /**
32701     * Any logic you want to do after each layout,
32702     * i.e. size the container
32703     */
32704     _postLayout : function()
32705     {
32706         this.resizeContainer();
32707     },
32708     
32709     resizeContainer : function()
32710     {
32711         if ( !this.isResizingContainer ) {
32712             return;
32713         }
32714         var size = this._getContainerSize();
32715         if ( size ) {
32716             this.el.setSize(size.width,size.height);
32717             this.boxesEl.setSize(size.width,size.height);
32718         }
32719     },
32720     
32721     
32722     
32723     _resetLayout : function()
32724     {
32725         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32726         this.colWidth = this.el.getWidth();
32727         //this.gutter = this.el.getWidth(); 
32728         
32729         this.measureColumns();
32730
32731         // reset column Y
32732         var i = this.cols;
32733         this.colYs = [];
32734         while (i--) {
32735             this.colYs.push( 0 );
32736         }
32737     
32738         this.maxY = 0;
32739     },
32740
32741     measureColumns : function()
32742     {
32743         this.getContainerWidth();
32744       // if columnWidth is 0, default to outerWidth of first item
32745         if ( !this.columnWidth ) {
32746             var firstItem = this.bricks.first();
32747             Roo.log(firstItem);
32748             this.columnWidth  = this.containerWidth;
32749             if (firstItem && firstItem.attr('originalwidth') ) {
32750                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32751             }
32752             // columnWidth fall back to item of first element
32753             Roo.log("set column width?");
32754                         this.initialColumnWidth = this.columnWidth  ;
32755
32756             // if first elem has no width, default to size of container
32757             
32758         }
32759         
32760         
32761         if (this.initialColumnWidth) {
32762             this.columnWidth = this.initialColumnWidth;
32763         }
32764         
32765         
32766             
32767         // column width is fixed at the top - however if container width get's smaller we should
32768         // reduce it...
32769         
32770         // this bit calcs how man columns..
32771             
32772         var columnWidth = this.columnWidth += this.gutter;
32773       
32774         // calculate columns
32775         var containerWidth = this.containerWidth + this.gutter;
32776         
32777         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32778         // fix rounding errors, typically with gutters
32779         var excess = columnWidth - containerWidth % columnWidth;
32780         
32781         
32782         // if overshoot is less than a pixel, round up, otherwise floor it
32783         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32784         cols = Math[ mathMethod ]( cols );
32785         this.cols = Math.max( cols, 1 );
32786         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32787         
32788          // padding positioning..
32789         var totalColWidth = this.cols * this.columnWidth;
32790         var padavail = this.containerWidth - totalColWidth;
32791         // so for 2 columns - we need 3 'pads'
32792         
32793         var padNeeded = (1+this.cols) * this.padWidth;
32794         
32795         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32796         
32797         this.columnWidth += padExtra
32798         //this.padWidth = Math.floor(padavail /  ( this.cols));
32799         
32800         // adjust colum width so that padding is fixed??
32801         
32802         // we have 3 columns ... total = width * 3
32803         // we have X left over... that should be used by 
32804         
32805         //if (this.expandC) {
32806             
32807         //}
32808         
32809         
32810         
32811     },
32812     
32813     getContainerWidth : function()
32814     {
32815        /* // container is parent if fit width
32816         var container = this.isFitWidth ? this.element.parentNode : this.element;
32817         // check that this.size and size are there
32818         // IE8 triggers resize on body size change, so they might not be
32819         
32820         var size = getSize( container );  //FIXME
32821         this.containerWidth = size && size.innerWidth; //FIXME
32822         */
32823          
32824         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32825         
32826     },
32827     
32828     _getItemLayoutPosition : function( item )  // what is item?
32829     {
32830         // we resize the item to our columnWidth..
32831       
32832         item.setWidth(this.columnWidth);
32833         item.autoBoxAdjust  = false;
32834         
32835         var sz = item.getSize();
32836  
32837         // how many columns does this brick span
32838         var remainder = this.containerWidth % this.columnWidth;
32839         
32840         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32841         // round if off by 1 pixel, otherwise use ceil
32842         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32843         colSpan = Math.min( colSpan, this.cols );
32844         
32845         // normally this should be '1' as we dont' currently allow multi width columns..
32846         
32847         var colGroup = this._getColGroup( colSpan );
32848         // get the minimum Y value from the columns
32849         var minimumY = Math.min.apply( Math, colGroup );
32850         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32851         
32852         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32853          
32854         // position the brick
32855         var position = {
32856             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32857             y: this.currentSize.y + minimumY + this.padHeight
32858         };
32859         
32860         Roo.log(position);
32861         // apply setHeight to necessary columns
32862         var setHeight = minimumY + sz.height + this.padHeight;
32863         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32864         
32865         var setSpan = this.cols + 1 - colGroup.length;
32866         for ( var i = 0; i < setSpan; i++ ) {
32867           this.colYs[ shortColIndex + i ] = setHeight ;
32868         }
32869       
32870         return position;
32871     },
32872     
32873     /**
32874      * @param {Number} colSpan - number of columns the element spans
32875      * @returns {Array} colGroup
32876      */
32877     _getColGroup : function( colSpan )
32878     {
32879         if ( colSpan < 2 ) {
32880           // if brick spans only one column, use all the column Ys
32881           return this.colYs;
32882         }
32883       
32884         var colGroup = [];
32885         // how many different places could this brick fit horizontally
32886         var groupCount = this.cols + 1 - colSpan;
32887         // for each group potential horizontal position
32888         for ( var i = 0; i < groupCount; i++ ) {
32889           // make an array of colY values for that one group
32890           var groupColYs = this.colYs.slice( i, i + colSpan );
32891           // and get the max value of the array
32892           colGroup[i] = Math.max.apply( Math, groupColYs );
32893         }
32894         return colGroup;
32895     },
32896     /*
32897     _manageStamp : function( stamp )
32898     {
32899         var stampSize =  stamp.getSize();
32900         var offset = stamp.getBox();
32901         // get the columns that this stamp affects
32902         var firstX = this.isOriginLeft ? offset.x : offset.right;
32903         var lastX = firstX + stampSize.width;
32904         var firstCol = Math.floor( firstX / this.columnWidth );
32905         firstCol = Math.max( 0, firstCol );
32906         
32907         var lastCol = Math.floor( lastX / this.columnWidth );
32908         // lastCol should not go over if multiple of columnWidth #425
32909         lastCol -= lastX % this.columnWidth ? 0 : 1;
32910         lastCol = Math.min( this.cols - 1, lastCol );
32911         
32912         // set colYs to bottom of the stamp
32913         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32914             stampSize.height;
32915             
32916         for ( var i = firstCol; i <= lastCol; i++ ) {
32917           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32918         }
32919     },
32920     */
32921     
32922     _getContainerSize : function()
32923     {
32924         this.maxY = Math.max.apply( Math, this.colYs );
32925         var size = {
32926             height: this.maxY
32927         };
32928       
32929         if ( this.isFitWidth ) {
32930             size.width = this._getContainerFitWidth();
32931         }
32932       
32933         return size;
32934     },
32935     
32936     _getContainerFitWidth : function()
32937     {
32938         var unusedCols = 0;
32939         // count unused columns
32940         var i = this.cols;
32941         while ( --i ) {
32942           if ( this.colYs[i] !== 0 ) {
32943             break;
32944           }
32945           unusedCols++;
32946         }
32947         // fit container to columns that have been used
32948         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32949     },
32950     
32951     needsResizeLayout : function()
32952     {
32953         var previousWidth = this.containerWidth;
32954         this.getContainerWidth();
32955         return previousWidth !== this.containerWidth;
32956     }
32957  
32958 });
32959
32960  
32961
32962  /*
32963  * - LGPL
32964  *
32965  * element
32966  * 
32967  */
32968
32969 /**
32970  * @class Roo.bootstrap.MasonryBrick
32971  * @extends Roo.bootstrap.Component
32972  * Bootstrap MasonryBrick class
32973  * 
32974  * @constructor
32975  * Create a new MasonryBrick
32976  * @param {Object} config The config object
32977  */
32978
32979 Roo.bootstrap.MasonryBrick = function(config){
32980     
32981     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32982     
32983     Roo.bootstrap.MasonryBrick.register(this);
32984     
32985     this.addEvents({
32986         // raw events
32987         /**
32988          * @event click
32989          * When a MasonryBrick is clcik
32990          * @param {Roo.bootstrap.MasonryBrick} this
32991          * @param {Roo.EventObject} e
32992          */
32993         "click" : true
32994     });
32995 };
32996
32997 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32998     
32999     /**
33000      * @cfg {String} title
33001      */   
33002     title : '',
33003     /**
33004      * @cfg {String} html
33005      */   
33006     html : '',
33007     /**
33008      * @cfg {String} bgimage
33009      */   
33010     bgimage : '',
33011     /**
33012      * @cfg {String} videourl
33013      */   
33014     videourl : '',
33015     /**
33016      * @cfg {String} cls
33017      */   
33018     cls : '',
33019     /**
33020      * @cfg {String} href
33021      */   
33022     href : '',
33023     /**
33024      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
33025      */   
33026     size : 'xs',
33027     
33028     /**
33029      * @cfg {String} placetitle (center|bottom)
33030      */   
33031     placetitle : '',
33032     
33033     /**
33034      * @cfg {Boolean} isFitContainer defalut true
33035      */   
33036     isFitContainer : true, 
33037     
33038     /**
33039      * @cfg {Boolean} preventDefault defalut false
33040      */   
33041     preventDefault : false, 
33042     
33043     /**
33044      * @cfg {Boolean} inverse defalut false
33045      */   
33046     maskInverse : false, 
33047     
33048     getAutoCreate : function()
33049     {
33050         if(!this.isFitContainer){
33051             return this.getSplitAutoCreate();
33052         }
33053         
33054         var cls = 'masonry-brick masonry-brick-full';
33055         
33056         if(this.href.length){
33057             cls += ' masonry-brick-link';
33058         }
33059         
33060         if(this.bgimage.length){
33061             cls += ' masonry-brick-image';
33062         }
33063         
33064         if(this.maskInverse){
33065             cls += ' mask-inverse';
33066         }
33067         
33068         if(!this.html.length && !this.maskInverse && !this.videourl.length){
33069             cls += ' enable-mask';
33070         }
33071         
33072         if(this.size){
33073             cls += ' masonry-' + this.size + '-brick';
33074         }
33075         
33076         if(this.placetitle.length){
33077             
33078             switch (this.placetitle) {
33079                 case 'center' :
33080                     cls += ' masonry-center-title';
33081                     break;
33082                 case 'bottom' :
33083                     cls += ' masonry-bottom-title';
33084                     break;
33085                 default:
33086                     break;
33087             }
33088             
33089         } else {
33090             if(!this.html.length && !this.bgimage.length){
33091                 cls += ' masonry-center-title';
33092             }
33093
33094             if(!this.html.length && this.bgimage.length){
33095                 cls += ' masonry-bottom-title';
33096             }
33097         }
33098         
33099         if(this.cls){
33100             cls += ' ' + this.cls;
33101         }
33102         
33103         var cfg = {
33104             tag: (this.href.length) ? 'a' : 'div',
33105             cls: cls,
33106             cn: [
33107                 {
33108                     tag: 'div',
33109                     cls: 'masonry-brick-mask'
33110                 },
33111                 {
33112                     tag: 'div',
33113                     cls: 'masonry-brick-paragraph',
33114                     cn: []
33115                 }
33116             ]
33117         };
33118         
33119         if(this.href.length){
33120             cfg.href = this.href;
33121         }
33122         
33123         var cn = cfg.cn[1].cn;
33124         
33125         if(this.title.length){
33126             cn.push({
33127                 tag: 'h4',
33128                 cls: 'masonry-brick-title',
33129                 html: this.title
33130             });
33131         }
33132         
33133         if(this.html.length){
33134             cn.push({
33135                 tag: 'p',
33136                 cls: 'masonry-brick-text',
33137                 html: this.html
33138             });
33139         }
33140         
33141         if (!this.title.length && !this.html.length) {
33142             cfg.cn[1].cls += ' hide';
33143         }
33144         
33145         if(this.bgimage.length){
33146             cfg.cn.push({
33147                 tag: 'img',
33148                 cls: 'masonry-brick-image-view',
33149                 src: this.bgimage
33150             });
33151         }
33152         
33153         if(this.videourl.length){
33154             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33155             // youtube support only?
33156             cfg.cn.push({
33157                 tag: 'iframe',
33158                 cls: 'masonry-brick-image-view',
33159                 src: vurl,
33160                 frameborder : 0,
33161                 allowfullscreen : true
33162             });
33163         }
33164         
33165         return cfg;
33166         
33167     },
33168     
33169     getSplitAutoCreate : function()
33170     {
33171         var cls = 'masonry-brick masonry-brick-split';
33172         
33173         if(this.href.length){
33174             cls += ' masonry-brick-link';
33175         }
33176         
33177         if(this.bgimage.length){
33178             cls += ' masonry-brick-image';
33179         }
33180         
33181         if(this.size){
33182             cls += ' masonry-' + this.size + '-brick';
33183         }
33184         
33185         switch (this.placetitle) {
33186             case 'center' :
33187                 cls += ' masonry-center-title';
33188                 break;
33189             case 'bottom' :
33190                 cls += ' masonry-bottom-title';
33191                 break;
33192             default:
33193                 if(!this.bgimage.length){
33194                     cls += ' masonry-center-title';
33195                 }
33196
33197                 if(this.bgimage.length){
33198                     cls += ' masonry-bottom-title';
33199                 }
33200                 break;
33201         }
33202         
33203         if(this.cls){
33204             cls += ' ' + this.cls;
33205         }
33206         
33207         var cfg = {
33208             tag: (this.href.length) ? 'a' : 'div',
33209             cls: cls,
33210             cn: [
33211                 {
33212                     tag: 'div',
33213                     cls: 'masonry-brick-split-head',
33214                     cn: [
33215                         {
33216                             tag: 'div',
33217                             cls: 'masonry-brick-paragraph',
33218                             cn: []
33219                         }
33220                     ]
33221                 },
33222                 {
33223                     tag: 'div',
33224                     cls: 'masonry-brick-split-body',
33225                     cn: []
33226                 }
33227             ]
33228         };
33229         
33230         if(this.href.length){
33231             cfg.href = this.href;
33232         }
33233         
33234         if(this.title.length){
33235             cfg.cn[0].cn[0].cn.push({
33236                 tag: 'h4',
33237                 cls: 'masonry-brick-title',
33238                 html: this.title
33239             });
33240         }
33241         
33242         if(this.html.length){
33243             cfg.cn[1].cn.push({
33244                 tag: 'p',
33245                 cls: 'masonry-brick-text',
33246                 html: this.html
33247             });
33248         }
33249
33250         if(this.bgimage.length){
33251             cfg.cn[0].cn.push({
33252                 tag: 'img',
33253                 cls: 'masonry-brick-image-view',
33254                 src: this.bgimage
33255             });
33256         }
33257         
33258         if(this.videourl.length){
33259             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33260             // youtube support only?
33261             cfg.cn[0].cn.cn.push({
33262                 tag: 'iframe',
33263                 cls: 'masonry-brick-image-view',
33264                 src: vurl,
33265                 frameborder : 0,
33266                 allowfullscreen : true
33267             });
33268         }
33269         
33270         return cfg;
33271     },
33272     
33273     initEvents: function() 
33274     {
33275         switch (this.size) {
33276             case 'xs' :
33277                 this.x = 1;
33278                 this.y = 1;
33279                 break;
33280             case 'sm' :
33281                 this.x = 2;
33282                 this.y = 2;
33283                 break;
33284             case 'md' :
33285             case 'md-left' :
33286             case 'md-right' :
33287                 this.x = 3;
33288                 this.y = 3;
33289                 break;
33290             case 'tall' :
33291                 this.x = 2;
33292                 this.y = 3;
33293                 break;
33294             case 'wide' :
33295                 this.x = 3;
33296                 this.y = 2;
33297                 break;
33298             case 'wide-thin' :
33299                 this.x = 3;
33300                 this.y = 1;
33301                 break;
33302                         
33303             default :
33304                 break;
33305         }
33306         
33307         if(Roo.isTouch){
33308             this.el.on('touchstart', this.onTouchStart, this);
33309             this.el.on('touchmove', this.onTouchMove, this);
33310             this.el.on('touchend', this.onTouchEnd, this);
33311             this.el.on('contextmenu', this.onContextMenu, this);
33312         } else {
33313             this.el.on('mouseenter'  ,this.enter, this);
33314             this.el.on('mouseleave', this.leave, this);
33315             this.el.on('click', this.onClick, this);
33316         }
33317         
33318         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33319             this.parent().bricks.push(this);   
33320         }
33321         
33322     },
33323     
33324     onClick: function(e, el)
33325     {
33326         var time = this.endTimer - this.startTimer;
33327         // Roo.log(e.preventDefault());
33328         if(Roo.isTouch){
33329             if(time > 1000){
33330                 e.preventDefault();
33331                 return;
33332             }
33333         }
33334         
33335         if(!this.preventDefault){
33336             return;
33337         }
33338         
33339         e.preventDefault();
33340         
33341         if (this.activeClass != '') {
33342             this.selectBrick();
33343         }
33344         
33345         this.fireEvent('click', this, e);
33346     },
33347     
33348     enter: function(e, el)
33349     {
33350         e.preventDefault();
33351         
33352         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33353             return;
33354         }
33355         
33356         if(this.bgimage.length && this.html.length){
33357             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33358         }
33359     },
33360     
33361     leave: function(e, el)
33362     {
33363         e.preventDefault();
33364         
33365         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33366             return;
33367         }
33368         
33369         if(this.bgimage.length && this.html.length){
33370             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33371         }
33372     },
33373     
33374     onTouchStart: function(e, el)
33375     {
33376 //        e.preventDefault();
33377         
33378         this.touchmoved = false;
33379         
33380         if(!this.isFitContainer){
33381             return;
33382         }
33383         
33384         if(!this.bgimage.length || !this.html.length){
33385             return;
33386         }
33387         
33388         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33389         
33390         this.timer = new Date().getTime();
33391         
33392     },
33393     
33394     onTouchMove: function(e, el)
33395     {
33396         this.touchmoved = true;
33397     },
33398     
33399     onContextMenu : function(e,el)
33400     {
33401         e.preventDefault();
33402         e.stopPropagation();
33403         return false;
33404     },
33405     
33406     onTouchEnd: function(e, el)
33407     {
33408 //        e.preventDefault();
33409         
33410         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33411         
33412             this.leave(e,el);
33413             
33414             return;
33415         }
33416         
33417         if(!this.bgimage.length || !this.html.length){
33418             
33419             if(this.href.length){
33420                 window.location.href = this.href;
33421             }
33422             
33423             return;
33424         }
33425         
33426         if(!this.isFitContainer){
33427             return;
33428         }
33429         
33430         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33431         
33432         window.location.href = this.href;
33433     },
33434     
33435     //selection on single brick only
33436     selectBrick : function() {
33437         
33438         if (!this.parentId) {
33439             return;
33440         }
33441         
33442         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33443         var index = m.selectedBrick.indexOf(this.id);
33444         
33445         if ( index > -1) {
33446             m.selectedBrick.splice(index,1);
33447             this.el.removeClass(this.activeClass);
33448             return;
33449         }
33450         
33451         for(var i = 0; i < m.selectedBrick.length; i++) {
33452             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33453             b.el.removeClass(b.activeClass);
33454         }
33455         
33456         m.selectedBrick = [];
33457         
33458         m.selectedBrick.push(this.id);
33459         this.el.addClass(this.activeClass);
33460         return;
33461     },
33462     
33463     isSelected : function(){
33464         return this.el.hasClass(this.activeClass);
33465         
33466     }
33467 });
33468
33469 Roo.apply(Roo.bootstrap.MasonryBrick, {
33470     
33471     //groups: {},
33472     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33473      /**
33474     * register a Masonry Brick
33475     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33476     */
33477     
33478     register : function(brick)
33479     {
33480         //this.groups[brick.id] = brick;
33481         this.groups.add(brick.id, brick);
33482     },
33483     /**
33484     * fetch a  masonry brick based on the masonry brick ID
33485     * @param {string} the masonry brick to add
33486     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33487     */
33488     
33489     get: function(brick_id) 
33490     {
33491         // if (typeof(this.groups[brick_id]) == 'undefined') {
33492         //     return false;
33493         // }
33494         // return this.groups[brick_id] ;
33495         
33496         if(this.groups.key(brick_id)) {
33497             return this.groups.key(brick_id);
33498         }
33499         
33500         return false;
33501     }
33502     
33503     
33504     
33505 });
33506
33507  /*
33508  * - LGPL
33509  *
33510  * element
33511  * 
33512  */
33513
33514 /**
33515  * @class Roo.bootstrap.Brick
33516  * @extends Roo.bootstrap.Component
33517  * Bootstrap Brick class
33518  * 
33519  * @constructor
33520  * Create a new Brick
33521  * @param {Object} config The config object
33522  */
33523
33524 Roo.bootstrap.Brick = function(config){
33525     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33526     
33527     this.addEvents({
33528         // raw events
33529         /**
33530          * @event click
33531          * When a Brick is click
33532          * @param {Roo.bootstrap.Brick} this
33533          * @param {Roo.EventObject} e
33534          */
33535         "click" : true
33536     });
33537 };
33538
33539 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33540     
33541     /**
33542      * @cfg {String} title
33543      */   
33544     title : '',
33545     /**
33546      * @cfg {String} html
33547      */   
33548     html : '',
33549     /**
33550      * @cfg {String} bgimage
33551      */   
33552     bgimage : '',
33553     /**
33554      * @cfg {String} cls
33555      */   
33556     cls : '',
33557     /**
33558      * @cfg {String} href
33559      */   
33560     href : '',
33561     /**
33562      * @cfg {String} video
33563      */   
33564     video : '',
33565     /**
33566      * @cfg {Boolean} square
33567      */   
33568     square : true,
33569     
33570     getAutoCreate : function()
33571     {
33572         var cls = 'roo-brick';
33573         
33574         if(this.href.length){
33575             cls += ' roo-brick-link';
33576         }
33577         
33578         if(this.bgimage.length){
33579             cls += ' roo-brick-image';
33580         }
33581         
33582         if(!this.html.length && !this.bgimage.length){
33583             cls += ' roo-brick-center-title';
33584         }
33585         
33586         if(!this.html.length && this.bgimage.length){
33587             cls += ' roo-brick-bottom-title';
33588         }
33589         
33590         if(this.cls){
33591             cls += ' ' + this.cls;
33592         }
33593         
33594         var cfg = {
33595             tag: (this.href.length) ? 'a' : 'div',
33596             cls: cls,
33597             cn: [
33598                 {
33599                     tag: 'div',
33600                     cls: 'roo-brick-paragraph',
33601                     cn: []
33602                 }
33603             ]
33604         };
33605         
33606         if(this.href.length){
33607             cfg.href = this.href;
33608         }
33609         
33610         var cn = cfg.cn[0].cn;
33611         
33612         if(this.title.length){
33613             cn.push({
33614                 tag: 'h4',
33615                 cls: 'roo-brick-title',
33616                 html: this.title
33617             });
33618         }
33619         
33620         if(this.html.length){
33621             cn.push({
33622                 tag: 'p',
33623                 cls: 'roo-brick-text',
33624                 html: this.html
33625             });
33626         } else {
33627             cn.cls += ' hide';
33628         }
33629         
33630         if(this.bgimage.length){
33631             cfg.cn.push({
33632                 tag: 'img',
33633                 cls: 'roo-brick-image-view',
33634                 src: this.bgimage
33635             });
33636         }
33637         
33638         return cfg;
33639     },
33640     
33641     initEvents: function() 
33642     {
33643         if(this.title.length || this.html.length){
33644             this.el.on('mouseenter'  ,this.enter, this);
33645             this.el.on('mouseleave', this.leave, this);
33646         }
33647         
33648         Roo.EventManager.onWindowResize(this.resize, this); 
33649         
33650         if(this.bgimage.length){
33651             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33652             this.imageEl.on('load', this.onImageLoad, this);
33653             return;
33654         }
33655         
33656         this.resize();
33657     },
33658     
33659     onImageLoad : function()
33660     {
33661         this.resize();
33662     },
33663     
33664     resize : function()
33665     {
33666         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33667         
33668         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33669         
33670         if(this.bgimage.length){
33671             var image = this.el.select('.roo-brick-image-view', true).first();
33672             
33673             image.setWidth(paragraph.getWidth());
33674             
33675             if(this.square){
33676                 image.setHeight(paragraph.getWidth());
33677             }
33678             
33679             this.el.setHeight(image.getHeight());
33680             paragraph.setHeight(image.getHeight());
33681             
33682         }
33683         
33684     },
33685     
33686     enter: function(e, el)
33687     {
33688         e.preventDefault();
33689         
33690         if(this.bgimage.length){
33691             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33692             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33693         }
33694     },
33695     
33696     leave: function(e, el)
33697     {
33698         e.preventDefault();
33699         
33700         if(this.bgimage.length){
33701             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33702             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33703         }
33704     }
33705     
33706 });
33707
33708  
33709
33710  /*
33711  * - LGPL
33712  *
33713  * Number field 
33714  */
33715
33716 /**
33717  * @class Roo.bootstrap.NumberField
33718  * @extends Roo.bootstrap.Input
33719  * Bootstrap NumberField class
33720  * 
33721  * 
33722  * 
33723  * 
33724  * @constructor
33725  * Create a new NumberField
33726  * @param {Object} config The config object
33727  */
33728
33729 Roo.bootstrap.NumberField = function(config){
33730     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33731 };
33732
33733 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33734     
33735     /**
33736      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33737      */
33738     allowDecimals : true,
33739     /**
33740      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33741      */
33742     decimalSeparator : ".",
33743     /**
33744      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33745      */
33746     decimalPrecision : 2,
33747     /**
33748      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33749      */
33750     allowNegative : true,
33751     
33752     /**
33753      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33754      */
33755     allowZero: true,
33756     /**
33757      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33758      */
33759     minValue : Number.NEGATIVE_INFINITY,
33760     /**
33761      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33762      */
33763     maxValue : Number.MAX_VALUE,
33764     /**
33765      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33766      */
33767     minText : "The minimum value for this field is {0}",
33768     /**
33769      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33770      */
33771     maxText : "The maximum value for this field is {0}",
33772     /**
33773      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33774      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33775      */
33776     nanText : "{0} is not a valid number",
33777     /**
33778      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33779      */
33780     thousandsDelimiter : false,
33781     /**
33782      * @cfg {String} valueAlign alignment of value
33783      */
33784     valueAlign : "left",
33785
33786     getAutoCreate : function()
33787     {
33788         var hiddenInput = {
33789             tag: 'input',
33790             type: 'hidden',
33791             id: Roo.id(),
33792             cls: 'hidden-number-input'
33793         };
33794         
33795         if (this.name) {
33796             hiddenInput.name = this.name;
33797         }
33798         
33799         this.name = '';
33800         
33801         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33802         
33803         this.name = hiddenInput.name;
33804         
33805         if(cfg.cn.length > 0) {
33806             cfg.cn.push(hiddenInput);
33807         }
33808         
33809         return cfg;
33810     },
33811
33812     // private
33813     initEvents : function()
33814     {   
33815         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33816         
33817         var allowed = "0123456789";
33818         
33819         if(this.allowDecimals){
33820             allowed += this.decimalSeparator;
33821         }
33822         
33823         if(this.allowNegative){
33824             allowed += "-";
33825         }
33826         
33827         if(this.thousandsDelimiter) {
33828             allowed += ",";
33829         }
33830         
33831         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33832         
33833         var keyPress = function(e){
33834             
33835             var k = e.getKey();
33836             
33837             var c = e.getCharCode();
33838             
33839             if(
33840                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33841                     allowed.indexOf(String.fromCharCode(c)) === -1
33842             ){
33843                 e.stopEvent();
33844                 return;
33845             }
33846             
33847             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33848                 return;
33849             }
33850             
33851             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33852                 e.stopEvent();
33853             }
33854         };
33855         
33856         this.el.on("keypress", keyPress, this);
33857     },
33858     
33859     validateValue : function(value)
33860     {
33861         
33862         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33863             return false;
33864         }
33865         
33866         var num = this.parseValue(value);
33867         
33868         if(isNaN(num)){
33869             this.markInvalid(String.format(this.nanText, value));
33870             return false;
33871         }
33872         
33873         if(num < this.minValue){
33874             this.markInvalid(String.format(this.minText, this.minValue));
33875             return false;
33876         }
33877         
33878         if(num > this.maxValue){
33879             this.markInvalid(String.format(this.maxText, this.maxValue));
33880             return false;
33881         }
33882         
33883         return true;
33884     },
33885
33886     getValue : function()
33887     {
33888         var v = this.hiddenEl().getValue();
33889         
33890         return this.fixPrecision(this.parseValue(v));
33891     },
33892
33893     parseValue : function(value)
33894     {
33895         if(this.thousandsDelimiter) {
33896             value += "";
33897             r = new RegExp(",", "g");
33898             value = value.replace(r, "");
33899         }
33900         
33901         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33902         return isNaN(value) ? '' : value;
33903     },
33904
33905     fixPrecision : function(value)
33906     {
33907         if(this.thousandsDelimiter) {
33908             value += "";
33909             r = new RegExp(",", "g");
33910             value = value.replace(r, "");
33911         }
33912         
33913         var nan = isNaN(value);
33914         
33915         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33916             return nan ? '' : value;
33917         }
33918         return parseFloat(value).toFixed(this.decimalPrecision);
33919     },
33920
33921     setValue : function(v)
33922     {
33923         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33924         
33925         this.value = v;
33926         
33927         if(this.rendered){
33928             
33929             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33930             
33931             this.inputEl().dom.value = (v == '') ? '' :
33932                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33933             
33934             if(!this.allowZero && v === '0') {
33935                 this.hiddenEl().dom.value = '';
33936                 this.inputEl().dom.value = '';
33937             }
33938             
33939             this.validate();
33940         }
33941     },
33942
33943     decimalPrecisionFcn : function(v)
33944     {
33945         return Math.floor(v);
33946     },
33947
33948     beforeBlur : function()
33949     {
33950         var v = this.parseValue(this.getRawValue());
33951         
33952         if(v || v === 0 || v === ''){
33953             this.setValue(v);
33954         }
33955     },
33956     
33957     hiddenEl : function()
33958     {
33959         return this.el.select('input.hidden-number-input',true).first();
33960     }
33961     
33962 });
33963
33964  
33965
33966 /*
33967 * Licence: LGPL
33968 */
33969
33970 /**
33971  * @class Roo.bootstrap.DocumentSlider
33972  * @extends Roo.bootstrap.Component
33973  * Bootstrap DocumentSlider class
33974  * 
33975  * @constructor
33976  * Create a new DocumentViewer
33977  * @param {Object} config The config object
33978  */
33979
33980 Roo.bootstrap.DocumentSlider = function(config){
33981     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33982     
33983     this.files = [];
33984     
33985     this.addEvents({
33986         /**
33987          * @event initial
33988          * Fire after initEvent
33989          * @param {Roo.bootstrap.DocumentSlider} this
33990          */
33991         "initial" : true,
33992         /**
33993          * @event update
33994          * Fire after update
33995          * @param {Roo.bootstrap.DocumentSlider} this
33996          */
33997         "update" : true,
33998         /**
33999          * @event click
34000          * Fire after click
34001          * @param {Roo.bootstrap.DocumentSlider} this
34002          */
34003         "click" : true
34004     });
34005 };
34006
34007 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
34008     
34009     files : false,
34010     
34011     indicator : 0,
34012     
34013     getAutoCreate : function()
34014     {
34015         var cfg = {
34016             tag : 'div',
34017             cls : 'roo-document-slider',
34018             cn : [
34019                 {
34020                     tag : 'div',
34021                     cls : 'roo-document-slider-header',
34022                     cn : [
34023                         {
34024                             tag : 'div',
34025                             cls : 'roo-document-slider-header-title'
34026                         }
34027                     ]
34028                 },
34029                 {
34030                     tag : 'div',
34031                     cls : 'roo-document-slider-body',
34032                     cn : [
34033                         {
34034                             tag : 'div',
34035                             cls : 'roo-document-slider-prev',
34036                             cn : [
34037                                 {
34038                                     tag : 'i',
34039                                     cls : 'fa fa-chevron-left'
34040                                 }
34041                             ]
34042                         },
34043                         {
34044                             tag : 'div',
34045                             cls : 'roo-document-slider-thumb',
34046                             cn : [
34047                                 {
34048                                     tag : 'img',
34049                                     cls : 'roo-document-slider-image'
34050                                 }
34051                             ]
34052                         },
34053                         {
34054                             tag : 'div',
34055                             cls : 'roo-document-slider-next',
34056                             cn : [
34057                                 {
34058                                     tag : 'i',
34059                                     cls : 'fa fa-chevron-right'
34060                                 }
34061                             ]
34062                         }
34063                     ]
34064                 }
34065             ]
34066         };
34067         
34068         return cfg;
34069     },
34070     
34071     initEvents : function()
34072     {
34073         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
34074         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
34075         
34076         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
34077         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
34078         
34079         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
34080         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
34081         
34082         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
34083         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
34084         
34085         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
34086         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
34087         
34088         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
34089         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34090         
34091         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
34092         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34093         
34094         this.thumbEl.on('click', this.onClick, this);
34095         
34096         this.prevIndicator.on('click', this.prev, this);
34097         
34098         this.nextIndicator.on('click', this.next, this);
34099         
34100     },
34101     
34102     initial : function()
34103     {
34104         if(this.files.length){
34105             this.indicator = 1;
34106             this.update()
34107         }
34108         
34109         this.fireEvent('initial', this);
34110     },
34111     
34112     update : function()
34113     {
34114         this.imageEl.attr('src', this.files[this.indicator - 1]);
34115         
34116         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
34117         
34118         this.prevIndicator.show();
34119         
34120         if(this.indicator == 1){
34121             this.prevIndicator.hide();
34122         }
34123         
34124         this.nextIndicator.show();
34125         
34126         if(this.indicator == this.files.length){
34127             this.nextIndicator.hide();
34128         }
34129         
34130         this.thumbEl.scrollTo('top');
34131         
34132         this.fireEvent('update', this);
34133     },
34134     
34135     onClick : function(e)
34136     {
34137         e.preventDefault();
34138         
34139         this.fireEvent('click', this);
34140     },
34141     
34142     prev : function(e)
34143     {
34144         e.preventDefault();
34145         
34146         this.indicator = Math.max(1, this.indicator - 1);
34147         
34148         this.update();
34149     },
34150     
34151     next : function(e)
34152     {
34153         e.preventDefault();
34154         
34155         this.indicator = Math.min(this.files.length, this.indicator + 1);
34156         
34157         this.update();
34158     }
34159 });
34160 /*
34161  * - LGPL
34162  *
34163  * RadioSet
34164  *
34165  *
34166  */
34167
34168 /**
34169  * @class Roo.bootstrap.RadioSet
34170  * @extends Roo.bootstrap.Input
34171  * Bootstrap RadioSet class
34172  * @cfg {String} indicatorpos (left|right) default left
34173  * @cfg {Boolean} inline (true|false) inline the element (default true)
34174  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
34175  * @constructor
34176  * Create a new RadioSet
34177  * @param {Object} config The config object
34178  */
34179
34180 Roo.bootstrap.RadioSet = function(config){
34181     
34182     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
34183     
34184     this.radioes = [];
34185     
34186     Roo.bootstrap.RadioSet.register(this);
34187     
34188     this.addEvents({
34189         /**
34190         * @event check
34191         * Fires when the element is checked or unchecked.
34192         * @param {Roo.bootstrap.RadioSet} this This radio
34193         * @param {Roo.bootstrap.Radio} item The checked item
34194         */
34195        check : true,
34196        /**
34197         * @event click
34198         * Fires when the element is click.
34199         * @param {Roo.bootstrap.RadioSet} this This radio set
34200         * @param {Roo.bootstrap.Radio} item The checked item
34201         * @param {Roo.EventObject} e The event object
34202         */
34203        click : true
34204     });
34205     
34206 };
34207
34208 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
34209
34210     radioes : false,
34211     
34212     inline : true,
34213     
34214     weight : '',
34215     
34216     indicatorpos : 'left',
34217     
34218     getAutoCreate : function()
34219     {
34220         var label = {
34221             tag : 'label',
34222             cls : 'roo-radio-set-label',
34223             cn : [
34224                 {
34225                     tag : 'span',
34226                     html : this.fieldLabel
34227                 }
34228             ]
34229         };
34230         if (Roo.bootstrap.version == 3) {
34231             
34232             
34233             if(this.indicatorpos == 'left'){
34234                 label.cn.unshift({
34235                     tag : 'i',
34236                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34237                     tooltip : 'This field is required'
34238                 });
34239             } else {
34240                 label.cn.push({
34241                     tag : 'i',
34242                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34243                     tooltip : 'This field is required'
34244                 });
34245             }
34246         }
34247         var items = {
34248             tag : 'div',
34249             cls : 'roo-radio-set-items'
34250         };
34251         
34252         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34253         
34254         if (align === 'left' && this.fieldLabel.length) {
34255             
34256             items = {
34257                 cls : "roo-radio-set-right", 
34258                 cn: [
34259                     items
34260                 ]
34261             };
34262             
34263             if(this.labelWidth > 12){
34264                 label.style = "width: " + this.labelWidth + 'px';
34265             }
34266             
34267             if(this.labelWidth < 13 && this.labelmd == 0){
34268                 this.labelmd = this.labelWidth;
34269             }
34270             
34271             if(this.labellg > 0){
34272                 label.cls += ' col-lg-' + this.labellg;
34273                 items.cls += ' col-lg-' + (12 - this.labellg);
34274             }
34275             
34276             if(this.labelmd > 0){
34277                 label.cls += ' col-md-' + this.labelmd;
34278                 items.cls += ' col-md-' + (12 - this.labelmd);
34279             }
34280             
34281             if(this.labelsm > 0){
34282                 label.cls += ' col-sm-' + this.labelsm;
34283                 items.cls += ' col-sm-' + (12 - this.labelsm);
34284             }
34285             
34286             if(this.labelxs > 0){
34287                 label.cls += ' col-xs-' + this.labelxs;
34288                 items.cls += ' col-xs-' + (12 - this.labelxs);
34289             }
34290         }
34291         
34292         var cfg = {
34293             tag : 'div',
34294             cls : 'roo-radio-set',
34295             cn : [
34296                 {
34297                     tag : 'input',
34298                     cls : 'roo-radio-set-input',
34299                     type : 'hidden',
34300                     name : this.name,
34301                     value : this.value ? this.value :  ''
34302                 },
34303                 label,
34304                 items
34305             ]
34306         };
34307         
34308         if(this.weight.length){
34309             cfg.cls += ' roo-radio-' + this.weight;
34310         }
34311         
34312         if(this.inline) {
34313             cfg.cls += ' roo-radio-set-inline';
34314         }
34315         
34316         var settings=this;
34317         ['xs','sm','md','lg'].map(function(size){
34318             if (settings[size]) {
34319                 cfg.cls += ' col-' + size + '-' + settings[size];
34320             }
34321         });
34322         
34323         return cfg;
34324         
34325     },
34326
34327     initEvents : function()
34328     {
34329         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34330         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34331         
34332         if(!this.fieldLabel.length){
34333             this.labelEl.hide();
34334         }
34335         
34336         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34337         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34338         
34339         this.indicator = this.indicatorEl();
34340         
34341         if(this.indicator){
34342             this.indicator.addClass('invisible');
34343         }
34344         
34345         this.originalValue = this.getValue();
34346         
34347     },
34348     
34349     inputEl: function ()
34350     {
34351         return this.el.select('.roo-radio-set-input', true).first();
34352     },
34353     
34354     getChildContainer : function()
34355     {
34356         return this.itemsEl;
34357     },
34358     
34359     register : function(item)
34360     {
34361         this.radioes.push(item);
34362         
34363     },
34364     
34365     validate : function()
34366     {   
34367         if(this.getVisibilityEl().hasClass('hidden')){
34368             return true;
34369         }
34370         
34371         var valid = false;
34372         
34373         Roo.each(this.radioes, function(i){
34374             if(!i.checked){
34375                 return;
34376             }
34377             
34378             valid = true;
34379             return false;
34380         });
34381         
34382         if(this.allowBlank) {
34383             return true;
34384         }
34385         
34386         if(this.disabled || valid){
34387             this.markValid();
34388             return true;
34389         }
34390         
34391         this.markInvalid();
34392         return false;
34393         
34394     },
34395     
34396     markValid : function()
34397     {
34398         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34399             this.indicatorEl().removeClass('visible');
34400             this.indicatorEl().addClass('invisible');
34401         }
34402         
34403         
34404         if (Roo.bootstrap.version == 3) {
34405             this.el.removeClass([this.invalidClass, this.validClass]);
34406             this.el.addClass(this.validClass);
34407         } else {
34408             this.el.removeClass(['is-invalid','is-valid']);
34409             this.el.addClass(['is-valid']);
34410         }
34411         this.fireEvent('valid', this);
34412     },
34413     
34414     markInvalid : function(msg)
34415     {
34416         if(this.allowBlank || this.disabled){
34417             return;
34418         }
34419         
34420         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34421             this.indicatorEl().removeClass('invisible');
34422             this.indicatorEl().addClass('visible');
34423         }
34424         if (Roo.bootstrap.version == 3) {
34425             this.el.removeClass([this.invalidClass, this.validClass]);
34426             this.el.addClass(this.invalidClass);
34427         } else {
34428             this.el.removeClass(['is-invalid','is-valid']);
34429             this.el.addClass(['is-invalid']);
34430         }
34431         
34432         this.fireEvent('invalid', this, msg);
34433         
34434     },
34435     
34436     setValue : function(v, suppressEvent)
34437     {   
34438         if(this.value === v){
34439             return;
34440         }
34441         
34442         this.value = v;
34443         
34444         if(this.rendered){
34445             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34446         }
34447         
34448         Roo.each(this.radioes, function(i){
34449             i.checked = false;
34450             i.el.removeClass('checked');
34451         });
34452         
34453         Roo.each(this.radioes, function(i){
34454             
34455             if(i.value === v || i.value.toString() === v.toString()){
34456                 i.checked = true;
34457                 i.el.addClass('checked');
34458                 
34459                 if(suppressEvent !== true){
34460                     this.fireEvent('check', this, i);
34461                 }
34462                 
34463                 return false;
34464             }
34465             
34466         }, this);
34467         
34468         this.validate();
34469     },
34470     
34471     clearInvalid : function(){
34472         
34473         if(!this.el || this.preventMark){
34474             return;
34475         }
34476         
34477         this.el.removeClass([this.invalidClass]);
34478         
34479         this.fireEvent('valid', this);
34480     }
34481     
34482 });
34483
34484 Roo.apply(Roo.bootstrap.RadioSet, {
34485     
34486     groups: {},
34487     
34488     register : function(set)
34489     {
34490         this.groups[set.name] = set;
34491     },
34492     
34493     get: function(name) 
34494     {
34495         if (typeof(this.groups[name]) == 'undefined') {
34496             return false;
34497         }
34498         
34499         return this.groups[name] ;
34500     }
34501     
34502 });
34503 /*
34504  * Based on:
34505  * Ext JS Library 1.1.1
34506  * Copyright(c) 2006-2007, Ext JS, LLC.
34507  *
34508  * Originally Released Under LGPL - original licence link has changed is not relivant.
34509  *
34510  * Fork - LGPL
34511  * <script type="text/javascript">
34512  */
34513
34514
34515 /**
34516  * @class Roo.bootstrap.SplitBar
34517  * @extends Roo.util.Observable
34518  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34519  * <br><br>
34520  * Usage:
34521  * <pre><code>
34522 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34523                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34524 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34525 split.minSize = 100;
34526 split.maxSize = 600;
34527 split.animate = true;
34528 split.on('moved', splitterMoved);
34529 </code></pre>
34530  * @constructor
34531  * Create a new SplitBar
34532  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34533  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34534  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34535  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34536                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34537                         position of the SplitBar).
34538  */
34539 Roo.bootstrap.SplitBar = function(cfg){
34540     
34541     /** @private */
34542     
34543     //{
34544     //  dragElement : elm
34545     //  resizingElement: el,
34546         // optional..
34547     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34548     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34549         // existingProxy ???
34550     //}
34551     
34552     this.el = Roo.get(cfg.dragElement, true);
34553     this.el.dom.unselectable = "on";
34554     /** @private */
34555     this.resizingEl = Roo.get(cfg.resizingElement, true);
34556
34557     /**
34558      * @private
34559      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34560      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34561      * @type Number
34562      */
34563     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34564     
34565     /**
34566      * The minimum size of the resizing element. (Defaults to 0)
34567      * @type Number
34568      */
34569     this.minSize = 0;
34570     
34571     /**
34572      * The maximum size of the resizing element. (Defaults to 2000)
34573      * @type Number
34574      */
34575     this.maxSize = 2000;
34576     
34577     /**
34578      * Whether to animate the transition to the new size
34579      * @type Boolean
34580      */
34581     this.animate = false;
34582     
34583     /**
34584      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34585      * @type Boolean
34586      */
34587     this.useShim = false;
34588     
34589     /** @private */
34590     this.shim = null;
34591     
34592     if(!cfg.existingProxy){
34593         /** @private */
34594         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34595     }else{
34596         this.proxy = Roo.get(cfg.existingProxy).dom;
34597     }
34598     /** @private */
34599     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34600     
34601     /** @private */
34602     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34603     
34604     /** @private */
34605     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34606     
34607     /** @private */
34608     this.dragSpecs = {};
34609     
34610     /**
34611      * @private The adapter to use to positon and resize elements
34612      */
34613     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34614     this.adapter.init(this);
34615     
34616     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34617         /** @private */
34618         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34619         this.el.addClass("roo-splitbar-h");
34620     }else{
34621         /** @private */
34622         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34623         this.el.addClass("roo-splitbar-v");
34624     }
34625     
34626     this.addEvents({
34627         /**
34628          * @event resize
34629          * Fires when the splitter is moved (alias for {@link #event-moved})
34630          * @param {Roo.bootstrap.SplitBar} this
34631          * @param {Number} newSize the new width or height
34632          */
34633         "resize" : true,
34634         /**
34635          * @event moved
34636          * Fires when the splitter is moved
34637          * @param {Roo.bootstrap.SplitBar} this
34638          * @param {Number} newSize the new width or height
34639          */
34640         "moved" : true,
34641         /**
34642          * @event beforeresize
34643          * Fires before the splitter is dragged
34644          * @param {Roo.bootstrap.SplitBar} this
34645          */
34646         "beforeresize" : true,
34647
34648         "beforeapply" : true
34649     });
34650
34651     Roo.util.Observable.call(this);
34652 };
34653
34654 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34655     onStartProxyDrag : function(x, y){
34656         this.fireEvent("beforeresize", this);
34657         if(!this.overlay){
34658             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34659             o.unselectable();
34660             o.enableDisplayMode("block");
34661             // all splitbars share the same overlay
34662             Roo.bootstrap.SplitBar.prototype.overlay = o;
34663         }
34664         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34665         this.overlay.show();
34666         Roo.get(this.proxy).setDisplayed("block");
34667         var size = this.adapter.getElementSize(this);
34668         this.activeMinSize = this.getMinimumSize();;
34669         this.activeMaxSize = this.getMaximumSize();;
34670         var c1 = size - this.activeMinSize;
34671         var c2 = Math.max(this.activeMaxSize - size, 0);
34672         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34673             this.dd.resetConstraints();
34674             this.dd.setXConstraint(
34675                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34676                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34677             );
34678             this.dd.setYConstraint(0, 0);
34679         }else{
34680             this.dd.resetConstraints();
34681             this.dd.setXConstraint(0, 0);
34682             this.dd.setYConstraint(
34683                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34684                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34685             );
34686          }
34687         this.dragSpecs.startSize = size;
34688         this.dragSpecs.startPoint = [x, y];
34689         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34690     },
34691     
34692     /** 
34693      * @private Called after the drag operation by the DDProxy
34694      */
34695     onEndProxyDrag : function(e){
34696         Roo.get(this.proxy).setDisplayed(false);
34697         var endPoint = Roo.lib.Event.getXY(e);
34698         if(this.overlay){
34699             this.overlay.hide();
34700         }
34701         var newSize;
34702         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34703             newSize = this.dragSpecs.startSize + 
34704                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34705                     endPoint[0] - this.dragSpecs.startPoint[0] :
34706                     this.dragSpecs.startPoint[0] - endPoint[0]
34707                 );
34708         }else{
34709             newSize = this.dragSpecs.startSize + 
34710                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34711                     endPoint[1] - this.dragSpecs.startPoint[1] :
34712                     this.dragSpecs.startPoint[1] - endPoint[1]
34713                 );
34714         }
34715         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34716         if(newSize != this.dragSpecs.startSize){
34717             if(this.fireEvent('beforeapply', this, newSize) !== false){
34718                 this.adapter.setElementSize(this, newSize);
34719                 this.fireEvent("moved", this, newSize);
34720                 this.fireEvent("resize", this, newSize);
34721             }
34722         }
34723     },
34724     
34725     /**
34726      * Get the adapter this SplitBar uses
34727      * @return The adapter object
34728      */
34729     getAdapter : function(){
34730         return this.adapter;
34731     },
34732     
34733     /**
34734      * Set the adapter this SplitBar uses
34735      * @param {Object} adapter A SplitBar adapter object
34736      */
34737     setAdapter : function(adapter){
34738         this.adapter = adapter;
34739         this.adapter.init(this);
34740     },
34741     
34742     /**
34743      * Gets the minimum size for the resizing element
34744      * @return {Number} The minimum size
34745      */
34746     getMinimumSize : function(){
34747         return this.minSize;
34748     },
34749     
34750     /**
34751      * Sets the minimum size for the resizing element
34752      * @param {Number} minSize The minimum size
34753      */
34754     setMinimumSize : function(minSize){
34755         this.minSize = minSize;
34756     },
34757     
34758     /**
34759      * Gets the maximum size for the resizing element
34760      * @return {Number} The maximum size
34761      */
34762     getMaximumSize : function(){
34763         return this.maxSize;
34764     },
34765     
34766     /**
34767      * Sets the maximum size for the resizing element
34768      * @param {Number} maxSize The maximum size
34769      */
34770     setMaximumSize : function(maxSize){
34771         this.maxSize = maxSize;
34772     },
34773     
34774     /**
34775      * Sets the initialize size for the resizing element
34776      * @param {Number} size The initial size
34777      */
34778     setCurrentSize : function(size){
34779         var oldAnimate = this.animate;
34780         this.animate = false;
34781         this.adapter.setElementSize(this, size);
34782         this.animate = oldAnimate;
34783     },
34784     
34785     /**
34786      * Destroy this splitbar. 
34787      * @param {Boolean} removeEl True to remove the element
34788      */
34789     destroy : function(removeEl){
34790         if(this.shim){
34791             this.shim.remove();
34792         }
34793         this.dd.unreg();
34794         this.proxy.parentNode.removeChild(this.proxy);
34795         if(removeEl){
34796             this.el.remove();
34797         }
34798     }
34799 });
34800
34801 /**
34802  * @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.
34803  */
34804 Roo.bootstrap.SplitBar.createProxy = function(dir){
34805     var proxy = new Roo.Element(document.createElement("div"));
34806     proxy.unselectable();
34807     var cls = 'roo-splitbar-proxy';
34808     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34809     document.body.appendChild(proxy.dom);
34810     return proxy.dom;
34811 };
34812
34813 /** 
34814  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34815  * Default Adapter. It assumes the splitter and resizing element are not positioned
34816  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34817  */
34818 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34819 };
34820
34821 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34822     // do nothing for now
34823     init : function(s){
34824     
34825     },
34826     /**
34827      * Called before drag operations to get the current size of the resizing element. 
34828      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34829      */
34830      getElementSize : function(s){
34831         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34832             return s.resizingEl.getWidth();
34833         }else{
34834             return s.resizingEl.getHeight();
34835         }
34836     },
34837     
34838     /**
34839      * Called after drag operations to set the size of the resizing element.
34840      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34841      * @param {Number} newSize The new size to set
34842      * @param {Function} onComplete A function to be invoked when resizing is complete
34843      */
34844     setElementSize : function(s, newSize, onComplete){
34845         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34846             if(!s.animate){
34847                 s.resizingEl.setWidth(newSize);
34848                 if(onComplete){
34849                     onComplete(s, newSize);
34850                 }
34851             }else{
34852                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34853             }
34854         }else{
34855             
34856             if(!s.animate){
34857                 s.resizingEl.setHeight(newSize);
34858                 if(onComplete){
34859                     onComplete(s, newSize);
34860                 }
34861             }else{
34862                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34863             }
34864         }
34865     }
34866 };
34867
34868 /** 
34869  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34870  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34871  * Adapter that  moves the splitter element to align with the resized sizing element. 
34872  * Used with an absolute positioned SplitBar.
34873  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34874  * document.body, make sure you assign an id to the body element.
34875  */
34876 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34877     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34878     this.container = Roo.get(container);
34879 };
34880
34881 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34882     init : function(s){
34883         this.basic.init(s);
34884     },
34885     
34886     getElementSize : function(s){
34887         return this.basic.getElementSize(s);
34888     },
34889     
34890     setElementSize : function(s, newSize, onComplete){
34891         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34892     },
34893     
34894     moveSplitter : function(s){
34895         var yes = Roo.bootstrap.SplitBar;
34896         switch(s.placement){
34897             case yes.LEFT:
34898                 s.el.setX(s.resizingEl.getRight());
34899                 break;
34900             case yes.RIGHT:
34901                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34902                 break;
34903             case yes.TOP:
34904                 s.el.setY(s.resizingEl.getBottom());
34905                 break;
34906             case yes.BOTTOM:
34907                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34908                 break;
34909         }
34910     }
34911 };
34912
34913 /**
34914  * Orientation constant - Create a vertical SplitBar
34915  * @static
34916  * @type Number
34917  */
34918 Roo.bootstrap.SplitBar.VERTICAL = 1;
34919
34920 /**
34921  * Orientation constant - Create a horizontal SplitBar
34922  * @static
34923  * @type Number
34924  */
34925 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34926
34927 /**
34928  * Placement constant - The resizing element is to the left of the splitter element
34929  * @static
34930  * @type Number
34931  */
34932 Roo.bootstrap.SplitBar.LEFT = 1;
34933
34934 /**
34935  * Placement constant - The resizing element is to the right of the splitter element
34936  * @static
34937  * @type Number
34938  */
34939 Roo.bootstrap.SplitBar.RIGHT = 2;
34940
34941 /**
34942  * Placement constant - The resizing element is positioned above the splitter element
34943  * @static
34944  * @type Number
34945  */
34946 Roo.bootstrap.SplitBar.TOP = 3;
34947
34948 /**
34949  * Placement constant - The resizing element is positioned under splitter element
34950  * @static
34951  * @type Number
34952  */
34953 Roo.bootstrap.SplitBar.BOTTOM = 4;
34954 Roo.namespace("Roo.bootstrap.layout");/*
34955  * Based on:
34956  * Ext JS Library 1.1.1
34957  * Copyright(c) 2006-2007, Ext JS, LLC.
34958  *
34959  * Originally Released Under LGPL - original licence link has changed is not relivant.
34960  *
34961  * Fork - LGPL
34962  * <script type="text/javascript">
34963  */
34964
34965 /**
34966  * @class Roo.bootstrap.layout.Manager
34967  * @extends Roo.bootstrap.Component
34968  * Base class for layout managers.
34969  */
34970 Roo.bootstrap.layout.Manager = function(config)
34971 {
34972     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34973
34974
34975
34976
34977
34978     /** false to disable window resize monitoring @type Boolean */
34979     this.monitorWindowResize = true;
34980     this.regions = {};
34981     this.addEvents({
34982         /**
34983          * @event layout
34984          * Fires when a layout is performed.
34985          * @param {Roo.LayoutManager} this
34986          */
34987         "layout" : true,
34988         /**
34989          * @event regionresized
34990          * Fires when the user resizes a region.
34991          * @param {Roo.LayoutRegion} region The resized region
34992          * @param {Number} newSize The new size (width for east/west, height for north/south)
34993          */
34994         "regionresized" : true,
34995         /**
34996          * @event regioncollapsed
34997          * Fires when a region is collapsed.
34998          * @param {Roo.LayoutRegion} region The collapsed region
34999          */
35000         "regioncollapsed" : true,
35001         /**
35002          * @event regionexpanded
35003          * Fires when a region is expanded.
35004          * @param {Roo.LayoutRegion} region The expanded region
35005          */
35006         "regionexpanded" : true
35007     });
35008     this.updating = false;
35009
35010     if (config.el) {
35011         this.el = Roo.get(config.el);
35012         this.initEvents();
35013     }
35014
35015 };
35016
35017 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
35018
35019
35020     regions : null,
35021
35022     monitorWindowResize : true,
35023
35024
35025     updating : false,
35026
35027
35028     onRender : function(ct, position)
35029     {
35030         if(!this.el){
35031             this.el = Roo.get(ct);
35032             this.initEvents();
35033         }
35034         //this.fireEvent('render',this);
35035     },
35036
35037
35038     initEvents: function()
35039     {
35040
35041
35042         // ie scrollbar fix
35043         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
35044             document.body.scroll = "no";
35045         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
35046             this.el.position('relative');
35047         }
35048         this.id = this.el.id;
35049         this.el.addClass("roo-layout-container");
35050         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
35051         if(this.el.dom != document.body ) {
35052             this.el.on('resize', this.layout,this);
35053             this.el.on('show', this.layout,this);
35054         }
35055
35056     },
35057
35058     /**
35059      * Returns true if this layout is currently being updated
35060      * @return {Boolean}
35061      */
35062     isUpdating : function(){
35063         return this.updating;
35064     },
35065
35066     /**
35067      * Suspend the LayoutManager from doing auto-layouts while
35068      * making multiple add or remove calls
35069      */
35070     beginUpdate : function(){
35071         this.updating = true;
35072     },
35073
35074     /**
35075      * Restore auto-layouts and optionally disable the manager from performing a layout
35076      * @param {Boolean} noLayout true to disable a layout update
35077      */
35078     endUpdate : function(noLayout){
35079         this.updating = false;
35080         if(!noLayout){
35081             this.layout();
35082         }
35083     },
35084
35085     layout: function(){
35086         // abstract...
35087     },
35088
35089     onRegionResized : function(region, newSize){
35090         this.fireEvent("regionresized", region, newSize);
35091         this.layout();
35092     },
35093
35094     onRegionCollapsed : function(region){
35095         this.fireEvent("regioncollapsed", region);
35096     },
35097
35098     onRegionExpanded : function(region){
35099         this.fireEvent("regionexpanded", region);
35100     },
35101
35102     /**
35103      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
35104      * performs box-model adjustments.
35105      * @return {Object} The size as an object {width: (the width), height: (the height)}
35106      */
35107     getViewSize : function()
35108     {
35109         var size;
35110         if(this.el.dom != document.body){
35111             size = this.el.getSize();
35112         }else{
35113             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
35114         }
35115         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
35116         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35117         return size;
35118     },
35119
35120     /**
35121      * Returns the Element this layout is bound to.
35122      * @return {Roo.Element}
35123      */
35124     getEl : function(){
35125         return this.el;
35126     },
35127
35128     /**
35129      * Returns the specified region.
35130      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
35131      * @return {Roo.LayoutRegion}
35132      */
35133     getRegion : function(target){
35134         return this.regions[target.toLowerCase()];
35135     },
35136
35137     onWindowResize : function(){
35138         if(this.monitorWindowResize){
35139             this.layout();
35140         }
35141     }
35142 });
35143 /*
35144  * Based on:
35145  * Ext JS Library 1.1.1
35146  * Copyright(c) 2006-2007, Ext JS, LLC.
35147  *
35148  * Originally Released Under LGPL - original licence link has changed is not relivant.
35149  *
35150  * Fork - LGPL
35151  * <script type="text/javascript">
35152  */
35153 /**
35154  * @class Roo.bootstrap.layout.Border
35155  * @extends Roo.bootstrap.layout.Manager
35156  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
35157  * please see: examples/bootstrap/nested.html<br><br>
35158  
35159 <b>The container the layout is rendered into can be either the body element or any other element.
35160 If it is not the body element, the container needs to either be an absolute positioned element,
35161 or you will need to add "position:relative" to the css of the container.  You will also need to specify
35162 the container size if it is not the body element.</b>
35163
35164 * @constructor
35165 * Create a new Border
35166 * @param {Object} config Configuration options
35167  */
35168 Roo.bootstrap.layout.Border = function(config){
35169     config = config || {};
35170     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
35171     
35172     
35173     
35174     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35175         if(config[region]){
35176             config[region].region = region;
35177             this.addRegion(config[region]);
35178         }
35179     },this);
35180     
35181 };
35182
35183 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
35184
35185 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
35186     
35187     parent : false, // this might point to a 'nest' or a ???
35188     
35189     /**
35190      * Creates and adds a new region if it doesn't already exist.
35191      * @param {String} target The target region key (north, south, east, west or center).
35192      * @param {Object} config The regions config object
35193      * @return {BorderLayoutRegion} The new region
35194      */
35195     addRegion : function(config)
35196     {
35197         if(!this.regions[config.region]){
35198             var r = this.factory(config);
35199             this.bindRegion(r);
35200         }
35201         return this.regions[config.region];
35202     },
35203
35204     // private (kinda)
35205     bindRegion : function(r){
35206         this.regions[r.config.region] = r;
35207         
35208         r.on("visibilitychange",    this.layout, this);
35209         r.on("paneladded",          this.layout, this);
35210         r.on("panelremoved",        this.layout, this);
35211         r.on("invalidated",         this.layout, this);
35212         r.on("resized",             this.onRegionResized, this);
35213         r.on("collapsed",           this.onRegionCollapsed, this);
35214         r.on("expanded",            this.onRegionExpanded, this);
35215     },
35216
35217     /**
35218      * Performs a layout update.
35219      */
35220     layout : function()
35221     {
35222         if(this.updating) {
35223             return;
35224         }
35225         
35226         // render all the rebions if they have not been done alreayd?
35227         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35228             if(this.regions[region] && !this.regions[region].bodyEl){
35229                 this.regions[region].onRender(this.el)
35230             }
35231         },this);
35232         
35233         var size = this.getViewSize();
35234         var w = size.width;
35235         var h = size.height;
35236         var centerW = w;
35237         var centerH = h;
35238         var centerY = 0;
35239         var centerX = 0;
35240         //var x = 0, y = 0;
35241
35242         var rs = this.regions;
35243         var north = rs["north"];
35244         var south = rs["south"]; 
35245         var west = rs["west"];
35246         var east = rs["east"];
35247         var center = rs["center"];
35248         //if(this.hideOnLayout){ // not supported anymore
35249             //c.el.setStyle("display", "none");
35250         //}
35251         if(north && north.isVisible()){
35252             var b = north.getBox();
35253             var m = north.getMargins();
35254             b.width = w - (m.left+m.right);
35255             b.x = m.left;
35256             b.y = m.top;
35257             centerY = b.height + b.y + m.bottom;
35258             centerH -= centerY;
35259             north.updateBox(this.safeBox(b));
35260         }
35261         if(south && south.isVisible()){
35262             var b = south.getBox();
35263             var m = south.getMargins();
35264             b.width = w - (m.left+m.right);
35265             b.x = m.left;
35266             var totalHeight = (b.height + m.top + m.bottom);
35267             b.y = h - totalHeight + m.top;
35268             centerH -= totalHeight;
35269             south.updateBox(this.safeBox(b));
35270         }
35271         if(west && west.isVisible()){
35272             var b = west.getBox();
35273             var m = west.getMargins();
35274             b.height = centerH - (m.top+m.bottom);
35275             b.x = m.left;
35276             b.y = centerY + m.top;
35277             var totalWidth = (b.width + m.left + m.right);
35278             centerX += totalWidth;
35279             centerW -= totalWidth;
35280             west.updateBox(this.safeBox(b));
35281         }
35282         if(east && east.isVisible()){
35283             var b = east.getBox();
35284             var m = east.getMargins();
35285             b.height = centerH - (m.top+m.bottom);
35286             var totalWidth = (b.width + m.left + m.right);
35287             b.x = w - totalWidth + m.left;
35288             b.y = centerY + m.top;
35289             centerW -= totalWidth;
35290             east.updateBox(this.safeBox(b));
35291         }
35292         if(center){
35293             var m = center.getMargins();
35294             var centerBox = {
35295                 x: centerX + m.left,
35296                 y: centerY + m.top,
35297                 width: centerW - (m.left+m.right),
35298                 height: centerH - (m.top+m.bottom)
35299             };
35300             //if(this.hideOnLayout){
35301                 //center.el.setStyle("display", "block");
35302             //}
35303             center.updateBox(this.safeBox(centerBox));
35304         }
35305         this.el.repaint();
35306         this.fireEvent("layout", this);
35307     },
35308
35309     // private
35310     safeBox : function(box){
35311         box.width = Math.max(0, box.width);
35312         box.height = Math.max(0, box.height);
35313         return box;
35314     },
35315
35316     /**
35317      * Adds a ContentPanel (or subclass) to this layout.
35318      * @param {String} target The target region key (north, south, east, west or center).
35319      * @param {Roo.ContentPanel} panel The panel to add
35320      * @return {Roo.ContentPanel} The added panel
35321      */
35322     add : function(target, panel){
35323          
35324         target = target.toLowerCase();
35325         return this.regions[target].add(panel);
35326     },
35327
35328     /**
35329      * Remove a ContentPanel (or subclass) to this layout.
35330      * @param {String} target The target region key (north, south, east, west or center).
35331      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35332      * @return {Roo.ContentPanel} The removed panel
35333      */
35334     remove : function(target, panel){
35335         target = target.toLowerCase();
35336         return this.regions[target].remove(panel);
35337     },
35338
35339     /**
35340      * Searches all regions for a panel with the specified id
35341      * @param {String} panelId
35342      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35343      */
35344     findPanel : function(panelId){
35345         var rs = this.regions;
35346         for(var target in rs){
35347             if(typeof rs[target] != "function"){
35348                 var p = rs[target].getPanel(panelId);
35349                 if(p){
35350                     return p;
35351                 }
35352             }
35353         }
35354         return null;
35355     },
35356
35357     /**
35358      * Searches all regions for a panel with the specified id and activates (shows) it.
35359      * @param {String/ContentPanel} panelId The panels id or the panel itself
35360      * @return {Roo.ContentPanel} The shown panel or null
35361      */
35362     showPanel : function(panelId) {
35363       var rs = this.regions;
35364       for(var target in rs){
35365          var r = rs[target];
35366          if(typeof r != "function"){
35367             if(r.hasPanel(panelId)){
35368                return r.showPanel(panelId);
35369             }
35370          }
35371       }
35372       return null;
35373    },
35374
35375    /**
35376      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35377      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35378      */
35379    /*
35380     restoreState : function(provider){
35381         if(!provider){
35382             provider = Roo.state.Manager;
35383         }
35384         var sm = new Roo.LayoutStateManager();
35385         sm.init(this, provider);
35386     },
35387 */
35388  
35389  
35390     /**
35391      * Adds a xtype elements to the layout.
35392      * <pre><code>
35393
35394 layout.addxtype({
35395        xtype : 'ContentPanel',
35396        region: 'west',
35397        items: [ .... ]
35398    }
35399 );
35400
35401 layout.addxtype({
35402         xtype : 'NestedLayoutPanel',
35403         region: 'west',
35404         layout: {
35405            center: { },
35406            west: { }   
35407         },
35408         items : [ ... list of content panels or nested layout panels.. ]
35409    }
35410 );
35411 </code></pre>
35412      * @param {Object} cfg Xtype definition of item to add.
35413      */
35414     addxtype : function(cfg)
35415     {
35416         // basically accepts a pannel...
35417         // can accept a layout region..!?!?
35418         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35419         
35420         
35421         // theory?  children can only be panels??
35422         
35423         //if (!cfg.xtype.match(/Panel$/)) {
35424         //    return false;
35425         //}
35426         var ret = false;
35427         
35428         if (typeof(cfg.region) == 'undefined') {
35429             Roo.log("Failed to add Panel, region was not set");
35430             Roo.log(cfg);
35431             return false;
35432         }
35433         var region = cfg.region;
35434         delete cfg.region;
35435         
35436           
35437         var xitems = [];
35438         if (cfg.items) {
35439             xitems = cfg.items;
35440             delete cfg.items;
35441         }
35442         var nb = false;
35443         
35444         if ( region == 'center') {
35445             Roo.log("Center: " + cfg.title);
35446         }
35447         
35448         
35449         switch(cfg.xtype) 
35450         {
35451             case 'Content':  // ContentPanel (el, cfg)
35452             case 'Scroll':  // ContentPanel (el, cfg)
35453             case 'View': 
35454                 cfg.autoCreate = cfg.autoCreate || true;
35455                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35456                 //} else {
35457                 //    var el = this.el.createChild();
35458                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35459                 //}
35460                 
35461                 this.add(region, ret);
35462                 break;
35463             
35464             /*
35465             case 'TreePanel': // our new panel!
35466                 cfg.el = this.el.createChild();
35467                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35468                 this.add(region, ret);
35469                 break;
35470             */
35471             
35472             case 'Nest': 
35473                 // create a new Layout (which is  a Border Layout...
35474                 
35475                 var clayout = cfg.layout;
35476                 clayout.el  = this.el.createChild();
35477                 clayout.items   = clayout.items  || [];
35478                 
35479                 delete cfg.layout;
35480                 
35481                 // replace this exitems with the clayout ones..
35482                 xitems = clayout.items;
35483                  
35484                 // force background off if it's in center...
35485                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35486                     cfg.background = false;
35487                 }
35488                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35489                 
35490                 
35491                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35492                 //console.log('adding nested layout panel '  + cfg.toSource());
35493                 this.add(region, ret);
35494                 nb = {}; /// find first...
35495                 break;
35496             
35497             case 'Grid':
35498                 
35499                 // needs grid and region
35500                 
35501                 //var el = this.getRegion(region).el.createChild();
35502                 /*
35503                  *var el = this.el.createChild();
35504                 // create the grid first...
35505                 cfg.grid.container = el;
35506                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35507                 */
35508                 
35509                 if (region == 'center' && this.active ) {
35510                     cfg.background = false;
35511                 }
35512                 
35513                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35514                 
35515                 this.add(region, ret);
35516                 /*
35517                 if (cfg.background) {
35518                     // render grid on panel activation (if panel background)
35519                     ret.on('activate', function(gp) {
35520                         if (!gp.grid.rendered) {
35521                     //        gp.grid.render(el);
35522                         }
35523                     });
35524                 } else {
35525                   //  cfg.grid.render(el);
35526                 }
35527                 */
35528                 break;
35529            
35530            
35531             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35532                 // it was the old xcomponent building that caused this before.
35533                 // espeically if border is the top element in the tree.
35534                 ret = this;
35535                 break; 
35536                 
35537                     
35538                 
35539                 
35540                 
35541             default:
35542                 /*
35543                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35544                     
35545                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35546                     this.add(region, ret);
35547                 } else {
35548                 */
35549                     Roo.log(cfg);
35550                     throw "Can not add '" + cfg.xtype + "' to Border";
35551                     return null;
35552              
35553                                 
35554              
35555         }
35556         this.beginUpdate();
35557         // add children..
35558         var region = '';
35559         var abn = {};
35560         Roo.each(xitems, function(i)  {
35561             region = nb && i.region ? i.region : false;
35562             
35563             var add = ret.addxtype(i);
35564            
35565             if (region) {
35566                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35567                 if (!i.background) {
35568                     abn[region] = nb[region] ;
35569                 }
35570             }
35571             
35572         });
35573         this.endUpdate();
35574
35575         // make the last non-background panel active..
35576         //if (nb) { Roo.log(abn); }
35577         if (nb) {
35578             
35579             for(var r in abn) {
35580                 region = this.getRegion(r);
35581                 if (region) {
35582                     // tried using nb[r], but it does not work..
35583                      
35584                     region.showPanel(abn[r]);
35585                    
35586                 }
35587             }
35588         }
35589         return ret;
35590         
35591     },
35592     
35593     
35594 // private
35595     factory : function(cfg)
35596     {
35597         
35598         var validRegions = Roo.bootstrap.layout.Border.regions;
35599
35600         var target = cfg.region;
35601         cfg.mgr = this;
35602         
35603         var r = Roo.bootstrap.layout;
35604         Roo.log(target);
35605         switch(target){
35606             case "north":
35607                 return new r.North(cfg);
35608             case "south":
35609                 return new r.South(cfg);
35610             case "east":
35611                 return new r.East(cfg);
35612             case "west":
35613                 return new r.West(cfg);
35614             case "center":
35615                 return new r.Center(cfg);
35616         }
35617         throw 'Layout region "'+target+'" not supported.';
35618     }
35619     
35620     
35621 });
35622  /*
35623  * Based on:
35624  * Ext JS Library 1.1.1
35625  * Copyright(c) 2006-2007, Ext JS, LLC.
35626  *
35627  * Originally Released Under LGPL - original licence link has changed is not relivant.
35628  *
35629  * Fork - LGPL
35630  * <script type="text/javascript">
35631  */
35632  
35633 /**
35634  * @class Roo.bootstrap.layout.Basic
35635  * @extends Roo.util.Observable
35636  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35637  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35638  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35639  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35640  * @cfg {string}   region  the region that it inhabits..
35641  * @cfg {bool}   skipConfig skip config?
35642  * 
35643
35644  */
35645 Roo.bootstrap.layout.Basic = function(config){
35646     
35647     this.mgr = config.mgr;
35648     
35649     this.position = config.region;
35650     
35651     var skipConfig = config.skipConfig;
35652     
35653     this.events = {
35654         /**
35655          * @scope Roo.BasicLayoutRegion
35656          */
35657         
35658         /**
35659          * @event beforeremove
35660          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35661          * @param {Roo.LayoutRegion} this
35662          * @param {Roo.ContentPanel} panel The panel
35663          * @param {Object} e The cancel event object
35664          */
35665         "beforeremove" : true,
35666         /**
35667          * @event invalidated
35668          * Fires when the layout for this region is changed.
35669          * @param {Roo.LayoutRegion} this
35670          */
35671         "invalidated" : true,
35672         /**
35673          * @event visibilitychange
35674          * Fires when this region is shown or hidden 
35675          * @param {Roo.LayoutRegion} this
35676          * @param {Boolean} visibility true or false
35677          */
35678         "visibilitychange" : true,
35679         /**
35680          * @event paneladded
35681          * Fires when a panel is added. 
35682          * @param {Roo.LayoutRegion} this
35683          * @param {Roo.ContentPanel} panel The panel
35684          */
35685         "paneladded" : true,
35686         /**
35687          * @event panelremoved
35688          * Fires when a panel is removed. 
35689          * @param {Roo.LayoutRegion} this
35690          * @param {Roo.ContentPanel} panel The panel
35691          */
35692         "panelremoved" : true,
35693         /**
35694          * @event beforecollapse
35695          * Fires when this region before collapse.
35696          * @param {Roo.LayoutRegion} this
35697          */
35698         "beforecollapse" : true,
35699         /**
35700          * @event collapsed
35701          * Fires when this region is collapsed.
35702          * @param {Roo.LayoutRegion} this
35703          */
35704         "collapsed" : true,
35705         /**
35706          * @event expanded
35707          * Fires when this region is expanded.
35708          * @param {Roo.LayoutRegion} this
35709          */
35710         "expanded" : true,
35711         /**
35712          * @event slideshow
35713          * Fires when this region is slid into view.
35714          * @param {Roo.LayoutRegion} this
35715          */
35716         "slideshow" : true,
35717         /**
35718          * @event slidehide
35719          * Fires when this region slides out of view. 
35720          * @param {Roo.LayoutRegion} this
35721          */
35722         "slidehide" : true,
35723         /**
35724          * @event panelactivated
35725          * Fires when a panel is activated. 
35726          * @param {Roo.LayoutRegion} this
35727          * @param {Roo.ContentPanel} panel The activated panel
35728          */
35729         "panelactivated" : true,
35730         /**
35731          * @event resized
35732          * Fires when the user resizes this region. 
35733          * @param {Roo.LayoutRegion} this
35734          * @param {Number} newSize The new size (width for east/west, height for north/south)
35735          */
35736         "resized" : true
35737     };
35738     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35739     this.panels = new Roo.util.MixedCollection();
35740     this.panels.getKey = this.getPanelId.createDelegate(this);
35741     this.box = null;
35742     this.activePanel = null;
35743     // ensure listeners are added...
35744     
35745     if (config.listeners || config.events) {
35746         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35747             listeners : config.listeners || {},
35748             events : config.events || {}
35749         });
35750     }
35751     
35752     if(skipConfig !== true){
35753         this.applyConfig(config);
35754     }
35755 };
35756
35757 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35758 {
35759     getPanelId : function(p){
35760         return p.getId();
35761     },
35762     
35763     applyConfig : function(config){
35764         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35765         this.config = config;
35766         
35767     },
35768     
35769     /**
35770      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35771      * the width, for horizontal (north, south) the height.
35772      * @param {Number} newSize The new width or height
35773      */
35774     resizeTo : function(newSize){
35775         var el = this.el ? this.el :
35776                  (this.activePanel ? this.activePanel.getEl() : null);
35777         if(el){
35778             switch(this.position){
35779                 case "east":
35780                 case "west":
35781                     el.setWidth(newSize);
35782                     this.fireEvent("resized", this, newSize);
35783                 break;
35784                 case "north":
35785                 case "south":
35786                     el.setHeight(newSize);
35787                     this.fireEvent("resized", this, newSize);
35788                 break;                
35789             }
35790         }
35791     },
35792     
35793     getBox : function(){
35794         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35795     },
35796     
35797     getMargins : function(){
35798         return this.margins;
35799     },
35800     
35801     updateBox : function(box){
35802         this.box = box;
35803         var el = this.activePanel.getEl();
35804         el.dom.style.left = box.x + "px";
35805         el.dom.style.top = box.y + "px";
35806         this.activePanel.setSize(box.width, box.height);
35807     },
35808     
35809     /**
35810      * Returns the container element for this region.
35811      * @return {Roo.Element}
35812      */
35813     getEl : function(){
35814         return this.activePanel;
35815     },
35816     
35817     /**
35818      * Returns true if this region is currently visible.
35819      * @return {Boolean}
35820      */
35821     isVisible : function(){
35822         return this.activePanel ? true : false;
35823     },
35824     
35825     setActivePanel : function(panel){
35826         panel = this.getPanel(panel);
35827         if(this.activePanel && this.activePanel != panel){
35828             this.activePanel.setActiveState(false);
35829             this.activePanel.getEl().setLeftTop(-10000,-10000);
35830         }
35831         this.activePanel = panel;
35832         panel.setActiveState(true);
35833         if(this.box){
35834             panel.setSize(this.box.width, this.box.height);
35835         }
35836         this.fireEvent("panelactivated", this, panel);
35837         this.fireEvent("invalidated");
35838     },
35839     
35840     /**
35841      * Show the specified panel.
35842      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35843      * @return {Roo.ContentPanel} The shown panel or null
35844      */
35845     showPanel : function(panel){
35846         panel = this.getPanel(panel);
35847         if(panel){
35848             this.setActivePanel(panel);
35849         }
35850         return panel;
35851     },
35852     
35853     /**
35854      * Get the active panel for this region.
35855      * @return {Roo.ContentPanel} The active panel or null
35856      */
35857     getActivePanel : function(){
35858         return this.activePanel;
35859     },
35860     
35861     /**
35862      * Add the passed ContentPanel(s)
35863      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35864      * @return {Roo.ContentPanel} The panel added (if only one was added)
35865      */
35866     add : function(panel){
35867         if(arguments.length > 1){
35868             for(var i = 0, len = arguments.length; i < len; i++) {
35869                 this.add(arguments[i]);
35870             }
35871             return null;
35872         }
35873         if(this.hasPanel(panel)){
35874             this.showPanel(panel);
35875             return panel;
35876         }
35877         var el = panel.getEl();
35878         if(el.dom.parentNode != this.mgr.el.dom){
35879             this.mgr.el.dom.appendChild(el.dom);
35880         }
35881         if(panel.setRegion){
35882             panel.setRegion(this);
35883         }
35884         this.panels.add(panel);
35885         el.setStyle("position", "absolute");
35886         if(!panel.background){
35887             this.setActivePanel(panel);
35888             if(this.config.initialSize && this.panels.getCount()==1){
35889                 this.resizeTo(this.config.initialSize);
35890             }
35891         }
35892         this.fireEvent("paneladded", this, panel);
35893         return panel;
35894     },
35895     
35896     /**
35897      * Returns true if the panel is in this region.
35898      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35899      * @return {Boolean}
35900      */
35901     hasPanel : function(panel){
35902         if(typeof panel == "object"){ // must be panel obj
35903             panel = panel.getId();
35904         }
35905         return this.getPanel(panel) ? true : false;
35906     },
35907     
35908     /**
35909      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35910      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35911      * @param {Boolean} preservePanel Overrides the config preservePanel option
35912      * @return {Roo.ContentPanel} The panel that was removed
35913      */
35914     remove : function(panel, preservePanel){
35915         panel = this.getPanel(panel);
35916         if(!panel){
35917             return null;
35918         }
35919         var e = {};
35920         this.fireEvent("beforeremove", this, panel, e);
35921         if(e.cancel === true){
35922             return null;
35923         }
35924         var panelId = panel.getId();
35925         this.panels.removeKey(panelId);
35926         return panel;
35927     },
35928     
35929     /**
35930      * Returns the panel specified or null if it's not in this region.
35931      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35932      * @return {Roo.ContentPanel}
35933      */
35934     getPanel : function(id){
35935         if(typeof id == "object"){ // must be panel obj
35936             return id;
35937         }
35938         return this.panels.get(id);
35939     },
35940     
35941     /**
35942      * Returns this regions position (north/south/east/west/center).
35943      * @return {String} 
35944      */
35945     getPosition: function(){
35946         return this.position;    
35947     }
35948 });/*
35949  * Based on:
35950  * Ext JS Library 1.1.1
35951  * Copyright(c) 2006-2007, Ext JS, LLC.
35952  *
35953  * Originally Released Under LGPL - original licence link has changed is not relivant.
35954  *
35955  * Fork - LGPL
35956  * <script type="text/javascript">
35957  */
35958  
35959 /**
35960  * @class Roo.bootstrap.layout.Region
35961  * @extends Roo.bootstrap.layout.Basic
35962  * This class represents a region in a layout manager.
35963  
35964  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35965  * @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})
35966  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35967  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35968  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35969  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35970  * @cfg {String}    title           The title for the region (overrides panel titles)
35971  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35972  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35973  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35974  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35975  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35976  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35977  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35978  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35979  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35980  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35981
35982  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35983  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35984  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35985  * @cfg {Number}    width           For East/West panels
35986  * @cfg {Number}    height          For North/South panels
35987  * @cfg {Boolean}   split           To show the splitter
35988  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35989  * 
35990  * @cfg {string}   cls             Extra CSS classes to add to region
35991  * 
35992  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35993  * @cfg {string}   region  the region that it inhabits..
35994  *
35995
35996  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35997  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35998
35999  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
36000  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
36001  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
36002  */
36003 Roo.bootstrap.layout.Region = function(config)
36004 {
36005     this.applyConfig(config);
36006
36007     var mgr = config.mgr;
36008     var pos = config.region;
36009     config.skipConfig = true;
36010     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
36011     
36012     if (mgr.el) {
36013         this.onRender(mgr.el);   
36014     }
36015      
36016     this.visible = true;
36017     this.collapsed = false;
36018     this.unrendered_panels = [];
36019 };
36020
36021 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
36022
36023     position: '', // set by wrapper (eg. north/south etc..)
36024     unrendered_panels : null,  // unrendered panels.
36025     
36026     tabPosition : false,
36027     
36028     mgr: false, // points to 'Border'
36029     
36030     
36031     createBody : function(){
36032         /** This region's body element 
36033         * @type Roo.Element */
36034         this.bodyEl = this.el.createChild({
36035                 tag: "div",
36036                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
36037         });
36038     },
36039
36040     onRender: function(ctr, pos)
36041     {
36042         var dh = Roo.DomHelper;
36043         /** This region's container element 
36044         * @type Roo.Element */
36045         this.el = dh.append(ctr.dom, {
36046                 tag: "div",
36047                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
36048             }, true);
36049         /** This region's title element 
36050         * @type Roo.Element */
36051     
36052         this.titleEl = dh.append(this.el.dom,  {
36053                 tag: "div",
36054                 unselectable: "on",
36055                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
36056                 children:[
36057                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
36058                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
36059                 ]
36060             }, true);
36061         
36062         this.titleEl.enableDisplayMode();
36063         /** This region's title text element 
36064         * @type HTMLElement */
36065         this.titleTextEl = this.titleEl.dom.firstChild;
36066         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
36067         /*
36068         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
36069         this.closeBtn.enableDisplayMode();
36070         this.closeBtn.on("click", this.closeClicked, this);
36071         this.closeBtn.hide();
36072     */
36073         this.createBody(this.config);
36074         if(this.config.hideWhenEmpty){
36075             this.hide();
36076             this.on("paneladded", this.validateVisibility, this);
36077             this.on("panelremoved", this.validateVisibility, this);
36078         }
36079         if(this.autoScroll){
36080             this.bodyEl.setStyle("overflow", "auto");
36081         }else{
36082             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
36083         }
36084         //if(c.titlebar !== false){
36085             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
36086                 this.titleEl.hide();
36087             }else{
36088                 this.titleEl.show();
36089                 if(this.config.title){
36090                     this.titleTextEl.innerHTML = this.config.title;
36091                 }
36092             }
36093         //}
36094         if(this.config.collapsed){
36095             this.collapse(true);
36096         }
36097         if(this.config.hidden){
36098             this.hide();
36099         }
36100         
36101         if (this.unrendered_panels && this.unrendered_panels.length) {
36102             for (var i =0;i< this.unrendered_panels.length; i++) {
36103                 this.add(this.unrendered_panels[i]);
36104             }
36105             this.unrendered_panels = null;
36106             
36107         }
36108         
36109     },
36110     
36111     applyConfig : function(c)
36112     {
36113         /*
36114          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
36115             var dh = Roo.DomHelper;
36116             if(c.titlebar !== false){
36117                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
36118                 this.collapseBtn.on("click", this.collapse, this);
36119                 this.collapseBtn.enableDisplayMode();
36120                 /*
36121                 if(c.showPin === true || this.showPin){
36122                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
36123                     this.stickBtn.enableDisplayMode();
36124                     this.stickBtn.on("click", this.expand, this);
36125                     this.stickBtn.hide();
36126                 }
36127                 
36128             }
36129             */
36130             /** This region's collapsed element
36131             * @type Roo.Element */
36132             /*
36133              *
36134             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
36135                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
36136             ]}, true);
36137             
36138             if(c.floatable !== false){
36139                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
36140                this.collapsedEl.on("click", this.collapseClick, this);
36141             }
36142
36143             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
36144                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
36145                    id: "message", unselectable: "on", style:{"float":"left"}});
36146                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
36147              }
36148             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
36149             this.expandBtn.on("click", this.expand, this);
36150             
36151         }
36152         
36153         if(this.collapseBtn){
36154             this.collapseBtn.setVisible(c.collapsible == true);
36155         }
36156         
36157         this.cmargins = c.cmargins || this.cmargins ||
36158                          (this.position == "west" || this.position == "east" ?
36159                              {top: 0, left: 2, right:2, bottom: 0} :
36160                              {top: 2, left: 0, right:0, bottom: 2});
36161         */
36162         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36163         
36164         
36165         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
36166         
36167         this.autoScroll = c.autoScroll || false;
36168         
36169         
36170        
36171         
36172         this.duration = c.duration || .30;
36173         this.slideDuration = c.slideDuration || .45;
36174         this.config = c;
36175        
36176     },
36177     /**
36178      * Returns true if this region is currently visible.
36179      * @return {Boolean}
36180      */
36181     isVisible : function(){
36182         return this.visible;
36183     },
36184
36185     /**
36186      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
36187      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
36188      */
36189     //setCollapsedTitle : function(title){
36190     //    title = title || "&#160;";
36191      //   if(this.collapsedTitleTextEl){
36192       //      this.collapsedTitleTextEl.innerHTML = title;
36193        // }
36194     //},
36195
36196     getBox : function(){
36197         var b;
36198       //  if(!this.collapsed){
36199             b = this.el.getBox(false, true);
36200        // }else{
36201           //  b = this.collapsedEl.getBox(false, true);
36202         //}
36203         return b;
36204     },
36205
36206     getMargins : function(){
36207         return this.margins;
36208         //return this.collapsed ? this.cmargins : this.margins;
36209     },
36210 /*
36211     highlight : function(){
36212         this.el.addClass("x-layout-panel-dragover");
36213     },
36214
36215     unhighlight : function(){
36216         this.el.removeClass("x-layout-panel-dragover");
36217     },
36218 */
36219     updateBox : function(box)
36220     {
36221         if (!this.bodyEl) {
36222             return; // not rendered yet..
36223         }
36224         
36225         this.box = box;
36226         if(!this.collapsed){
36227             this.el.dom.style.left = box.x + "px";
36228             this.el.dom.style.top = box.y + "px";
36229             this.updateBody(box.width, box.height);
36230         }else{
36231             this.collapsedEl.dom.style.left = box.x + "px";
36232             this.collapsedEl.dom.style.top = box.y + "px";
36233             this.collapsedEl.setSize(box.width, box.height);
36234         }
36235         if(this.tabs){
36236             this.tabs.autoSizeTabs();
36237         }
36238     },
36239
36240     updateBody : function(w, h)
36241     {
36242         if(w !== null){
36243             this.el.setWidth(w);
36244             w -= this.el.getBorderWidth("rl");
36245             if(this.config.adjustments){
36246                 w += this.config.adjustments[0];
36247             }
36248         }
36249         if(h !== null && h > 0){
36250             this.el.setHeight(h);
36251             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36252             h -= this.el.getBorderWidth("tb");
36253             if(this.config.adjustments){
36254                 h += this.config.adjustments[1];
36255             }
36256             this.bodyEl.setHeight(h);
36257             if(this.tabs){
36258                 h = this.tabs.syncHeight(h);
36259             }
36260         }
36261         if(this.panelSize){
36262             w = w !== null ? w : this.panelSize.width;
36263             h = h !== null ? h : this.panelSize.height;
36264         }
36265         if(this.activePanel){
36266             var el = this.activePanel.getEl();
36267             w = w !== null ? w : el.getWidth();
36268             h = h !== null ? h : el.getHeight();
36269             this.panelSize = {width: w, height: h};
36270             this.activePanel.setSize(w, h);
36271         }
36272         if(Roo.isIE && this.tabs){
36273             this.tabs.el.repaint();
36274         }
36275     },
36276
36277     /**
36278      * Returns the container element for this region.
36279      * @return {Roo.Element}
36280      */
36281     getEl : function(){
36282         return this.el;
36283     },
36284
36285     /**
36286      * Hides this region.
36287      */
36288     hide : function(){
36289         //if(!this.collapsed){
36290             this.el.dom.style.left = "-2000px";
36291             this.el.hide();
36292         //}else{
36293          //   this.collapsedEl.dom.style.left = "-2000px";
36294          //   this.collapsedEl.hide();
36295        // }
36296         this.visible = false;
36297         this.fireEvent("visibilitychange", this, false);
36298     },
36299
36300     /**
36301      * Shows this region if it was previously hidden.
36302      */
36303     show : function(){
36304         //if(!this.collapsed){
36305             this.el.show();
36306         //}else{
36307         //    this.collapsedEl.show();
36308        // }
36309         this.visible = true;
36310         this.fireEvent("visibilitychange", this, true);
36311     },
36312 /*
36313     closeClicked : function(){
36314         if(this.activePanel){
36315             this.remove(this.activePanel);
36316         }
36317     },
36318
36319     collapseClick : function(e){
36320         if(this.isSlid){
36321            e.stopPropagation();
36322            this.slideIn();
36323         }else{
36324            e.stopPropagation();
36325            this.slideOut();
36326         }
36327     },
36328 */
36329     /**
36330      * Collapses this region.
36331      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36332      */
36333     /*
36334     collapse : function(skipAnim, skipCheck = false){
36335         if(this.collapsed) {
36336             return;
36337         }
36338         
36339         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36340             
36341             this.collapsed = true;
36342             if(this.split){
36343                 this.split.el.hide();
36344             }
36345             if(this.config.animate && skipAnim !== true){
36346                 this.fireEvent("invalidated", this);
36347                 this.animateCollapse();
36348             }else{
36349                 this.el.setLocation(-20000,-20000);
36350                 this.el.hide();
36351                 this.collapsedEl.show();
36352                 this.fireEvent("collapsed", this);
36353                 this.fireEvent("invalidated", this);
36354             }
36355         }
36356         
36357     },
36358 */
36359     animateCollapse : function(){
36360         // overridden
36361     },
36362
36363     /**
36364      * Expands this region if it was previously collapsed.
36365      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36366      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36367      */
36368     /*
36369     expand : function(e, skipAnim){
36370         if(e) {
36371             e.stopPropagation();
36372         }
36373         if(!this.collapsed || this.el.hasActiveFx()) {
36374             return;
36375         }
36376         if(this.isSlid){
36377             this.afterSlideIn();
36378             skipAnim = true;
36379         }
36380         this.collapsed = false;
36381         if(this.config.animate && skipAnim !== true){
36382             this.animateExpand();
36383         }else{
36384             this.el.show();
36385             if(this.split){
36386                 this.split.el.show();
36387             }
36388             this.collapsedEl.setLocation(-2000,-2000);
36389             this.collapsedEl.hide();
36390             this.fireEvent("invalidated", this);
36391             this.fireEvent("expanded", this);
36392         }
36393     },
36394 */
36395     animateExpand : function(){
36396         // overridden
36397     },
36398
36399     initTabs : function()
36400     {
36401         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36402         
36403         var ts = new Roo.bootstrap.panel.Tabs({
36404             el: this.bodyEl.dom,
36405             region : this,
36406             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
36407             disableTooltips: this.config.disableTabTips,
36408             toolbar : this.config.toolbar
36409         });
36410         
36411         if(this.config.hideTabs){
36412             ts.stripWrap.setDisplayed(false);
36413         }
36414         this.tabs = ts;
36415         ts.resizeTabs = this.config.resizeTabs === true;
36416         ts.minTabWidth = this.config.minTabWidth || 40;
36417         ts.maxTabWidth = this.config.maxTabWidth || 250;
36418         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36419         ts.monitorResize = false;
36420         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36421         ts.bodyEl.addClass('roo-layout-tabs-body');
36422         this.panels.each(this.initPanelAsTab, this);
36423     },
36424
36425     initPanelAsTab : function(panel){
36426         var ti = this.tabs.addTab(
36427             panel.getEl().id,
36428             panel.getTitle(),
36429             null,
36430             this.config.closeOnTab && panel.isClosable(),
36431             panel.tpl
36432         );
36433         if(panel.tabTip !== undefined){
36434             ti.setTooltip(panel.tabTip);
36435         }
36436         ti.on("activate", function(){
36437               this.setActivePanel(panel);
36438         }, this);
36439         
36440         if(this.config.closeOnTab){
36441             ti.on("beforeclose", function(t, e){
36442                 e.cancel = true;
36443                 this.remove(panel);
36444             }, this);
36445         }
36446         
36447         panel.tabItem = ti;
36448         
36449         return ti;
36450     },
36451
36452     updatePanelTitle : function(panel, title)
36453     {
36454         if(this.activePanel == panel){
36455             this.updateTitle(title);
36456         }
36457         if(this.tabs){
36458             var ti = this.tabs.getTab(panel.getEl().id);
36459             ti.setText(title);
36460             if(panel.tabTip !== undefined){
36461                 ti.setTooltip(panel.tabTip);
36462             }
36463         }
36464     },
36465
36466     updateTitle : function(title){
36467         if(this.titleTextEl && !this.config.title){
36468             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36469         }
36470     },
36471
36472     setActivePanel : function(panel)
36473     {
36474         panel = this.getPanel(panel);
36475         if(this.activePanel && this.activePanel != panel){
36476             if(this.activePanel.setActiveState(false) === false){
36477                 return;
36478             }
36479         }
36480         this.activePanel = panel;
36481         panel.setActiveState(true);
36482         if(this.panelSize){
36483             panel.setSize(this.panelSize.width, this.panelSize.height);
36484         }
36485         if(this.closeBtn){
36486             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36487         }
36488         this.updateTitle(panel.getTitle());
36489         if(this.tabs){
36490             this.fireEvent("invalidated", this);
36491         }
36492         this.fireEvent("panelactivated", this, panel);
36493     },
36494
36495     /**
36496      * Shows the specified panel.
36497      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36498      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36499      */
36500     showPanel : function(panel)
36501     {
36502         panel = this.getPanel(panel);
36503         if(panel){
36504             if(this.tabs){
36505                 var tab = this.tabs.getTab(panel.getEl().id);
36506                 if(tab.isHidden()){
36507                     this.tabs.unhideTab(tab.id);
36508                 }
36509                 tab.activate();
36510             }else{
36511                 this.setActivePanel(panel);
36512             }
36513         }
36514         return panel;
36515     },
36516
36517     /**
36518      * Get the active panel for this region.
36519      * @return {Roo.ContentPanel} The active panel or null
36520      */
36521     getActivePanel : function(){
36522         return this.activePanel;
36523     },
36524
36525     validateVisibility : function(){
36526         if(this.panels.getCount() < 1){
36527             this.updateTitle("&#160;");
36528             this.closeBtn.hide();
36529             this.hide();
36530         }else{
36531             if(!this.isVisible()){
36532                 this.show();
36533             }
36534         }
36535     },
36536
36537     /**
36538      * Adds the passed ContentPanel(s) to this region.
36539      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36540      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36541      */
36542     add : function(panel)
36543     {
36544         if(arguments.length > 1){
36545             for(var i = 0, len = arguments.length; i < len; i++) {
36546                 this.add(arguments[i]);
36547             }
36548             return null;
36549         }
36550         
36551         // if we have not been rendered yet, then we can not really do much of this..
36552         if (!this.bodyEl) {
36553             this.unrendered_panels.push(panel);
36554             return panel;
36555         }
36556         
36557         
36558         
36559         
36560         if(this.hasPanel(panel)){
36561             this.showPanel(panel);
36562             return panel;
36563         }
36564         panel.setRegion(this);
36565         this.panels.add(panel);
36566        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36567             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36568             // and hide them... ???
36569             this.bodyEl.dom.appendChild(panel.getEl().dom);
36570             if(panel.background !== true){
36571                 this.setActivePanel(panel);
36572             }
36573             this.fireEvent("paneladded", this, panel);
36574             return panel;
36575         }
36576         */
36577         if(!this.tabs){
36578             this.initTabs();
36579         }else{
36580             this.initPanelAsTab(panel);
36581         }
36582         
36583         
36584         if(panel.background !== true){
36585             this.tabs.activate(panel.getEl().id);
36586         }
36587         this.fireEvent("paneladded", this, panel);
36588         return panel;
36589     },
36590
36591     /**
36592      * Hides the tab for the specified panel.
36593      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36594      */
36595     hidePanel : function(panel){
36596         if(this.tabs && (panel = this.getPanel(panel))){
36597             this.tabs.hideTab(panel.getEl().id);
36598         }
36599     },
36600
36601     /**
36602      * Unhides the tab for a previously hidden panel.
36603      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36604      */
36605     unhidePanel : function(panel){
36606         if(this.tabs && (panel = this.getPanel(panel))){
36607             this.tabs.unhideTab(panel.getEl().id);
36608         }
36609     },
36610
36611     clearPanels : function(){
36612         while(this.panels.getCount() > 0){
36613              this.remove(this.panels.first());
36614         }
36615     },
36616
36617     /**
36618      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36619      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36620      * @param {Boolean} preservePanel Overrides the config preservePanel option
36621      * @return {Roo.ContentPanel} The panel that was removed
36622      */
36623     remove : function(panel, preservePanel)
36624     {
36625         panel = this.getPanel(panel);
36626         if(!panel){
36627             return null;
36628         }
36629         var e = {};
36630         this.fireEvent("beforeremove", this, panel, e);
36631         if(e.cancel === true){
36632             return null;
36633         }
36634         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36635         var panelId = panel.getId();
36636         this.panels.removeKey(panelId);
36637         if(preservePanel){
36638             document.body.appendChild(panel.getEl().dom);
36639         }
36640         if(this.tabs){
36641             this.tabs.removeTab(panel.getEl().id);
36642         }else if (!preservePanel){
36643             this.bodyEl.dom.removeChild(panel.getEl().dom);
36644         }
36645         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36646             var p = this.panels.first();
36647             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36648             tempEl.appendChild(p.getEl().dom);
36649             this.bodyEl.update("");
36650             this.bodyEl.dom.appendChild(p.getEl().dom);
36651             tempEl = null;
36652             this.updateTitle(p.getTitle());
36653             this.tabs = null;
36654             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36655             this.setActivePanel(p);
36656         }
36657         panel.setRegion(null);
36658         if(this.activePanel == panel){
36659             this.activePanel = null;
36660         }
36661         if(this.config.autoDestroy !== false && preservePanel !== true){
36662             try{panel.destroy();}catch(e){}
36663         }
36664         this.fireEvent("panelremoved", this, panel);
36665         return panel;
36666     },
36667
36668     /**
36669      * Returns the TabPanel component used by this region
36670      * @return {Roo.TabPanel}
36671      */
36672     getTabs : function(){
36673         return this.tabs;
36674     },
36675
36676     createTool : function(parentEl, className){
36677         var btn = Roo.DomHelper.append(parentEl, {
36678             tag: "div",
36679             cls: "x-layout-tools-button",
36680             children: [ {
36681                 tag: "div",
36682                 cls: "roo-layout-tools-button-inner " + className,
36683                 html: "&#160;"
36684             }]
36685         }, true);
36686         btn.addClassOnOver("roo-layout-tools-button-over");
36687         return btn;
36688     }
36689 });/*
36690  * Based on:
36691  * Ext JS Library 1.1.1
36692  * Copyright(c) 2006-2007, Ext JS, LLC.
36693  *
36694  * Originally Released Under LGPL - original licence link has changed is not relivant.
36695  *
36696  * Fork - LGPL
36697  * <script type="text/javascript">
36698  */
36699  
36700
36701
36702 /**
36703  * @class Roo.SplitLayoutRegion
36704  * @extends Roo.LayoutRegion
36705  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36706  */
36707 Roo.bootstrap.layout.Split = function(config){
36708     this.cursor = config.cursor;
36709     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36710 };
36711
36712 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36713 {
36714     splitTip : "Drag to resize.",
36715     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36716     useSplitTips : false,
36717
36718     applyConfig : function(config){
36719         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36720     },
36721     
36722     onRender : function(ctr,pos) {
36723         
36724         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36725         if(!this.config.split){
36726             return;
36727         }
36728         if(!this.split){
36729             
36730             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36731                             tag: "div",
36732                             id: this.el.id + "-split",
36733                             cls: "roo-layout-split roo-layout-split-"+this.position,
36734                             html: "&#160;"
36735             });
36736             /** The SplitBar for this region 
36737             * @type Roo.SplitBar */
36738             // does not exist yet...
36739             Roo.log([this.position, this.orientation]);
36740             
36741             this.split = new Roo.bootstrap.SplitBar({
36742                 dragElement : splitEl,
36743                 resizingElement: this.el,
36744                 orientation : this.orientation
36745             });
36746             
36747             this.split.on("moved", this.onSplitMove, this);
36748             this.split.useShim = this.config.useShim === true;
36749             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36750             if(this.useSplitTips){
36751                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36752             }
36753             //if(config.collapsible){
36754             //    this.split.el.on("dblclick", this.collapse,  this);
36755             //}
36756         }
36757         if(typeof this.config.minSize != "undefined"){
36758             this.split.minSize = this.config.minSize;
36759         }
36760         if(typeof this.config.maxSize != "undefined"){
36761             this.split.maxSize = this.config.maxSize;
36762         }
36763         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36764             this.hideSplitter();
36765         }
36766         
36767     },
36768
36769     getHMaxSize : function(){
36770          var cmax = this.config.maxSize || 10000;
36771          var center = this.mgr.getRegion("center");
36772          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36773     },
36774
36775     getVMaxSize : function(){
36776          var cmax = this.config.maxSize || 10000;
36777          var center = this.mgr.getRegion("center");
36778          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36779     },
36780
36781     onSplitMove : function(split, newSize){
36782         this.fireEvent("resized", this, newSize);
36783     },
36784     
36785     /** 
36786      * Returns the {@link Roo.SplitBar} for this region.
36787      * @return {Roo.SplitBar}
36788      */
36789     getSplitBar : function(){
36790         return this.split;
36791     },
36792     
36793     hide : function(){
36794         this.hideSplitter();
36795         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36796     },
36797
36798     hideSplitter : function(){
36799         if(this.split){
36800             this.split.el.setLocation(-2000,-2000);
36801             this.split.el.hide();
36802         }
36803     },
36804
36805     show : function(){
36806         if(this.split){
36807             this.split.el.show();
36808         }
36809         Roo.bootstrap.layout.Split.superclass.show.call(this);
36810     },
36811     
36812     beforeSlide: function(){
36813         if(Roo.isGecko){// firefox overflow auto bug workaround
36814             this.bodyEl.clip();
36815             if(this.tabs) {
36816                 this.tabs.bodyEl.clip();
36817             }
36818             if(this.activePanel){
36819                 this.activePanel.getEl().clip();
36820                 
36821                 if(this.activePanel.beforeSlide){
36822                     this.activePanel.beforeSlide();
36823                 }
36824             }
36825         }
36826     },
36827     
36828     afterSlide : function(){
36829         if(Roo.isGecko){// firefox overflow auto bug workaround
36830             this.bodyEl.unclip();
36831             if(this.tabs) {
36832                 this.tabs.bodyEl.unclip();
36833             }
36834             if(this.activePanel){
36835                 this.activePanel.getEl().unclip();
36836                 if(this.activePanel.afterSlide){
36837                     this.activePanel.afterSlide();
36838                 }
36839             }
36840         }
36841     },
36842
36843     initAutoHide : function(){
36844         if(this.autoHide !== false){
36845             if(!this.autoHideHd){
36846                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36847                 this.autoHideHd = {
36848                     "mouseout": function(e){
36849                         if(!e.within(this.el, true)){
36850                             st.delay(500);
36851                         }
36852                     },
36853                     "mouseover" : function(e){
36854                         st.cancel();
36855                     },
36856                     scope : this
36857                 };
36858             }
36859             this.el.on(this.autoHideHd);
36860         }
36861     },
36862
36863     clearAutoHide : function(){
36864         if(this.autoHide !== false){
36865             this.el.un("mouseout", this.autoHideHd.mouseout);
36866             this.el.un("mouseover", this.autoHideHd.mouseover);
36867         }
36868     },
36869
36870     clearMonitor : function(){
36871         Roo.get(document).un("click", this.slideInIf, this);
36872     },
36873
36874     // these names are backwards but not changed for compat
36875     slideOut : function(){
36876         if(this.isSlid || this.el.hasActiveFx()){
36877             return;
36878         }
36879         this.isSlid = true;
36880         if(this.collapseBtn){
36881             this.collapseBtn.hide();
36882         }
36883         this.closeBtnState = this.closeBtn.getStyle('display');
36884         this.closeBtn.hide();
36885         if(this.stickBtn){
36886             this.stickBtn.show();
36887         }
36888         this.el.show();
36889         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36890         this.beforeSlide();
36891         this.el.setStyle("z-index", 10001);
36892         this.el.slideIn(this.getSlideAnchor(), {
36893             callback: function(){
36894                 this.afterSlide();
36895                 this.initAutoHide();
36896                 Roo.get(document).on("click", this.slideInIf, this);
36897                 this.fireEvent("slideshow", this);
36898             },
36899             scope: this,
36900             block: true
36901         });
36902     },
36903
36904     afterSlideIn : function(){
36905         this.clearAutoHide();
36906         this.isSlid = false;
36907         this.clearMonitor();
36908         this.el.setStyle("z-index", "");
36909         if(this.collapseBtn){
36910             this.collapseBtn.show();
36911         }
36912         this.closeBtn.setStyle('display', this.closeBtnState);
36913         if(this.stickBtn){
36914             this.stickBtn.hide();
36915         }
36916         this.fireEvent("slidehide", this);
36917     },
36918
36919     slideIn : function(cb){
36920         if(!this.isSlid || this.el.hasActiveFx()){
36921             Roo.callback(cb);
36922             return;
36923         }
36924         this.isSlid = false;
36925         this.beforeSlide();
36926         this.el.slideOut(this.getSlideAnchor(), {
36927             callback: function(){
36928                 this.el.setLeftTop(-10000, -10000);
36929                 this.afterSlide();
36930                 this.afterSlideIn();
36931                 Roo.callback(cb);
36932             },
36933             scope: this,
36934             block: true
36935         });
36936     },
36937     
36938     slideInIf : function(e){
36939         if(!e.within(this.el)){
36940             this.slideIn();
36941         }
36942     },
36943
36944     animateCollapse : function(){
36945         this.beforeSlide();
36946         this.el.setStyle("z-index", 20000);
36947         var anchor = this.getSlideAnchor();
36948         this.el.slideOut(anchor, {
36949             callback : function(){
36950                 this.el.setStyle("z-index", "");
36951                 this.collapsedEl.slideIn(anchor, {duration:.3});
36952                 this.afterSlide();
36953                 this.el.setLocation(-10000,-10000);
36954                 this.el.hide();
36955                 this.fireEvent("collapsed", this);
36956             },
36957             scope: this,
36958             block: true
36959         });
36960     },
36961
36962     animateExpand : function(){
36963         this.beforeSlide();
36964         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36965         this.el.setStyle("z-index", 20000);
36966         this.collapsedEl.hide({
36967             duration:.1
36968         });
36969         this.el.slideIn(this.getSlideAnchor(), {
36970             callback : function(){
36971                 this.el.setStyle("z-index", "");
36972                 this.afterSlide();
36973                 if(this.split){
36974                     this.split.el.show();
36975                 }
36976                 this.fireEvent("invalidated", this);
36977                 this.fireEvent("expanded", this);
36978             },
36979             scope: this,
36980             block: true
36981         });
36982     },
36983
36984     anchors : {
36985         "west" : "left",
36986         "east" : "right",
36987         "north" : "top",
36988         "south" : "bottom"
36989     },
36990
36991     sanchors : {
36992         "west" : "l",
36993         "east" : "r",
36994         "north" : "t",
36995         "south" : "b"
36996     },
36997
36998     canchors : {
36999         "west" : "tl-tr",
37000         "east" : "tr-tl",
37001         "north" : "tl-bl",
37002         "south" : "bl-tl"
37003     },
37004
37005     getAnchor : function(){
37006         return this.anchors[this.position];
37007     },
37008
37009     getCollapseAnchor : function(){
37010         return this.canchors[this.position];
37011     },
37012
37013     getSlideAnchor : function(){
37014         return this.sanchors[this.position];
37015     },
37016
37017     getAlignAdj : function(){
37018         var cm = this.cmargins;
37019         switch(this.position){
37020             case "west":
37021                 return [0, 0];
37022             break;
37023             case "east":
37024                 return [0, 0];
37025             break;
37026             case "north":
37027                 return [0, 0];
37028             break;
37029             case "south":
37030                 return [0, 0];
37031             break;
37032         }
37033     },
37034
37035     getExpandAdj : function(){
37036         var c = this.collapsedEl, cm = this.cmargins;
37037         switch(this.position){
37038             case "west":
37039                 return [-(cm.right+c.getWidth()+cm.left), 0];
37040             break;
37041             case "east":
37042                 return [cm.right+c.getWidth()+cm.left, 0];
37043             break;
37044             case "north":
37045                 return [0, -(cm.top+cm.bottom+c.getHeight())];
37046             break;
37047             case "south":
37048                 return [0, cm.top+cm.bottom+c.getHeight()];
37049             break;
37050         }
37051     }
37052 });/*
37053  * Based on:
37054  * Ext JS Library 1.1.1
37055  * Copyright(c) 2006-2007, Ext JS, LLC.
37056  *
37057  * Originally Released Under LGPL - original licence link has changed is not relivant.
37058  *
37059  * Fork - LGPL
37060  * <script type="text/javascript">
37061  */
37062 /*
37063  * These classes are private internal classes
37064  */
37065 Roo.bootstrap.layout.Center = function(config){
37066     config.region = "center";
37067     Roo.bootstrap.layout.Region.call(this, config);
37068     this.visible = true;
37069     this.minWidth = config.minWidth || 20;
37070     this.minHeight = config.minHeight || 20;
37071 };
37072
37073 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
37074     hide : function(){
37075         // center panel can't be hidden
37076     },
37077     
37078     show : function(){
37079         // center panel can't be hidden
37080     },
37081     
37082     getMinWidth: function(){
37083         return this.minWidth;
37084     },
37085     
37086     getMinHeight: function(){
37087         return this.minHeight;
37088     }
37089 });
37090
37091
37092
37093
37094  
37095
37096
37097
37098
37099
37100
37101 Roo.bootstrap.layout.North = function(config)
37102 {
37103     config.region = 'north';
37104     config.cursor = 'n-resize';
37105     
37106     Roo.bootstrap.layout.Split.call(this, config);
37107     
37108     
37109     if(this.split){
37110         this.split.placement = Roo.bootstrap.SplitBar.TOP;
37111         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37112         this.split.el.addClass("roo-layout-split-v");
37113     }
37114     var size = config.initialSize || config.height;
37115     if(typeof size != "undefined"){
37116         this.el.setHeight(size);
37117     }
37118 };
37119 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
37120 {
37121     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37122     
37123     
37124     
37125     getBox : function(){
37126         if(this.collapsed){
37127             return this.collapsedEl.getBox();
37128         }
37129         var box = this.el.getBox();
37130         if(this.split){
37131             box.height += this.split.el.getHeight();
37132         }
37133         return box;
37134     },
37135     
37136     updateBox : function(box){
37137         if(this.split && !this.collapsed){
37138             box.height -= this.split.el.getHeight();
37139             this.split.el.setLeft(box.x);
37140             this.split.el.setTop(box.y+box.height);
37141             this.split.el.setWidth(box.width);
37142         }
37143         if(this.collapsed){
37144             this.updateBody(box.width, null);
37145         }
37146         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37147     }
37148 });
37149
37150
37151
37152
37153
37154 Roo.bootstrap.layout.South = function(config){
37155     config.region = 'south';
37156     config.cursor = 's-resize';
37157     Roo.bootstrap.layout.Split.call(this, config);
37158     if(this.split){
37159         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
37160         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37161         this.split.el.addClass("roo-layout-split-v");
37162     }
37163     var size = config.initialSize || config.height;
37164     if(typeof size != "undefined"){
37165         this.el.setHeight(size);
37166     }
37167 };
37168
37169 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
37170     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37171     getBox : function(){
37172         if(this.collapsed){
37173             return this.collapsedEl.getBox();
37174         }
37175         var box = this.el.getBox();
37176         if(this.split){
37177             var sh = this.split.el.getHeight();
37178             box.height += sh;
37179             box.y -= sh;
37180         }
37181         return box;
37182     },
37183     
37184     updateBox : function(box){
37185         if(this.split && !this.collapsed){
37186             var sh = this.split.el.getHeight();
37187             box.height -= sh;
37188             box.y += sh;
37189             this.split.el.setLeft(box.x);
37190             this.split.el.setTop(box.y-sh);
37191             this.split.el.setWidth(box.width);
37192         }
37193         if(this.collapsed){
37194             this.updateBody(box.width, null);
37195         }
37196         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37197     }
37198 });
37199
37200 Roo.bootstrap.layout.East = function(config){
37201     config.region = "east";
37202     config.cursor = "e-resize";
37203     Roo.bootstrap.layout.Split.call(this, config);
37204     if(this.split){
37205         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
37206         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37207         this.split.el.addClass("roo-layout-split-h");
37208     }
37209     var size = config.initialSize || config.width;
37210     if(typeof size != "undefined"){
37211         this.el.setWidth(size);
37212     }
37213 };
37214 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
37215     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37216     getBox : function(){
37217         if(this.collapsed){
37218             return this.collapsedEl.getBox();
37219         }
37220         var box = this.el.getBox();
37221         if(this.split){
37222             var sw = this.split.el.getWidth();
37223             box.width += sw;
37224             box.x -= sw;
37225         }
37226         return box;
37227     },
37228
37229     updateBox : function(box){
37230         if(this.split && !this.collapsed){
37231             var sw = this.split.el.getWidth();
37232             box.width -= sw;
37233             this.split.el.setLeft(box.x);
37234             this.split.el.setTop(box.y);
37235             this.split.el.setHeight(box.height);
37236             box.x += sw;
37237         }
37238         if(this.collapsed){
37239             this.updateBody(null, box.height);
37240         }
37241         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37242     }
37243 });
37244
37245 Roo.bootstrap.layout.West = function(config){
37246     config.region = "west";
37247     config.cursor = "w-resize";
37248     
37249     Roo.bootstrap.layout.Split.call(this, config);
37250     if(this.split){
37251         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37252         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37253         this.split.el.addClass("roo-layout-split-h");
37254     }
37255     
37256 };
37257 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37258     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37259     
37260     onRender: function(ctr, pos)
37261     {
37262         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37263         var size = this.config.initialSize || this.config.width;
37264         if(typeof size != "undefined"){
37265             this.el.setWidth(size);
37266         }
37267     },
37268     
37269     getBox : function(){
37270         if(this.collapsed){
37271             return this.collapsedEl.getBox();
37272         }
37273         var box = this.el.getBox();
37274         if(this.split){
37275             box.width += this.split.el.getWidth();
37276         }
37277         return box;
37278     },
37279     
37280     updateBox : function(box){
37281         if(this.split && !this.collapsed){
37282             var sw = this.split.el.getWidth();
37283             box.width -= sw;
37284             this.split.el.setLeft(box.x+box.width);
37285             this.split.el.setTop(box.y);
37286             this.split.el.setHeight(box.height);
37287         }
37288         if(this.collapsed){
37289             this.updateBody(null, box.height);
37290         }
37291         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37292     }
37293 });Roo.namespace("Roo.bootstrap.panel");/*
37294  * Based on:
37295  * Ext JS Library 1.1.1
37296  * Copyright(c) 2006-2007, Ext JS, LLC.
37297  *
37298  * Originally Released Under LGPL - original licence link has changed is not relivant.
37299  *
37300  * Fork - LGPL
37301  * <script type="text/javascript">
37302  */
37303 /**
37304  * @class Roo.ContentPanel
37305  * @extends Roo.util.Observable
37306  * A basic ContentPanel element.
37307  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
37308  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
37309  * @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
37310  * @cfg {Boolean}   closable      True if the panel can be closed/removed
37311  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
37312  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37313  * @cfg {Toolbar}   toolbar       A toolbar for this panel
37314  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
37315  * @cfg {String} title          The title for this panel
37316  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37317  * @cfg {String} url            Calls {@link #setUrl} with this value
37318  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37319  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37320  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37321  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37322  * @cfg {Boolean} badges render the badges
37323
37324  * @constructor
37325  * Create a new ContentPanel.
37326  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37327  * @param {String/Object} config A string to set only the title or a config object
37328  * @param {String} content (optional) Set the HTML content for this panel
37329  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37330  */
37331 Roo.bootstrap.panel.Content = function( config){
37332     
37333     this.tpl = config.tpl || false;
37334     
37335     var el = config.el;
37336     var content = config.content;
37337
37338     if(config.autoCreate){ // xtype is available if this is called from factory
37339         el = Roo.id();
37340     }
37341     this.el = Roo.get(el);
37342     if(!this.el && config && config.autoCreate){
37343         if(typeof config.autoCreate == "object"){
37344             if(!config.autoCreate.id){
37345                 config.autoCreate.id = config.id||el;
37346             }
37347             this.el = Roo.DomHelper.append(document.body,
37348                         config.autoCreate, true);
37349         }else{
37350             var elcfg =  {   tag: "div",
37351                             cls: "roo-layout-inactive-content",
37352                             id: config.id||el
37353                             };
37354             if (config.html) {
37355                 elcfg.html = config.html;
37356                 
37357             }
37358                         
37359             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37360         }
37361     } 
37362     this.closable = false;
37363     this.loaded = false;
37364     this.active = false;
37365    
37366       
37367     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37368         
37369         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37370         
37371         this.wrapEl = this.el; //this.el.wrap();
37372         var ti = [];
37373         if (config.toolbar.items) {
37374             ti = config.toolbar.items ;
37375             delete config.toolbar.items ;
37376         }
37377         
37378         var nitems = [];
37379         this.toolbar.render(this.wrapEl, 'before');
37380         for(var i =0;i < ti.length;i++) {
37381           //  Roo.log(['add child', items[i]]);
37382             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37383         }
37384         this.toolbar.items = nitems;
37385         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37386         delete config.toolbar;
37387         
37388     }
37389     /*
37390     // xtype created footer. - not sure if will work as we normally have to render first..
37391     if (this.footer && !this.footer.el && this.footer.xtype) {
37392         if (!this.wrapEl) {
37393             this.wrapEl = this.el.wrap();
37394         }
37395     
37396         this.footer.container = this.wrapEl.createChild();
37397          
37398         this.footer = Roo.factory(this.footer, Roo);
37399         
37400     }
37401     */
37402     
37403      if(typeof config == "string"){
37404         this.title = config;
37405     }else{
37406         Roo.apply(this, config);
37407     }
37408     
37409     if(this.resizeEl){
37410         this.resizeEl = Roo.get(this.resizeEl, true);
37411     }else{
37412         this.resizeEl = this.el;
37413     }
37414     // handle view.xtype
37415     
37416  
37417     
37418     
37419     this.addEvents({
37420         /**
37421          * @event activate
37422          * Fires when this panel is activated. 
37423          * @param {Roo.ContentPanel} this
37424          */
37425         "activate" : true,
37426         /**
37427          * @event deactivate
37428          * Fires when this panel is activated. 
37429          * @param {Roo.ContentPanel} this
37430          */
37431         "deactivate" : true,
37432
37433         /**
37434          * @event resize
37435          * Fires when this panel is resized if fitToFrame is true.
37436          * @param {Roo.ContentPanel} this
37437          * @param {Number} width The width after any component adjustments
37438          * @param {Number} height The height after any component adjustments
37439          */
37440         "resize" : true,
37441         
37442          /**
37443          * @event render
37444          * Fires when this tab is created
37445          * @param {Roo.ContentPanel} this
37446          */
37447         "render" : true
37448         
37449         
37450         
37451     });
37452     
37453
37454     
37455     
37456     if(this.autoScroll){
37457         this.resizeEl.setStyle("overflow", "auto");
37458     } else {
37459         // fix randome scrolling
37460         //this.el.on('scroll', function() {
37461         //    Roo.log('fix random scolling');
37462         //    this.scrollTo('top',0); 
37463         //});
37464     }
37465     content = content || this.content;
37466     if(content){
37467         this.setContent(content);
37468     }
37469     if(config && config.url){
37470         this.setUrl(this.url, this.params, this.loadOnce);
37471     }
37472     
37473     
37474     
37475     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37476     
37477     if (this.view && typeof(this.view.xtype) != 'undefined') {
37478         this.view.el = this.el.appendChild(document.createElement("div"));
37479         this.view = Roo.factory(this.view); 
37480         this.view.render  &&  this.view.render(false, '');  
37481     }
37482     
37483     
37484     this.fireEvent('render', this);
37485 };
37486
37487 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37488     
37489     tabTip : '',
37490     
37491     setRegion : function(region){
37492         this.region = region;
37493         this.setActiveClass(region && !this.background);
37494     },
37495     
37496     
37497     setActiveClass: function(state)
37498     {
37499         if(state){
37500            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37501            this.el.setStyle('position','relative');
37502         }else{
37503            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37504            this.el.setStyle('position', 'absolute');
37505         } 
37506     },
37507     
37508     /**
37509      * Returns the toolbar for this Panel if one was configured. 
37510      * @return {Roo.Toolbar} 
37511      */
37512     getToolbar : function(){
37513         return this.toolbar;
37514     },
37515     
37516     setActiveState : function(active)
37517     {
37518         this.active = active;
37519         this.setActiveClass(active);
37520         if(!active){
37521             if(this.fireEvent("deactivate", this) === false){
37522                 return false;
37523             }
37524             return true;
37525         }
37526         this.fireEvent("activate", this);
37527         return true;
37528     },
37529     /**
37530      * Updates this panel's element
37531      * @param {String} content The new content
37532      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37533     */
37534     setContent : function(content, loadScripts){
37535         this.el.update(content, loadScripts);
37536     },
37537
37538     ignoreResize : function(w, h){
37539         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37540             return true;
37541         }else{
37542             this.lastSize = {width: w, height: h};
37543             return false;
37544         }
37545     },
37546     /**
37547      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37548      * @return {Roo.UpdateManager} The UpdateManager
37549      */
37550     getUpdateManager : function(){
37551         return this.el.getUpdateManager();
37552     },
37553      /**
37554      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37555      * @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:
37556 <pre><code>
37557 panel.load({
37558     url: "your-url.php",
37559     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37560     callback: yourFunction,
37561     scope: yourObject, //(optional scope)
37562     discardUrl: false,
37563     nocache: false,
37564     text: "Loading...",
37565     timeout: 30,
37566     scripts: false
37567 });
37568 </code></pre>
37569      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37570      * 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.
37571      * @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}
37572      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37573      * @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.
37574      * @return {Roo.ContentPanel} this
37575      */
37576     load : function(){
37577         var um = this.el.getUpdateManager();
37578         um.update.apply(um, arguments);
37579         return this;
37580     },
37581
37582
37583     /**
37584      * 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.
37585      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37586      * @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)
37587      * @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)
37588      * @return {Roo.UpdateManager} The UpdateManager
37589      */
37590     setUrl : function(url, params, loadOnce){
37591         if(this.refreshDelegate){
37592             this.removeListener("activate", this.refreshDelegate);
37593         }
37594         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37595         this.on("activate", this.refreshDelegate);
37596         return this.el.getUpdateManager();
37597     },
37598     
37599     _handleRefresh : function(url, params, loadOnce){
37600         if(!loadOnce || !this.loaded){
37601             var updater = this.el.getUpdateManager();
37602             updater.update(url, params, this._setLoaded.createDelegate(this));
37603         }
37604     },
37605     
37606     _setLoaded : function(){
37607         this.loaded = true;
37608     }, 
37609     
37610     /**
37611      * Returns this panel's id
37612      * @return {String} 
37613      */
37614     getId : function(){
37615         return this.el.id;
37616     },
37617     
37618     /** 
37619      * Returns this panel's element - used by regiosn to add.
37620      * @return {Roo.Element} 
37621      */
37622     getEl : function(){
37623         return this.wrapEl || this.el;
37624     },
37625     
37626    
37627     
37628     adjustForComponents : function(width, height)
37629     {
37630         //Roo.log('adjustForComponents ');
37631         if(this.resizeEl != this.el){
37632             width -= this.el.getFrameWidth('lr');
37633             height -= this.el.getFrameWidth('tb');
37634         }
37635         if(this.toolbar){
37636             var te = this.toolbar.getEl();
37637             te.setWidth(width);
37638             height -= te.getHeight();
37639         }
37640         if(this.footer){
37641             var te = this.footer.getEl();
37642             te.setWidth(width);
37643             height -= te.getHeight();
37644         }
37645         
37646         
37647         if(this.adjustments){
37648             width += this.adjustments[0];
37649             height += this.adjustments[1];
37650         }
37651         return {"width": width, "height": height};
37652     },
37653     
37654     setSize : function(width, height){
37655         if(this.fitToFrame && !this.ignoreResize(width, height)){
37656             if(this.fitContainer && this.resizeEl != this.el){
37657                 this.el.setSize(width, height);
37658             }
37659             var size = this.adjustForComponents(width, height);
37660             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37661             this.fireEvent('resize', this, size.width, size.height);
37662         }
37663     },
37664     
37665     /**
37666      * Returns this panel's title
37667      * @return {String} 
37668      */
37669     getTitle : function(){
37670         
37671         if (typeof(this.title) != 'object') {
37672             return this.title;
37673         }
37674         
37675         var t = '';
37676         for (var k in this.title) {
37677             if (!this.title.hasOwnProperty(k)) {
37678                 continue;
37679             }
37680             
37681             if (k.indexOf('-') >= 0) {
37682                 var s = k.split('-');
37683                 for (var i = 0; i<s.length; i++) {
37684                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37685                 }
37686             } else {
37687                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37688             }
37689         }
37690         return t;
37691     },
37692     
37693     /**
37694      * Set this panel's title
37695      * @param {String} title
37696      */
37697     setTitle : function(title){
37698         this.title = title;
37699         if(this.region){
37700             this.region.updatePanelTitle(this, title);
37701         }
37702     },
37703     
37704     /**
37705      * Returns true is this panel was configured to be closable
37706      * @return {Boolean} 
37707      */
37708     isClosable : function(){
37709         return this.closable;
37710     },
37711     
37712     beforeSlide : function(){
37713         this.el.clip();
37714         this.resizeEl.clip();
37715     },
37716     
37717     afterSlide : function(){
37718         this.el.unclip();
37719         this.resizeEl.unclip();
37720     },
37721     
37722     /**
37723      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37724      *   Will fail silently if the {@link #setUrl} method has not been called.
37725      *   This does not activate the panel, just updates its content.
37726      */
37727     refresh : function(){
37728         if(this.refreshDelegate){
37729            this.loaded = false;
37730            this.refreshDelegate();
37731         }
37732     },
37733     
37734     /**
37735      * Destroys this panel
37736      */
37737     destroy : function(){
37738         this.el.removeAllListeners();
37739         var tempEl = document.createElement("span");
37740         tempEl.appendChild(this.el.dom);
37741         tempEl.innerHTML = "";
37742         this.el.remove();
37743         this.el = null;
37744     },
37745     
37746     /**
37747      * form - if the content panel contains a form - this is a reference to it.
37748      * @type {Roo.form.Form}
37749      */
37750     form : false,
37751     /**
37752      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37753      *    This contains a reference to it.
37754      * @type {Roo.View}
37755      */
37756     view : false,
37757     
37758       /**
37759      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37760      * <pre><code>
37761
37762 layout.addxtype({
37763        xtype : 'Form',
37764        items: [ .... ]
37765    }
37766 );
37767
37768 </code></pre>
37769      * @param {Object} cfg Xtype definition of item to add.
37770      */
37771     
37772     
37773     getChildContainer: function () {
37774         return this.getEl();
37775     }
37776     
37777     
37778     /*
37779         var  ret = new Roo.factory(cfg);
37780         return ret;
37781         
37782         
37783         // add form..
37784         if (cfg.xtype.match(/^Form$/)) {
37785             
37786             var el;
37787             //if (this.footer) {
37788             //    el = this.footer.container.insertSibling(false, 'before');
37789             //} else {
37790                 el = this.el.createChild();
37791             //}
37792
37793             this.form = new  Roo.form.Form(cfg);
37794             
37795             
37796             if ( this.form.allItems.length) {
37797                 this.form.render(el.dom);
37798             }
37799             return this.form;
37800         }
37801         // should only have one of theses..
37802         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37803             // views.. should not be just added - used named prop 'view''
37804             
37805             cfg.el = this.el.appendChild(document.createElement("div"));
37806             // factory?
37807             
37808             var ret = new Roo.factory(cfg);
37809              
37810              ret.render && ret.render(false, ''); // render blank..
37811             this.view = ret;
37812             return ret;
37813         }
37814         return false;
37815     }
37816     \*/
37817 });
37818  
37819 /**
37820  * @class Roo.bootstrap.panel.Grid
37821  * @extends Roo.bootstrap.panel.Content
37822  * @constructor
37823  * Create a new GridPanel.
37824  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37825  * @param {Object} config A the config object
37826   
37827  */
37828
37829
37830
37831 Roo.bootstrap.panel.Grid = function(config)
37832 {
37833     
37834       
37835     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37836         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37837
37838     config.el = this.wrapper;
37839     //this.el = this.wrapper;
37840     
37841       if (config.container) {
37842         // ctor'ed from a Border/panel.grid
37843         
37844         
37845         this.wrapper.setStyle("overflow", "hidden");
37846         this.wrapper.addClass('roo-grid-container');
37847
37848     }
37849     
37850     
37851     if(config.toolbar){
37852         var tool_el = this.wrapper.createChild();    
37853         this.toolbar = Roo.factory(config.toolbar);
37854         var ti = [];
37855         if (config.toolbar.items) {
37856             ti = config.toolbar.items ;
37857             delete config.toolbar.items ;
37858         }
37859         
37860         var nitems = [];
37861         this.toolbar.render(tool_el);
37862         for(var i =0;i < ti.length;i++) {
37863           //  Roo.log(['add child', items[i]]);
37864             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37865         }
37866         this.toolbar.items = nitems;
37867         
37868         delete config.toolbar;
37869     }
37870     
37871     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37872     config.grid.scrollBody = true;;
37873     config.grid.monitorWindowResize = false; // turn off autosizing
37874     config.grid.autoHeight = false;
37875     config.grid.autoWidth = false;
37876     
37877     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37878     
37879     if (config.background) {
37880         // render grid on panel activation (if panel background)
37881         this.on('activate', function(gp) {
37882             if (!gp.grid.rendered) {
37883                 gp.grid.render(this.wrapper);
37884                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37885             }
37886         });
37887             
37888     } else {
37889         this.grid.render(this.wrapper);
37890         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37891
37892     }
37893     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37894     // ??? needed ??? config.el = this.wrapper;
37895     
37896     
37897     
37898   
37899     // xtype created footer. - not sure if will work as we normally have to render first..
37900     if (this.footer && !this.footer.el && this.footer.xtype) {
37901         
37902         var ctr = this.grid.getView().getFooterPanel(true);
37903         this.footer.dataSource = this.grid.dataSource;
37904         this.footer = Roo.factory(this.footer, Roo);
37905         this.footer.render(ctr);
37906         
37907     }
37908     
37909     
37910     
37911     
37912      
37913 };
37914
37915 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37916     getId : function(){
37917         return this.grid.id;
37918     },
37919     
37920     /**
37921      * Returns the grid for this panel
37922      * @return {Roo.bootstrap.Table} 
37923      */
37924     getGrid : function(){
37925         return this.grid;    
37926     },
37927     
37928     setSize : function(width, height){
37929         if(!this.ignoreResize(width, height)){
37930             var grid = this.grid;
37931             var size = this.adjustForComponents(width, height);
37932             var gridel = grid.getGridEl();
37933             gridel.setSize(size.width, size.height);
37934             /*
37935             var thd = grid.getGridEl().select('thead',true).first();
37936             var tbd = grid.getGridEl().select('tbody', true).first();
37937             if (tbd) {
37938                 tbd.setSize(width, height - thd.getHeight());
37939             }
37940             */
37941             grid.autoSize();
37942         }
37943     },
37944      
37945     
37946     
37947     beforeSlide : function(){
37948         this.grid.getView().scroller.clip();
37949     },
37950     
37951     afterSlide : function(){
37952         this.grid.getView().scroller.unclip();
37953     },
37954     
37955     destroy : function(){
37956         this.grid.destroy();
37957         delete this.grid;
37958         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37959     }
37960 });
37961
37962 /**
37963  * @class Roo.bootstrap.panel.Nest
37964  * @extends Roo.bootstrap.panel.Content
37965  * @constructor
37966  * Create a new Panel, that can contain a layout.Border.
37967  * 
37968  * 
37969  * @param {Roo.BorderLayout} layout The layout for this panel
37970  * @param {String/Object} config A string to set only the title or a config object
37971  */
37972 Roo.bootstrap.panel.Nest = function(config)
37973 {
37974     // construct with only one argument..
37975     /* FIXME - implement nicer consturctors
37976     if (layout.layout) {
37977         config = layout;
37978         layout = config.layout;
37979         delete config.layout;
37980     }
37981     if (layout.xtype && !layout.getEl) {
37982         // then layout needs constructing..
37983         layout = Roo.factory(layout, Roo);
37984     }
37985     */
37986     
37987     config.el =  config.layout.getEl();
37988     
37989     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37990     
37991     config.layout.monitorWindowResize = false; // turn off autosizing
37992     this.layout = config.layout;
37993     this.layout.getEl().addClass("roo-layout-nested-layout");
37994     this.layout.parent = this;
37995     
37996     
37997     
37998     
37999 };
38000
38001 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
38002
38003     setSize : function(width, height){
38004         if(!this.ignoreResize(width, height)){
38005             var size = this.adjustForComponents(width, height);
38006             var el = this.layout.getEl();
38007             if (size.height < 1) {
38008                 el.setWidth(size.width);   
38009             } else {
38010                 el.setSize(size.width, size.height);
38011             }
38012             var touch = el.dom.offsetWidth;
38013             this.layout.layout();
38014             // ie requires a double layout on the first pass
38015             if(Roo.isIE && !this.initialized){
38016                 this.initialized = true;
38017                 this.layout.layout();
38018             }
38019         }
38020     },
38021     
38022     // activate all subpanels if not currently active..
38023     
38024     setActiveState : function(active){
38025         this.active = active;
38026         this.setActiveClass(active);
38027         
38028         if(!active){
38029             this.fireEvent("deactivate", this);
38030             return;
38031         }
38032         
38033         this.fireEvent("activate", this);
38034         // not sure if this should happen before or after..
38035         if (!this.layout) {
38036             return; // should not happen..
38037         }
38038         var reg = false;
38039         for (var r in this.layout.regions) {
38040             reg = this.layout.getRegion(r);
38041             if (reg.getActivePanel()) {
38042                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
38043                 reg.setActivePanel(reg.getActivePanel());
38044                 continue;
38045             }
38046             if (!reg.panels.length) {
38047                 continue;
38048             }
38049             reg.showPanel(reg.getPanel(0));
38050         }
38051         
38052         
38053         
38054         
38055     },
38056     
38057     /**
38058      * Returns the nested BorderLayout for this panel
38059      * @return {Roo.BorderLayout} 
38060      */
38061     getLayout : function(){
38062         return this.layout;
38063     },
38064     
38065      /**
38066      * Adds a xtype elements to the layout of the nested panel
38067      * <pre><code>
38068
38069 panel.addxtype({
38070        xtype : 'ContentPanel',
38071        region: 'west',
38072        items: [ .... ]
38073    }
38074 );
38075
38076 panel.addxtype({
38077         xtype : 'NestedLayoutPanel',
38078         region: 'west',
38079         layout: {
38080            center: { },
38081            west: { }   
38082         },
38083         items : [ ... list of content panels or nested layout panels.. ]
38084    }
38085 );
38086 </code></pre>
38087      * @param {Object} cfg Xtype definition of item to add.
38088      */
38089     addxtype : function(cfg) {
38090         return this.layout.addxtype(cfg);
38091     
38092     }
38093 });/*
38094  * Based on:
38095  * Ext JS Library 1.1.1
38096  * Copyright(c) 2006-2007, Ext JS, LLC.
38097  *
38098  * Originally Released Under LGPL - original licence link has changed is not relivant.
38099  *
38100  * Fork - LGPL
38101  * <script type="text/javascript">
38102  */
38103 /**
38104  * @class Roo.TabPanel
38105  * @extends Roo.util.Observable
38106  * A lightweight tab container.
38107  * <br><br>
38108  * Usage:
38109  * <pre><code>
38110 // basic tabs 1, built from existing content
38111 var tabs = new Roo.TabPanel("tabs1");
38112 tabs.addTab("script", "View Script");
38113 tabs.addTab("markup", "View Markup");
38114 tabs.activate("script");
38115
38116 // more advanced tabs, built from javascript
38117 var jtabs = new Roo.TabPanel("jtabs");
38118 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
38119
38120 // set up the UpdateManager
38121 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
38122 var updater = tab2.getUpdateManager();
38123 updater.setDefaultUrl("ajax1.htm");
38124 tab2.on('activate', updater.refresh, updater, true);
38125
38126 // Use setUrl for Ajax loading
38127 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
38128 tab3.setUrl("ajax2.htm", null, true);
38129
38130 // Disabled tab
38131 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
38132 tab4.disable();
38133
38134 jtabs.activate("jtabs-1");
38135  * </code></pre>
38136  * @constructor
38137  * Create a new TabPanel.
38138  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
38139  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
38140  */
38141 Roo.bootstrap.panel.Tabs = function(config){
38142     /**
38143     * The container element for this TabPanel.
38144     * @type Roo.Element
38145     */
38146     this.el = Roo.get(config.el);
38147     delete config.el;
38148     if(config){
38149         if(typeof config == "boolean"){
38150             this.tabPosition = config ? "bottom" : "top";
38151         }else{
38152             Roo.apply(this, config);
38153         }
38154     }
38155     
38156     if(this.tabPosition == "bottom"){
38157         // if tabs are at the bottom = create the body first.
38158         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38159         this.el.addClass("roo-tabs-bottom");
38160     }
38161     // next create the tabs holders
38162     
38163     if (this.tabPosition == "west"){
38164         
38165         var reg = this.region; // fake it..
38166         while (reg) {
38167             if (!reg.mgr.parent) {
38168                 break;
38169             }
38170             reg = reg.mgr.parent.region;
38171         }
38172         Roo.log("got nest?");
38173         Roo.log(reg);
38174         if (reg.mgr.getRegion('west')) {
38175             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
38176             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
38177             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38178             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38179             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38180         
38181             
38182         }
38183         
38184         
38185     } else {
38186      
38187         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
38188         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38189         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38190         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38191     }
38192     
38193     
38194     if(Roo.isIE){
38195         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
38196     }
38197     
38198     // finally - if tabs are at the top, then create the body last..
38199     if(this.tabPosition != "bottom"){
38200         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
38201          * @type Roo.Element
38202          */
38203         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38204         this.el.addClass("roo-tabs-top");
38205     }
38206     this.items = [];
38207
38208     this.bodyEl.setStyle("position", "relative");
38209
38210     this.active = null;
38211     this.activateDelegate = this.activate.createDelegate(this);
38212
38213     this.addEvents({
38214         /**
38215          * @event tabchange
38216          * Fires when the active tab changes
38217          * @param {Roo.TabPanel} this
38218          * @param {Roo.TabPanelItem} activePanel The new active tab
38219          */
38220         "tabchange": true,
38221         /**
38222          * @event beforetabchange
38223          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
38224          * @param {Roo.TabPanel} this
38225          * @param {Object} e Set cancel to true on this object to cancel the tab change
38226          * @param {Roo.TabPanelItem} tab The tab being changed to
38227          */
38228         "beforetabchange" : true
38229     });
38230
38231     Roo.EventManager.onWindowResize(this.onResize, this);
38232     this.cpad = this.el.getPadding("lr");
38233     this.hiddenCount = 0;
38234
38235
38236     // toolbar on the tabbar support...
38237     if (this.toolbar) {
38238         alert("no toolbar support yet");
38239         this.toolbar  = false;
38240         /*
38241         var tcfg = this.toolbar;
38242         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
38243         this.toolbar = new Roo.Toolbar(tcfg);
38244         if (Roo.isSafari) {
38245             var tbl = tcfg.container.child('table', true);
38246             tbl.setAttribute('width', '100%');
38247         }
38248         */
38249         
38250     }
38251    
38252
38253
38254     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
38255 };
38256
38257 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
38258     /*
38259      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
38260      */
38261     tabPosition : "top",
38262     /*
38263      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
38264      */
38265     currentTabWidth : 0,
38266     /*
38267      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
38268      */
38269     minTabWidth : 40,
38270     /*
38271      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
38272      */
38273     maxTabWidth : 250,
38274     /*
38275      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
38276      */
38277     preferredTabWidth : 175,
38278     /*
38279      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
38280      */
38281     resizeTabs : false,
38282     /*
38283      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38284      */
38285     monitorResize : true,
38286     /*
38287      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
38288      */
38289     toolbar : false,  // set by caller..
38290     
38291     region : false, /// set by caller
38292     
38293     disableTooltips : true, // not used yet...
38294
38295     /**
38296      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38297      * @param {String} id The id of the div to use <b>or create</b>
38298      * @param {String} text The text for the tab
38299      * @param {String} content (optional) Content to put in the TabPanelItem body
38300      * @param {Boolean} closable (optional) True to create a close icon on the tab
38301      * @return {Roo.TabPanelItem} The created TabPanelItem
38302      */
38303     addTab : function(id, text, content, closable, tpl)
38304     {
38305         var item = new Roo.bootstrap.panel.TabItem({
38306             panel: this,
38307             id : id,
38308             text : text,
38309             closable : closable,
38310             tpl : tpl
38311         });
38312         this.addTabItem(item);
38313         if(content){
38314             item.setContent(content);
38315         }
38316         return item;
38317     },
38318
38319     /**
38320      * Returns the {@link Roo.TabPanelItem} with the specified id/index
38321      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38322      * @return {Roo.TabPanelItem}
38323      */
38324     getTab : function(id){
38325         return this.items[id];
38326     },
38327
38328     /**
38329      * Hides the {@link Roo.TabPanelItem} with the specified id/index
38330      * @param {String/Number} id The id or index of the TabPanelItem to hide.
38331      */
38332     hideTab : function(id){
38333         var t = this.items[id];
38334         if(!t.isHidden()){
38335            t.setHidden(true);
38336            this.hiddenCount++;
38337            this.autoSizeTabs();
38338         }
38339     },
38340
38341     /**
38342      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38343      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38344      */
38345     unhideTab : function(id){
38346         var t = this.items[id];
38347         if(t.isHidden()){
38348            t.setHidden(false);
38349            this.hiddenCount--;
38350            this.autoSizeTabs();
38351         }
38352     },
38353
38354     /**
38355      * Adds an existing {@link Roo.TabPanelItem}.
38356      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38357      */
38358     addTabItem : function(item)
38359     {
38360         this.items[item.id] = item;
38361         this.items.push(item);
38362         this.autoSizeTabs();
38363       //  if(this.resizeTabs){
38364     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38365   //         this.autoSizeTabs();
38366 //        }else{
38367 //            item.autoSize();
38368        // }
38369     },
38370
38371     /**
38372      * Removes a {@link Roo.TabPanelItem}.
38373      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38374      */
38375     removeTab : function(id){
38376         var items = this.items;
38377         var tab = items[id];
38378         if(!tab) { return; }
38379         var index = items.indexOf(tab);
38380         if(this.active == tab && items.length > 1){
38381             var newTab = this.getNextAvailable(index);
38382             if(newTab) {
38383                 newTab.activate();
38384             }
38385         }
38386         this.stripEl.dom.removeChild(tab.pnode.dom);
38387         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38388             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38389         }
38390         items.splice(index, 1);
38391         delete this.items[tab.id];
38392         tab.fireEvent("close", tab);
38393         tab.purgeListeners();
38394         this.autoSizeTabs();
38395     },
38396
38397     getNextAvailable : function(start){
38398         var items = this.items;
38399         var index = start;
38400         // look for a next tab that will slide over to
38401         // replace the one being removed
38402         while(index < items.length){
38403             var item = items[++index];
38404             if(item && !item.isHidden()){
38405                 return item;
38406             }
38407         }
38408         // if one isn't found select the previous tab (on the left)
38409         index = start;
38410         while(index >= 0){
38411             var item = items[--index];
38412             if(item && !item.isHidden()){
38413                 return item;
38414             }
38415         }
38416         return null;
38417     },
38418
38419     /**
38420      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38421      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38422      */
38423     disableTab : function(id){
38424         var tab = this.items[id];
38425         if(tab && this.active != tab){
38426             tab.disable();
38427         }
38428     },
38429
38430     /**
38431      * Enables a {@link Roo.TabPanelItem} that is disabled.
38432      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38433      */
38434     enableTab : function(id){
38435         var tab = this.items[id];
38436         tab.enable();
38437     },
38438
38439     /**
38440      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38441      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38442      * @return {Roo.TabPanelItem} The TabPanelItem.
38443      */
38444     activate : function(id)
38445     {
38446         //Roo.log('activite:'  + id);
38447         
38448         var tab = this.items[id];
38449         if(!tab){
38450             return null;
38451         }
38452         if(tab == this.active || tab.disabled){
38453             return tab;
38454         }
38455         var e = {};
38456         this.fireEvent("beforetabchange", this, e, tab);
38457         if(e.cancel !== true && !tab.disabled){
38458             if(this.active){
38459                 this.active.hide();
38460             }
38461             this.active = this.items[id];
38462             this.active.show();
38463             this.fireEvent("tabchange", this, this.active);
38464         }
38465         return tab;
38466     },
38467
38468     /**
38469      * Gets the active {@link Roo.TabPanelItem}.
38470      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38471      */
38472     getActiveTab : function(){
38473         return this.active;
38474     },
38475
38476     /**
38477      * Updates the tab body element to fit the height of the container element
38478      * for overflow scrolling
38479      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38480      */
38481     syncHeight : function(targetHeight){
38482         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38483         var bm = this.bodyEl.getMargins();
38484         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38485         this.bodyEl.setHeight(newHeight);
38486         return newHeight;
38487     },
38488
38489     onResize : function(){
38490         if(this.monitorResize){
38491             this.autoSizeTabs();
38492         }
38493     },
38494
38495     /**
38496      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38497      */
38498     beginUpdate : function(){
38499         this.updating = true;
38500     },
38501
38502     /**
38503      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38504      */
38505     endUpdate : function(){
38506         this.updating = false;
38507         this.autoSizeTabs();
38508     },
38509
38510     /**
38511      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38512      */
38513     autoSizeTabs : function()
38514     {
38515         var count = this.items.length;
38516         var vcount = count - this.hiddenCount;
38517         
38518         if (vcount < 2) {
38519             this.stripEl.hide();
38520         } else {
38521             this.stripEl.show();
38522         }
38523         
38524         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38525             return;
38526         }
38527         
38528         
38529         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38530         var availWidth = Math.floor(w / vcount);
38531         var b = this.stripBody;
38532         if(b.getWidth() > w){
38533             var tabs = this.items;
38534             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38535             if(availWidth < this.minTabWidth){
38536                 /*if(!this.sleft){    // incomplete scrolling code
38537                     this.createScrollButtons();
38538                 }
38539                 this.showScroll();
38540                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38541             }
38542         }else{
38543             if(this.currentTabWidth < this.preferredTabWidth){
38544                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38545             }
38546         }
38547     },
38548
38549     /**
38550      * Returns the number of tabs in this TabPanel.
38551      * @return {Number}
38552      */
38553      getCount : function(){
38554          return this.items.length;
38555      },
38556
38557     /**
38558      * Resizes all the tabs to the passed width
38559      * @param {Number} The new width
38560      */
38561     setTabWidth : function(width){
38562         this.currentTabWidth = width;
38563         for(var i = 0, len = this.items.length; i < len; i++) {
38564                 if(!this.items[i].isHidden()) {
38565                 this.items[i].setWidth(width);
38566             }
38567         }
38568     },
38569
38570     /**
38571      * Destroys this TabPanel
38572      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38573      */
38574     destroy : function(removeEl){
38575         Roo.EventManager.removeResizeListener(this.onResize, this);
38576         for(var i = 0, len = this.items.length; i < len; i++){
38577             this.items[i].purgeListeners();
38578         }
38579         if(removeEl === true){
38580             this.el.update("");
38581             this.el.remove();
38582         }
38583     },
38584     
38585     createStrip : function(container)
38586     {
38587         var strip = document.createElement("nav");
38588         strip.className = Roo.bootstrap.version == 4 ?
38589             "navbar-light bg-light" : 
38590             "navbar navbar-default"; //"x-tabs-wrap";
38591         container.appendChild(strip);
38592         return strip;
38593     },
38594     
38595     createStripList : function(strip)
38596     {
38597         // div wrapper for retard IE
38598         // returns the "tr" element.
38599         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38600         //'<div class="x-tabs-strip-wrap">'+
38601           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38602           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38603         return strip.firstChild; //.firstChild.firstChild.firstChild;
38604     },
38605     createBody : function(container)
38606     {
38607         var body = document.createElement("div");
38608         Roo.id(body, "tab-body");
38609         //Roo.fly(body).addClass("x-tabs-body");
38610         Roo.fly(body).addClass("tab-content");
38611         container.appendChild(body);
38612         return body;
38613     },
38614     createItemBody :function(bodyEl, id){
38615         var body = Roo.getDom(id);
38616         if(!body){
38617             body = document.createElement("div");
38618             body.id = id;
38619         }
38620         //Roo.fly(body).addClass("x-tabs-item-body");
38621         Roo.fly(body).addClass("tab-pane");
38622          bodyEl.insertBefore(body, bodyEl.firstChild);
38623         return body;
38624     },
38625     /** @private */
38626     createStripElements :  function(stripEl, text, closable, tpl)
38627     {
38628         var td = document.createElement("li"); // was td..
38629         td.className = 'nav-item';
38630         
38631         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38632         
38633         
38634         stripEl.appendChild(td);
38635         /*if(closable){
38636             td.className = "x-tabs-closable";
38637             if(!this.closeTpl){
38638                 this.closeTpl = new Roo.Template(
38639                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38640                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38641                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38642                 );
38643             }
38644             var el = this.closeTpl.overwrite(td, {"text": text});
38645             var close = el.getElementsByTagName("div")[0];
38646             var inner = el.getElementsByTagName("em")[0];
38647             return {"el": el, "close": close, "inner": inner};
38648         } else {
38649         */
38650         // not sure what this is..
38651 //            if(!this.tabTpl){
38652                 //this.tabTpl = new Roo.Template(
38653                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38654                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38655                 //);
38656 //                this.tabTpl = new Roo.Template(
38657 //                   '<a href="#">' +
38658 //                   '<span unselectable="on"' +
38659 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38660 //                            ' >{text}</span></a>'
38661 //                );
38662 //                
38663 //            }
38664
38665
38666             var template = tpl || this.tabTpl || false;
38667             
38668             if(!template){
38669                 template =  new Roo.Template(
38670                         Roo.bootstrap.version == 4 ? 
38671                             (
38672                                 '<a class="nav-link" href="#" unselectable="on"' +
38673                                      (this.disableTooltips ? '' : ' title="{text}"') +
38674                                      ' >{text}</a>'
38675                             ) : (
38676                                 '<a class="nav-link" href="#">' +
38677                                 '<span unselectable="on"' +
38678                                          (this.disableTooltips ? '' : ' title="{text}"') +
38679                                     ' >{text}</span></a>'
38680                             )
38681                 );
38682             }
38683             
38684             switch (typeof(template)) {
38685                 case 'object' :
38686                     break;
38687                 case 'string' :
38688                     template = new Roo.Template(template);
38689                     break;
38690                 default :
38691                     break;
38692             }
38693             
38694             var el = template.overwrite(td, {"text": text});
38695             
38696             var inner = el.getElementsByTagName("span")[0];
38697             
38698             return {"el": el, "inner": inner};
38699             
38700     }
38701         
38702     
38703 });
38704
38705 /**
38706  * @class Roo.TabPanelItem
38707  * @extends Roo.util.Observable
38708  * Represents an individual item (tab plus body) in a TabPanel.
38709  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38710  * @param {String} id The id of this TabPanelItem
38711  * @param {String} text The text for the tab of this TabPanelItem
38712  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38713  */
38714 Roo.bootstrap.panel.TabItem = function(config){
38715     /**
38716      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38717      * @type Roo.TabPanel
38718      */
38719     this.tabPanel = config.panel;
38720     /**
38721      * The id for this TabPanelItem
38722      * @type String
38723      */
38724     this.id = config.id;
38725     /** @private */
38726     this.disabled = false;
38727     /** @private */
38728     this.text = config.text;
38729     /** @private */
38730     this.loaded = false;
38731     this.closable = config.closable;
38732
38733     /**
38734      * The body element for this TabPanelItem.
38735      * @type Roo.Element
38736      */
38737     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38738     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38739     this.bodyEl.setStyle("display", "block");
38740     this.bodyEl.setStyle("zoom", "1");
38741     //this.hideAction();
38742
38743     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38744     /** @private */
38745     this.el = Roo.get(els.el);
38746     this.inner = Roo.get(els.inner, true);
38747      this.textEl = Roo.bootstrap.version == 4 ?
38748         this.el : Roo.get(this.el.dom.firstChild, true);
38749
38750     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
38751     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
38752
38753     
38754 //    this.el.on("mousedown", this.onTabMouseDown, this);
38755     this.el.on("click", this.onTabClick, this);
38756     /** @private */
38757     if(config.closable){
38758         var c = Roo.get(els.close, true);
38759         c.dom.title = this.closeText;
38760         c.addClassOnOver("close-over");
38761         c.on("click", this.closeClick, this);
38762      }
38763
38764     this.addEvents({
38765          /**
38766          * @event activate
38767          * Fires when this tab becomes the active tab.
38768          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38769          * @param {Roo.TabPanelItem} this
38770          */
38771         "activate": true,
38772         /**
38773          * @event beforeclose
38774          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38775          * @param {Roo.TabPanelItem} this
38776          * @param {Object} e Set cancel to true on this object to cancel the close.
38777          */
38778         "beforeclose": true,
38779         /**
38780          * @event close
38781          * Fires when this tab is closed.
38782          * @param {Roo.TabPanelItem} this
38783          */
38784          "close": true,
38785         /**
38786          * @event deactivate
38787          * Fires when this tab is no longer the active tab.
38788          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38789          * @param {Roo.TabPanelItem} this
38790          */
38791          "deactivate" : true
38792     });
38793     this.hidden = false;
38794
38795     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38796 };
38797
38798 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38799            {
38800     purgeListeners : function(){
38801        Roo.util.Observable.prototype.purgeListeners.call(this);
38802        this.el.removeAllListeners();
38803     },
38804     /**
38805      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38806      */
38807     show : function(){
38808         this.status_node.addClass("active");
38809         this.showAction();
38810         if(Roo.isOpera){
38811             this.tabPanel.stripWrap.repaint();
38812         }
38813         this.fireEvent("activate", this.tabPanel, this);
38814     },
38815
38816     /**
38817      * Returns true if this tab is the active tab.
38818      * @return {Boolean}
38819      */
38820     isActive : function(){
38821         return this.tabPanel.getActiveTab() == this;
38822     },
38823
38824     /**
38825      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38826      */
38827     hide : function(){
38828         this.status_node.removeClass("active");
38829         this.hideAction();
38830         this.fireEvent("deactivate", this.tabPanel, this);
38831     },
38832
38833     hideAction : function(){
38834         this.bodyEl.hide();
38835         this.bodyEl.setStyle("position", "absolute");
38836         this.bodyEl.setLeft("-20000px");
38837         this.bodyEl.setTop("-20000px");
38838     },
38839
38840     showAction : function(){
38841         this.bodyEl.setStyle("position", "relative");
38842         this.bodyEl.setTop("");
38843         this.bodyEl.setLeft("");
38844         this.bodyEl.show();
38845     },
38846
38847     /**
38848      * Set the tooltip for the tab.
38849      * @param {String} tooltip The tab's tooltip
38850      */
38851     setTooltip : function(text){
38852         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38853             this.textEl.dom.qtip = text;
38854             this.textEl.dom.removeAttribute('title');
38855         }else{
38856             this.textEl.dom.title = text;
38857         }
38858     },
38859
38860     onTabClick : function(e){
38861         e.preventDefault();
38862         this.tabPanel.activate(this.id);
38863     },
38864
38865     onTabMouseDown : function(e){
38866         e.preventDefault();
38867         this.tabPanel.activate(this.id);
38868     },
38869 /*
38870     getWidth : function(){
38871         return this.inner.getWidth();
38872     },
38873
38874     setWidth : function(width){
38875         var iwidth = width - this.linode.getPadding("lr");
38876         this.inner.setWidth(iwidth);
38877         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38878         this.linode.setWidth(width);
38879     },
38880 */
38881     /**
38882      * Show or hide the tab
38883      * @param {Boolean} hidden True to hide or false to show.
38884      */
38885     setHidden : function(hidden){
38886         this.hidden = hidden;
38887         this.linode.setStyle("display", hidden ? "none" : "");
38888     },
38889
38890     /**
38891      * Returns true if this tab is "hidden"
38892      * @return {Boolean}
38893      */
38894     isHidden : function(){
38895         return this.hidden;
38896     },
38897
38898     /**
38899      * Returns the text for this tab
38900      * @return {String}
38901      */
38902     getText : function(){
38903         return this.text;
38904     },
38905     /*
38906     autoSize : function(){
38907         //this.el.beginMeasure();
38908         this.textEl.setWidth(1);
38909         /*
38910          *  #2804 [new] Tabs in Roojs
38911          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38912          */
38913         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38914         //this.el.endMeasure();
38915     //},
38916
38917     /**
38918      * Sets the text for the tab (Note: this also sets the tooltip text)
38919      * @param {String} text The tab's text and tooltip
38920      */
38921     setText : function(text){
38922         this.text = text;
38923         this.textEl.update(text);
38924         this.setTooltip(text);
38925         //if(!this.tabPanel.resizeTabs){
38926         //    this.autoSize();
38927         //}
38928     },
38929     /**
38930      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38931      */
38932     activate : function(){
38933         this.tabPanel.activate(this.id);
38934     },
38935
38936     /**
38937      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38938      */
38939     disable : function(){
38940         if(this.tabPanel.active != this){
38941             this.disabled = true;
38942             this.status_node.addClass("disabled");
38943         }
38944     },
38945
38946     /**
38947      * Enables this TabPanelItem if it was previously disabled.
38948      */
38949     enable : function(){
38950         this.disabled = false;
38951         this.status_node.removeClass("disabled");
38952     },
38953
38954     /**
38955      * Sets the content for this TabPanelItem.
38956      * @param {String} content The content
38957      * @param {Boolean} loadScripts true to look for and load scripts
38958      */
38959     setContent : function(content, loadScripts){
38960         this.bodyEl.update(content, loadScripts);
38961     },
38962
38963     /**
38964      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38965      * @return {Roo.UpdateManager} The UpdateManager
38966      */
38967     getUpdateManager : function(){
38968         return this.bodyEl.getUpdateManager();
38969     },
38970
38971     /**
38972      * Set a URL to be used to load the content for this TabPanelItem.
38973      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38974      * @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)
38975      * @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)
38976      * @return {Roo.UpdateManager} The UpdateManager
38977      */
38978     setUrl : function(url, params, loadOnce){
38979         if(this.refreshDelegate){
38980             this.un('activate', this.refreshDelegate);
38981         }
38982         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38983         this.on("activate", this.refreshDelegate);
38984         return this.bodyEl.getUpdateManager();
38985     },
38986
38987     /** @private */
38988     _handleRefresh : function(url, params, loadOnce){
38989         if(!loadOnce || !this.loaded){
38990             var updater = this.bodyEl.getUpdateManager();
38991             updater.update(url, params, this._setLoaded.createDelegate(this));
38992         }
38993     },
38994
38995     /**
38996      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38997      *   Will fail silently if the setUrl method has not been called.
38998      *   This does not activate the panel, just updates its content.
38999      */
39000     refresh : function(){
39001         if(this.refreshDelegate){
39002            this.loaded = false;
39003            this.refreshDelegate();
39004         }
39005     },
39006
39007     /** @private */
39008     _setLoaded : function(){
39009         this.loaded = true;
39010     },
39011
39012     /** @private */
39013     closeClick : function(e){
39014         var o = {};
39015         e.stopEvent();
39016         this.fireEvent("beforeclose", this, o);
39017         if(o.cancel !== true){
39018             this.tabPanel.removeTab(this.id);
39019         }
39020     },
39021     /**
39022      * The text displayed in the tooltip for the close icon.
39023      * @type String
39024      */
39025     closeText : "Close this tab"
39026 });
39027 /**
39028 *    This script refer to:
39029 *    Title: International Telephone Input
39030 *    Author: Jack O'Connor
39031 *    Code version:  v12.1.12
39032 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39033 **/
39034
39035 Roo.bootstrap.PhoneInputData = function() {
39036     var d = [
39037       [
39038         "Afghanistan (‫افغانستان‬‎)",
39039         "af",
39040         "93"
39041       ],
39042       [
39043         "Albania (Shqipëri)",
39044         "al",
39045         "355"
39046       ],
39047       [
39048         "Algeria (‫الجزائر‬‎)",
39049         "dz",
39050         "213"
39051       ],
39052       [
39053         "American Samoa",
39054         "as",
39055         "1684"
39056       ],
39057       [
39058         "Andorra",
39059         "ad",
39060         "376"
39061       ],
39062       [
39063         "Angola",
39064         "ao",
39065         "244"
39066       ],
39067       [
39068         "Anguilla",
39069         "ai",
39070         "1264"
39071       ],
39072       [
39073         "Antigua and Barbuda",
39074         "ag",
39075         "1268"
39076       ],
39077       [
39078         "Argentina",
39079         "ar",
39080         "54"
39081       ],
39082       [
39083         "Armenia (Հայաստան)",
39084         "am",
39085         "374"
39086       ],
39087       [
39088         "Aruba",
39089         "aw",
39090         "297"
39091       ],
39092       [
39093         "Australia",
39094         "au",
39095         "61",
39096         0
39097       ],
39098       [
39099         "Austria (Österreich)",
39100         "at",
39101         "43"
39102       ],
39103       [
39104         "Azerbaijan (Azərbaycan)",
39105         "az",
39106         "994"
39107       ],
39108       [
39109         "Bahamas",
39110         "bs",
39111         "1242"
39112       ],
39113       [
39114         "Bahrain (‫البحرين‬‎)",
39115         "bh",
39116         "973"
39117       ],
39118       [
39119         "Bangladesh (বাংলাদেশ)",
39120         "bd",
39121         "880"
39122       ],
39123       [
39124         "Barbados",
39125         "bb",
39126         "1246"
39127       ],
39128       [
39129         "Belarus (Беларусь)",
39130         "by",
39131         "375"
39132       ],
39133       [
39134         "Belgium (België)",
39135         "be",
39136         "32"
39137       ],
39138       [
39139         "Belize",
39140         "bz",
39141         "501"
39142       ],
39143       [
39144         "Benin (Bénin)",
39145         "bj",
39146         "229"
39147       ],
39148       [
39149         "Bermuda",
39150         "bm",
39151         "1441"
39152       ],
39153       [
39154         "Bhutan (འབྲུག)",
39155         "bt",
39156         "975"
39157       ],
39158       [
39159         "Bolivia",
39160         "bo",
39161         "591"
39162       ],
39163       [
39164         "Bosnia and Herzegovina (Босна и Херцеговина)",
39165         "ba",
39166         "387"
39167       ],
39168       [
39169         "Botswana",
39170         "bw",
39171         "267"
39172       ],
39173       [
39174         "Brazil (Brasil)",
39175         "br",
39176         "55"
39177       ],
39178       [
39179         "British Indian Ocean Territory",
39180         "io",
39181         "246"
39182       ],
39183       [
39184         "British Virgin Islands",
39185         "vg",
39186         "1284"
39187       ],
39188       [
39189         "Brunei",
39190         "bn",
39191         "673"
39192       ],
39193       [
39194         "Bulgaria (България)",
39195         "bg",
39196         "359"
39197       ],
39198       [
39199         "Burkina Faso",
39200         "bf",
39201         "226"
39202       ],
39203       [
39204         "Burundi (Uburundi)",
39205         "bi",
39206         "257"
39207       ],
39208       [
39209         "Cambodia (កម្ពុជា)",
39210         "kh",
39211         "855"
39212       ],
39213       [
39214         "Cameroon (Cameroun)",
39215         "cm",
39216         "237"
39217       ],
39218       [
39219         "Canada",
39220         "ca",
39221         "1",
39222         1,
39223         ["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"]
39224       ],
39225       [
39226         "Cape Verde (Kabu Verdi)",
39227         "cv",
39228         "238"
39229       ],
39230       [
39231         "Caribbean Netherlands",
39232         "bq",
39233         "599",
39234         1
39235       ],
39236       [
39237         "Cayman Islands",
39238         "ky",
39239         "1345"
39240       ],
39241       [
39242         "Central African Republic (République centrafricaine)",
39243         "cf",
39244         "236"
39245       ],
39246       [
39247         "Chad (Tchad)",
39248         "td",
39249         "235"
39250       ],
39251       [
39252         "Chile",
39253         "cl",
39254         "56"
39255       ],
39256       [
39257         "China (中国)",
39258         "cn",
39259         "86"
39260       ],
39261       [
39262         "Christmas Island",
39263         "cx",
39264         "61",
39265         2
39266       ],
39267       [
39268         "Cocos (Keeling) Islands",
39269         "cc",
39270         "61",
39271         1
39272       ],
39273       [
39274         "Colombia",
39275         "co",
39276         "57"
39277       ],
39278       [
39279         "Comoros (‫جزر القمر‬‎)",
39280         "km",
39281         "269"
39282       ],
39283       [
39284         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
39285         "cd",
39286         "243"
39287       ],
39288       [
39289         "Congo (Republic) (Congo-Brazzaville)",
39290         "cg",
39291         "242"
39292       ],
39293       [
39294         "Cook Islands",
39295         "ck",
39296         "682"
39297       ],
39298       [
39299         "Costa Rica",
39300         "cr",
39301         "506"
39302       ],
39303       [
39304         "Côte d’Ivoire",
39305         "ci",
39306         "225"
39307       ],
39308       [
39309         "Croatia (Hrvatska)",
39310         "hr",
39311         "385"
39312       ],
39313       [
39314         "Cuba",
39315         "cu",
39316         "53"
39317       ],
39318       [
39319         "Curaçao",
39320         "cw",
39321         "599",
39322         0
39323       ],
39324       [
39325         "Cyprus (Κύπρος)",
39326         "cy",
39327         "357"
39328       ],
39329       [
39330         "Czech Republic (Česká republika)",
39331         "cz",
39332         "420"
39333       ],
39334       [
39335         "Denmark (Danmark)",
39336         "dk",
39337         "45"
39338       ],
39339       [
39340         "Djibouti",
39341         "dj",
39342         "253"
39343       ],
39344       [
39345         "Dominica",
39346         "dm",
39347         "1767"
39348       ],
39349       [
39350         "Dominican Republic (República Dominicana)",
39351         "do",
39352         "1",
39353         2,
39354         ["809", "829", "849"]
39355       ],
39356       [
39357         "Ecuador",
39358         "ec",
39359         "593"
39360       ],
39361       [
39362         "Egypt (‫مصر‬‎)",
39363         "eg",
39364         "20"
39365       ],
39366       [
39367         "El Salvador",
39368         "sv",
39369         "503"
39370       ],
39371       [
39372         "Equatorial Guinea (Guinea Ecuatorial)",
39373         "gq",
39374         "240"
39375       ],
39376       [
39377         "Eritrea",
39378         "er",
39379         "291"
39380       ],
39381       [
39382         "Estonia (Eesti)",
39383         "ee",
39384         "372"
39385       ],
39386       [
39387         "Ethiopia",
39388         "et",
39389         "251"
39390       ],
39391       [
39392         "Falkland Islands (Islas Malvinas)",
39393         "fk",
39394         "500"
39395       ],
39396       [
39397         "Faroe Islands (Føroyar)",
39398         "fo",
39399         "298"
39400       ],
39401       [
39402         "Fiji",
39403         "fj",
39404         "679"
39405       ],
39406       [
39407         "Finland (Suomi)",
39408         "fi",
39409         "358",
39410         0
39411       ],
39412       [
39413         "France",
39414         "fr",
39415         "33"
39416       ],
39417       [
39418         "French Guiana (Guyane française)",
39419         "gf",
39420         "594"
39421       ],
39422       [
39423         "French Polynesia (Polynésie française)",
39424         "pf",
39425         "689"
39426       ],
39427       [
39428         "Gabon",
39429         "ga",
39430         "241"
39431       ],
39432       [
39433         "Gambia",
39434         "gm",
39435         "220"
39436       ],
39437       [
39438         "Georgia (საქართველო)",
39439         "ge",
39440         "995"
39441       ],
39442       [
39443         "Germany (Deutschland)",
39444         "de",
39445         "49"
39446       ],
39447       [
39448         "Ghana (Gaana)",
39449         "gh",
39450         "233"
39451       ],
39452       [
39453         "Gibraltar",
39454         "gi",
39455         "350"
39456       ],
39457       [
39458         "Greece (Ελλάδα)",
39459         "gr",
39460         "30"
39461       ],
39462       [
39463         "Greenland (Kalaallit Nunaat)",
39464         "gl",
39465         "299"
39466       ],
39467       [
39468         "Grenada",
39469         "gd",
39470         "1473"
39471       ],
39472       [
39473         "Guadeloupe",
39474         "gp",
39475         "590",
39476         0
39477       ],
39478       [
39479         "Guam",
39480         "gu",
39481         "1671"
39482       ],
39483       [
39484         "Guatemala",
39485         "gt",
39486         "502"
39487       ],
39488       [
39489         "Guernsey",
39490         "gg",
39491         "44",
39492         1
39493       ],
39494       [
39495         "Guinea (Guinée)",
39496         "gn",
39497         "224"
39498       ],
39499       [
39500         "Guinea-Bissau (Guiné Bissau)",
39501         "gw",
39502         "245"
39503       ],
39504       [
39505         "Guyana",
39506         "gy",
39507         "592"
39508       ],
39509       [
39510         "Haiti",
39511         "ht",
39512         "509"
39513       ],
39514       [
39515         "Honduras",
39516         "hn",
39517         "504"
39518       ],
39519       [
39520         "Hong Kong (香港)",
39521         "hk",
39522         "852"
39523       ],
39524       [
39525         "Hungary (Magyarország)",
39526         "hu",
39527         "36"
39528       ],
39529       [
39530         "Iceland (Ísland)",
39531         "is",
39532         "354"
39533       ],
39534       [
39535         "India (भारत)",
39536         "in",
39537         "91"
39538       ],
39539       [
39540         "Indonesia",
39541         "id",
39542         "62"
39543       ],
39544       [
39545         "Iran (‫ایران‬‎)",
39546         "ir",
39547         "98"
39548       ],
39549       [
39550         "Iraq (‫العراق‬‎)",
39551         "iq",
39552         "964"
39553       ],
39554       [
39555         "Ireland",
39556         "ie",
39557         "353"
39558       ],
39559       [
39560         "Isle of Man",
39561         "im",
39562         "44",
39563         2
39564       ],
39565       [
39566         "Israel (‫ישראל‬‎)",
39567         "il",
39568         "972"
39569       ],
39570       [
39571         "Italy (Italia)",
39572         "it",
39573         "39",
39574         0
39575       ],
39576       [
39577         "Jamaica",
39578         "jm",
39579         "1876"
39580       ],
39581       [
39582         "Japan (日本)",
39583         "jp",
39584         "81"
39585       ],
39586       [
39587         "Jersey",
39588         "je",
39589         "44",
39590         3
39591       ],
39592       [
39593         "Jordan (‫الأردن‬‎)",
39594         "jo",
39595         "962"
39596       ],
39597       [
39598         "Kazakhstan (Казахстан)",
39599         "kz",
39600         "7",
39601         1
39602       ],
39603       [
39604         "Kenya",
39605         "ke",
39606         "254"
39607       ],
39608       [
39609         "Kiribati",
39610         "ki",
39611         "686"
39612       ],
39613       [
39614         "Kosovo",
39615         "xk",
39616         "383"
39617       ],
39618       [
39619         "Kuwait (‫الكويت‬‎)",
39620         "kw",
39621         "965"
39622       ],
39623       [
39624         "Kyrgyzstan (Кыргызстан)",
39625         "kg",
39626         "996"
39627       ],
39628       [
39629         "Laos (ລາວ)",
39630         "la",
39631         "856"
39632       ],
39633       [
39634         "Latvia (Latvija)",
39635         "lv",
39636         "371"
39637       ],
39638       [
39639         "Lebanon (‫لبنان‬‎)",
39640         "lb",
39641         "961"
39642       ],
39643       [
39644         "Lesotho",
39645         "ls",
39646         "266"
39647       ],
39648       [
39649         "Liberia",
39650         "lr",
39651         "231"
39652       ],
39653       [
39654         "Libya (‫ليبيا‬‎)",
39655         "ly",
39656         "218"
39657       ],
39658       [
39659         "Liechtenstein",
39660         "li",
39661         "423"
39662       ],
39663       [
39664         "Lithuania (Lietuva)",
39665         "lt",
39666         "370"
39667       ],
39668       [
39669         "Luxembourg",
39670         "lu",
39671         "352"
39672       ],
39673       [
39674         "Macau (澳門)",
39675         "mo",
39676         "853"
39677       ],
39678       [
39679         "Macedonia (FYROM) (Македонија)",
39680         "mk",
39681         "389"
39682       ],
39683       [
39684         "Madagascar (Madagasikara)",
39685         "mg",
39686         "261"
39687       ],
39688       [
39689         "Malawi",
39690         "mw",
39691         "265"
39692       ],
39693       [
39694         "Malaysia",
39695         "my",
39696         "60"
39697       ],
39698       [
39699         "Maldives",
39700         "mv",
39701         "960"
39702       ],
39703       [
39704         "Mali",
39705         "ml",
39706         "223"
39707       ],
39708       [
39709         "Malta",
39710         "mt",
39711         "356"
39712       ],
39713       [
39714         "Marshall Islands",
39715         "mh",
39716         "692"
39717       ],
39718       [
39719         "Martinique",
39720         "mq",
39721         "596"
39722       ],
39723       [
39724         "Mauritania (‫موريتانيا‬‎)",
39725         "mr",
39726         "222"
39727       ],
39728       [
39729         "Mauritius (Moris)",
39730         "mu",
39731         "230"
39732       ],
39733       [
39734         "Mayotte",
39735         "yt",
39736         "262",
39737         1
39738       ],
39739       [
39740         "Mexico (México)",
39741         "mx",
39742         "52"
39743       ],
39744       [
39745         "Micronesia",
39746         "fm",
39747         "691"
39748       ],
39749       [
39750         "Moldova (Republica Moldova)",
39751         "md",
39752         "373"
39753       ],
39754       [
39755         "Monaco",
39756         "mc",
39757         "377"
39758       ],
39759       [
39760         "Mongolia (Монгол)",
39761         "mn",
39762         "976"
39763       ],
39764       [
39765         "Montenegro (Crna Gora)",
39766         "me",
39767         "382"
39768       ],
39769       [
39770         "Montserrat",
39771         "ms",
39772         "1664"
39773       ],
39774       [
39775         "Morocco (‫المغرب‬‎)",
39776         "ma",
39777         "212",
39778         0
39779       ],
39780       [
39781         "Mozambique (Moçambique)",
39782         "mz",
39783         "258"
39784       ],
39785       [
39786         "Myanmar (Burma) (မြန်မာ)",
39787         "mm",
39788         "95"
39789       ],
39790       [
39791         "Namibia (Namibië)",
39792         "na",
39793         "264"
39794       ],
39795       [
39796         "Nauru",
39797         "nr",
39798         "674"
39799       ],
39800       [
39801         "Nepal (नेपाल)",
39802         "np",
39803         "977"
39804       ],
39805       [
39806         "Netherlands (Nederland)",
39807         "nl",
39808         "31"
39809       ],
39810       [
39811         "New Caledonia (Nouvelle-Calédonie)",
39812         "nc",
39813         "687"
39814       ],
39815       [
39816         "New Zealand",
39817         "nz",
39818         "64"
39819       ],
39820       [
39821         "Nicaragua",
39822         "ni",
39823         "505"
39824       ],
39825       [
39826         "Niger (Nijar)",
39827         "ne",
39828         "227"
39829       ],
39830       [
39831         "Nigeria",
39832         "ng",
39833         "234"
39834       ],
39835       [
39836         "Niue",
39837         "nu",
39838         "683"
39839       ],
39840       [
39841         "Norfolk Island",
39842         "nf",
39843         "672"
39844       ],
39845       [
39846         "North Korea (조선 민주주의 인민 공화국)",
39847         "kp",
39848         "850"
39849       ],
39850       [
39851         "Northern Mariana Islands",
39852         "mp",
39853         "1670"
39854       ],
39855       [
39856         "Norway (Norge)",
39857         "no",
39858         "47",
39859         0
39860       ],
39861       [
39862         "Oman (‫عُمان‬‎)",
39863         "om",
39864         "968"
39865       ],
39866       [
39867         "Pakistan (‫پاکستان‬‎)",
39868         "pk",
39869         "92"
39870       ],
39871       [
39872         "Palau",
39873         "pw",
39874         "680"
39875       ],
39876       [
39877         "Palestine (‫فلسطين‬‎)",
39878         "ps",
39879         "970"
39880       ],
39881       [
39882         "Panama (Panamá)",
39883         "pa",
39884         "507"
39885       ],
39886       [
39887         "Papua New Guinea",
39888         "pg",
39889         "675"
39890       ],
39891       [
39892         "Paraguay",
39893         "py",
39894         "595"
39895       ],
39896       [
39897         "Peru (Perú)",
39898         "pe",
39899         "51"
39900       ],
39901       [
39902         "Philippines",
39903         "ph",
39904         "63"
39905       ],
39906       [
39907         "Poland (Polska)",
39908         "pl",
39909         "48"
39910       ],
39911       [
39912         "Portugal",
39913         "pt",
39914         "351"
39915       ],
39916       [
39917         "Puerto Rico",
39918         "pr",
39919         "1",
39920         3,
39921         ["787", "939"]
39922       ],
39923       [
39924         "Qatar (‫قطر‬‎)",
39925         "qa",
39926         "974"
39927       ],
39928       [
39929         "Réunion (La Réunion)",
39930         "re",
39931         "262",
39932         0
39933       ],
39934       [
39935         "Romania (România)",
39936         "ro",
39937         "40"
39938       ],
39939       [
39940         "Russia (Россия)",
39941         "ru",
39942         "7",
39943         0
39944       ],
39945       [
39946         "Rwanda",
39947         "rw",
39948         "250"
39949       ],
39950       [
39951         "Saint Barthélemy",
39952         "bl",
39953         "590",
39954         1
39955       ],
39956       [
39957         "Saint Helena",
39958         "sh",
39959         "290"
39960       ],
39961       [
39962         "Saint Kitts and Nevis",
39963         "kn",
39964         "1869"
39965       ],
39966       [
39967         "Saint Lucia",
39968         "lc",
39969         "1758"
39970       ],
39971       [
39972         "Saint Martin (Saint-Martin (partie française))",
39973         "mf",
39974         "590",
39975         2
39976       ],
39977       [
39978         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39979         "pm",
39980         "508"
39981       ],
39982       [
39983         "Saint Vincent and the Grenadines",
39984         "vc",
39985         "1784"
39986       ],
39987       [
39988         "Samoa",
39989         "ws",
39990         "685"
39991       ],
39992       [
39993         "San Marino",
39994         "sm",
39995         "378"
39996       ],
39997       [
39998         "São Tomé and Príncipe (São Tomé e Príncipe)",
39999         "st",
40000         "239"
40001       ],
40002       [
40003         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
40004         "sa",
40005         "966"
40006       ],
40007       [
40008         "Senegal (Sénégal)",
40009         "sn",
40010         "221"
40011       ],
40012       [
40013         "Serbia (Србија)",
40014         "rs",
40015         "381"
40016       ],
40017       [
40018         "Seychelles",
40019         "sc",
40020         "248"
40021       ],
40022       [
40023         "Sierra Leone",
40024         "sl",
40025         "232"
40026       ],
40027       [
40028         "Singapore",
40029         "sg",
40030         "65"
40031       ],
40032       [
40033         "Sint Maarten",
40034         "sx",
40035         "1721"
40036       ],
40037       [
40038         "Slovakia (Slovensko)",
40039         "sk",
40040         "421"
40041       ],
40042       [
40043         "Slovenia (Slovenija)",
40044         "si",
40045         "386"
40046       ],
40047       [
40048         "Solomon Islands",
40049         "sb",
40050         "677"
40051       ],
40052       [
40053         "Somalia (Soomaaliya)",
40054         "so",
40055         "252"
40056       ],
40057       [
40058         "South Africa",
40059         "za",
40060         "27"
40061       ],
40062       [
40063         "South Korea (대한민국)",
40064         "kr",
40065         "82"
40066       ],
40067       [
40068         "South Sudan (‫جنوب السودان‬‎)",
40069         "ss",
40070         "211"
40071       ],
40072       [
40073         "Spain (España)",
40074         "es",
40075         "34"
40076       ],
40077       [
40078         "Sri Lanka (ශ්‍රී ලංකාව)",
40079         "lk",
40080         "94"
40081       ],
40082       [
40083         "Sudan (‫السودان‬‎)",
40084         "sd",
40085         "249"
40086       ],
40087       [
40088         "Suriname",
40089         "sr",
40090         "597"
40091       ],
40092       [
40093         "Svalbard and Jan Mayen",
40094         "sj",
40095         "47",
40096         1
40097       ],
40098       [
40099         "Swaziland",
40100         "sz",
40101         "268"
40102       ],
40103       [
40104         "Sweden (Sverige)",
40105         "se",
40106         "46"
40107       ],
40108       [
40109         "Switzerland (Schweiz)",
40110         "ch",
40111         "41"
40112       ],
40113       [
40114         "Syria (‫سوريا‬‎)",
40115         "sy",
40116         "963"
40117       ],
40118       [
40119         "Taiwan (台灣)",
40120         "tw",
40121         "886"
40122       ],
40123       [
40124         "Tajikistan",
40125         "tj",
40126         "992"
40127       ],
40128       [
40129         "Tanzania",
40130         "tz",
40131         "255"
40132       ],
40133       [
40134         "Thailand (ไทย)",
40135         "th",
40136         "66"
40137       ],
40138       [
40139         "Timor-Leste",
40140         "tl",
40141         "670"
40142       ],
40143       [
40144         "Togo",
40145         "tg",
40146         "228"
40147       ],
40148       [
40149         "Tokelau",
40150         "tk",
40151         "690"
40152       ],
40153       [
40154         "Tonga",
40155         "to",
40156         "676"
40157       ],
40158       [
40159         "Trinidad and Tobago",
40160         "tt",
40161         "1868"
40162       ],
40163       [
40164         "Tunisia (‫تونس‬‎)",
40165         "tn",
40166         "216"
40167       ],
40168       [
40169         "Turkey (Türkiye)",
40170         "tr",
40171         "90"
40172       ],
40173       [
40174         "Turkmenistan",
40175         "tm",
40176         "993"
40177       ],
40178       [
40179         "Turks and Caicos Islands",
40180         "tc",
40181         "1649"
40182       ],
40183       [
40184         "Tuvalu",
40185         "tv",
40186         "688"
40187       ],
40188       [
40189         "U.S. Virgin Islands",
40190         "vi",
40191         "1340"
40192       ],
40193       [
40194         "Uganda",
40195         "ug",
40196         "256"
40197       ],
40198       [
40199         "Ukraine (Україна)",
40200         "ua",
40201         "380"
40202       ],
40203       [
40204         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
40205         "ae",
40206         "971"
40207       ],
40208       [
40209         "United Kingdom",
40210         "gb",
40211         "44",
40212         0
40213       ],
40214       [
40215         "United States",
40216         "us",
40217         "1",
40218         0
40219       ],
40220       [
40221         "Uruguay",
40222         "uy",
40223         "598"
40224       ],
40225       [
40226         "Uzbekistan (Oʻzbekiston)",
40227         "uz",
40228         "998"
40229       ],
40230       [
40231         "Vanuatu",
40232         "vu",
40233         "678"
40234       ],
40235       [
40236         "Vatican City (Città del Vaticano)",
40237         "va",
40238         "39",
40239         1
40240       ],
40241       [
40242         "Venezuela",
40243         "ve",
40244         "58"
40245       ],
40246       [
40247         "Vietnam (Việt Nam)",
40248         "vn",
40249         "84"
40250       ],
40251       [
40252         "Wallis and Futuna (Wallis-et-Futuna)",
40253         "wf",
40254         "681"
40255       ],
40256       [
40257         "Western Sahara (‫الصحراء الغربية‬‎)",
40258         "eh",
40259         "212",
40260         1
40261       ],
40262       [
40263         "Yemen (‫اليمن‬‎)",
40264         "ye",
40265         "967"
40266       ],
40267       [
40268         "Zambia",
40269         "zm",
40270         "260"
40271       ],
40272       [
40273         "Zimbabwe",
40274         "zw",
40275         "263"
40276       ],
40277       [
40278         "Åland Islands",
40279         "ax",
40280         "358",
40281         1
40282       ]
40283   ];
40284   
40285   return d;
40286 }/**
40287 *    This script refer to:
40288 *    Title: International Telephone Input
40289 *    Author: Jack O'Connor
40290 *    Code version:  v12.1.12
40291 *    Availability: https://github.com/jackocnr/intl-tel-input.git
40292 **/
40293
40294 /**
40295  * @class Roo.bootstrap.PhoneInput
40296  * @extends Roo.bootstrap.TriggerField
40297  * An input with International dial-code selection
40298  
40299  * @cfg {String} defaultDialCode default '+852'
40300  * @cfg {Array} preferedCountries default []
40301   
40302  * @constructor
40303  * Create a new PhoneInput.
40304  * @param {Object} config Configuration options
40305  */
40306
40307 Roo.bootstrap.PhoneInput = function(config) {
40308     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
40309 };
40310
40311 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
40312         
40313         listWidth: undefined,
40314         
40315         selectedClass: 'active',
40316         
40317         invalidClass : "has-warning",
40318         
40319         validClass: 'has-success',
40320         
40321         allowed: '0123456789',
40322         
40323         max_length: 15,
40324         
40325         /**
40326          * @cfg {String} defaultDialCode The default dial code when initializing the input
40327          */
40328         defaultDialCode: '+852',
40329         
40330         /**
40331          * @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
40332          */
40333         preferedCountries: false,
40334         
40335         getAutoCreate : function()
40336         {
40337             var data = Roo.bootstrap.PhoneInputData();
40338             var align = this.labelAlign || this.parentLabelAlign();
40339             var id = Roo.id();
40340             
40341             this.allCountries = [];
40342             this.dialCodeMapping = [];
40343             
40344             for (var i = 0; i < data.length; i++) {
40345               var c = data[i];
40346               this.allCountries[i] = {
40347                 name: c[0],
40348                 iso2: c[1],
40349                 dialCode: c[2],
40350                 priority: c[3] || 0,
40351                 areaCodes: c[4] || null
40352               };
40353               this.dialCodeMapping[c[2]] = {
40354                   name: c[0],
40355                   iso2: c[1],
40356                   priority: c[3] || 0,
40357                   areaCodes: c[4] || null
40358               };
40359             }
40360             
40361             var cfg = {
40362                 cls: 'form-group',
40363                 cn: []
40364             };
40365             
40366             var input =  {
40367                 tag: 'input',
40368                 id : id,
40369                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40370                 maxlength: this.max_length,
40371                 cls : 'form-control tel-input',
40372                 autocomplete: 'new-password'
40373             };
40374             
40375             var hiddenInput = {
40376                 tag: 'input',
40377                 type: 'hidden',
40378                 cls: 'hidden-tel-input'
40379             };
40380             
40381             if (this.name) {
40382                 hiddenInput.name = this.name;
40383             }
40384             
40385             if (this.disabled) {
40386                 input.disabled = true;
40387             }
40388             
40389             var flag_container = {
40390                 tag: 'div',
40391                 cls: 'flag-box',
40392                 cn: [
40393                     {
40394                         tag: 'div',
40395                         cls: 'flag'
40396                     },
40397                     {
40398                         tag: 'div',
40399                         cls: 'caret'
40400                     }
40401                 ]
40402             };
40403             
40404             var box = {
40405                 tag: 'div',
40406                 cls: this.hasFeedback ? 'has-feedback' : '',
40407                 cn: [
40408                     hiddenInput,
40409                     input,
40410                     {
40411                         tag: 'input',
40412                         cls: 'dial-code-holder',
40413                         disabled: true
40414                     }
40415                 ]
40416             };
40417             
40418             var container = {
40419                 cls: 'roo-select2-container input-group',
40420                 cn: [
40421                     flag_container,
40422                     box
40423                 ]
40424             };
40425             
40426             if (this.fieldLabel.length) {
40427                 var indicator = {
40428                     tag: 'i',
40429                     tooltip: 'This field is required'
40430                 };
40431                 
40432                 var label = {
40433                     tag: 'label',
40434                     'for':  id,
40435                     cls: 'control-label',
40436                     cn: []
40437                 };
40438                 
40439                 var label_text = {
40440                     tag: 'span',
40441                     html: this.fieldLabel
40442                 };
40443                 
40444                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40445                 label.cn = [
40446                     indicator,
40447                     label_text
40448                 ];
40449                 
40450                 if(this.indicatorpos == 'right') {
40451                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40452                     label.cn = [
40453                         label_text,
40454                         indicator
40455                     ];
40456                 }
40457                 
40458                 if(align == 'left') {
40459                     container = {
40460                         tag: 'div',
40461                         cn: [
40462                             container
40463                         ]
40464                     };
40465                     
40466                     if(this.labelWidth > 12){
40467                         label.style = "width: " + this.labelWidth + 'px';
40468                     }
40469                     if(this.labelWidth < 13 && this.labelmd == 0){
40470                         this.labelmd = this.labelWidth;
40471                     }
40472                     if(this.labellg > 0){
40473                         label.cls += ' col-lg-' + this.labellg;
40474                         input.cls += ' col-lg-' + (12 - this.labellg);
40475                     }
40476                     if(this.labelmd > 0){
40477                         label.cls += ' col-md-' + this.labelmd;
40478                         container.cls += ' col-md-' + (12 - this.labelmd);
40479                     }
40480                     if(this.labelsm > 0){
40481                         label.cls += ' col-sm-' + this.labelsm;
40482                         container.cls += ' col-sm-' + (12 - this.labelsm);
40483                     }
40484                     if(this.labelxs > 0){
40485                         label.cls += ' col-xs-' + this.labelxs;
40486                         container.cls += ' col-xs-' + (12 - this.labelxs);
40487                     }
40488                 }
40489             }
40490             
40491             cfg.cn = [
40492                 label,
40493                 container
40494             ];
40495             
40496             var settings = this;
40497             
40498             ['xs','sm','md','lg'].map(function(size){
40499                 if (settings[size]) {
40500                     cfg.cls += ' col-' + size + '-' + settings[size];
40501                 }
40502             });
40503             
40504             this.store = new Roo.data.Store({
40505                 proxy : new Roo.data.MemoryProxy({}),
40506                 reader : new Roo.data.JsonReader({
40507                     fields : [
40508                         {
40509                             'name' : 'name',
40510                             'type' : 'string'
40511                         },
40512                         {
40513                             'name' : 'iso2',
40514                             'type' : 'string'
40515                         },
40516                         {
40517                             'name' : 'dialCode',
40518                             'type' : 'string'
40519                         },
40520                         {
40521                             'name' : 'priority',
40522                             'type' : 'string'
40523                         },
40524                         {
40525                             'name' : 'areaCodes',
40526                             'type' : 'string'
40527                         }
40528                     ]
40529                 })
40530             });
40531             
40532             if(!this.preferedCountries) {
40533                 this.preferedCountries = [
40534                     'hk',
40535                     'gb',
40536                     'us'
40537                 ];
40538             }
40539             
40540             var p = this.preferedCountries.reverse();
40541             
40542             if(p) {
40543                 for (var i = 0; i < p.length; i++) {
40544                     for (var j = 0; j < this.allCountries.length; j++) {
40545                         if(this.allCountries[j].iso2 == p[i]) {
40546                             var t = this.allCountries[j];
40547                             this.allCountries.splice(j,1);
40548                             this.allCountries.unshift(t);
40549                         }
40550                     } 
40551                 }
40552             }
40553             
40554             this.store.proxy.data = {
40555                 success: true,
40556                 data: this.allCountries
40557             };
40558             
40559             return cfg;
40560         },
40561         
40562         initEvents : function()
40563         {
40564             this.createList();
40565             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40566             
40567             this.indicator = this.indicatorEl();
40568             this.flag = this.flagEl();
40569             this.dialCodeHolder = this.dialCodeHolderEl();
40570             
40571             this.trigger = this.el.select('div.flag-box',true).first();
40572             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40573             
40574             var _this = this;
40575             
40576             (function(){
40577                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40578                 _this.list.setWidth(lw);
40579             }).defer(100);
40580             
40581             this.list.on('mouseover', this.onViewOver, this);
40582             this.list.on('mousemove', this.onViewMove, this);
40583             this.inputEl().on("keyup", this.onKeyUp, this);
40584             this.inputEl().on("keypress", this.onKeyPress, this);
40585             
40586             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40587
40588             this.view = new Roo.View(this.list, this.tpl, {
40589                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40590             });
40591             
40592             this.view.on('click', this.onViewClick, this);
40593             this.setValue(this.defaultDialCode);
40594         },
40595         
40596         onTriggerClick : function(e)
40597         {
40598             Roo.log('trigger click');
40599             if(this.disabled){
40600                 return;
40601             }
40602             
40603             if(this.isExpanded()){
40604                 this.collapse();
40605                 this.hasFocus = false;
40606             }else {
40607                 this.store.load({});
40608                 this.hasFocus = true;
40609                 this.expand();
40610             }
40611         },
40612         
40613         isExpanded : function()
40614         {
40615             return this.list.isVisible();
40616         },
40617         
40618         collapse : function()
40619         {
40620             if(!this.isExpanded()){
40621                 return;
40622             }
40623             this.list.hide();
40624             Roo.get(document).un('mousedown', this.collapseIf, this);
40625             Roo.get(document).un('mousewheel', this.collapseIf, this);
40626             this.fireEvent('collapse', this);
40627             this.validate();
40628         },
40629         
40630         expand : function()
40631         {
40632             Roo.log('expand');
40633
40634             if(this.isExpanded() || !this.hasFocus){
40635                 return;
40636             }
40637             
40638             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40639             this.list.setWidth(lw);
40640             
40641             this.list.show();
40642             this.restrictHeight();
40643             
40644             Roo.get(document).on('mousedown', this.collapseIf, this);
40645             Roo.get(document).on('mousewheel', this.collapseIf, this);
40646             
40647             this.fireEvent('expand', this);
40648         },
40649         
40650         restrictHeight : function()
40651         {
40652             this.list.alignTo(this.inputEl(), this.listAlign);
40653             this.list.alignTo(this.inputEl(), this.listAlign);
40654         },
40655         
40656         onViewOver : function(e, t)
40657         {
40658             if(this.inKeyMode){
40659                 return;
40660             }
40661             var item = this.view.findItemFromChild(t);
40662             
40663             if(item){
40664                 var index = this.view.indexOf(item);
40665                 this.select(index, false);
40666             }
40667         },
40668
40669         // private
40670         onViewClick : function(view, doFocus, el, e)
40671         {
40672             var index = this.view.getSelectedIndexes()[0];
40673             
40674             var r = this.store.getAt(index);
40675             
40676             if(r){
40677                 this.onSelect(r, index);
40678             }
40679             if(doFocus !== false && !this.blockFocus){
40680                 this.inputEl().focus();
40681             }
40682         },
40683         
40684         onViewMove : function(e, t)
40685         {
40686             this.inKeyMode = false;
40687         },
40688         
40689         select : function(index, scrollIntoView)
40690         {
40691             this.selectedIndex = index;
40692             this.view.select(index);
40693             if(scrollIntoView !== false){
40694                 var el = this.view.getNode(index);
40695                 if(el){
40696                     this.list.scrollChildIntoView(el, false);
40697                 }
40698             }
40699         },
40700         
40701         createList : function()
40702         {
40703             this.list = Roo.get(document.body).createChild({
40704                 tag: 'ul',
40705                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40706                 style: 'display:none'
40707             });
40708             
40709             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40710         },
40711         
40712         collapseIf : function(e)
40713         {
40714             var in_combo  = e.within(this.el);
40715             var in_list =  e.within(this.list);
40716             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40717             
40718             if (in_combo || in_list || is_list) {
40719                 return;
40720             }
40721             this.collapse();
40722         },
40723         
40724         onSelect : function(record, index)
40725         {
40726             if(this.fireEvent('beforeselect', this, record, index) !== false){
40727                 
40728                 this.setFlagClass(record.data.iso2);
40729                 this.setDialCode(record.data.dialCode);
40730                 this.hasFocus = false;
40731                 this.collapse();
40732                 this.fireEvent('select', this, record, index);
40733             }
40734         },
40735         
40736         flagEl : function()
40737         {
40738             var flag = this.el.select('div.flag',true).first();
40739             if(!flag){
40740                 return false;
40741             }
40742             return flag;
40743         },
40744         
40745         dialCodeHolderEl : function()
40746         {
40747             var d = this.el.select('input.dial-code-holder',true).first();
40748             if(!d){
40749                 return false;
40750             }
40751             return d;
40752         },
40753         
40754         setDialCode : function(v)
40755         {
40756             this.dialCodeHolder.dom.value = '+'+v;
40757         },
40758         
40759         setFlagClass : function(n)
40760         {
40761             this.flag.dom.className = 'flag '+n;
40762         },
40763         
40764         getValue : function()
40765         {
40766             var v = this.inputEl().getValue();
40767             if(this.dialCodeHolder) {
40768                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40769             }
40770             return v;
40771         },
40772         
40773         setValue : function(v)
40774         {
40775             var d = this.getDialCode(v);
40776             
40777             //invalid dial code
40778             if(v.length == 0 || !d || d.length == 0) {
40779                 if(this.rendered){
40780                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40781                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40782                 }
40783                 return;
40784             }
40785             
40786             //valid dial code
40787             this.setFlagClass(this.dialCodeMapping[d].iso2);
40788             this.setDialCode(d);
40789             this.inputEl().dom.value = v.replace('+'+d,'');
40790             this.hiddenEl().dom.value = this.getValue();
40791             
40792             this.validate();
40793         },
40794         
40795         getDialCode : function(v)
40796         {
40797             v = v ||  '';
40798             
40799             if (v.length == 0) {
40800                 return this.dialCodeHolder.dom.value;
40801             }
40802             
40803             var dialCode = "";
40804             if (v.charAt(0) != "+") {
40805                 return false;
40806             }
40807             var numericChars = "";
40808             for (var i = 1; i < v.length; i++) {
40809               var c = v.charAt(i);
40810               if (!isNaN(c)) {
40811                 numericChars += c;
40812                 if (this.dialCodeMapping[numericChars]) {
40813                   dialCode = v.substr(1, i);
40814                 }
40815                 if (numericChars.length == 4) {
40816                   break;
40817                 }
40818               }
40819             }
40820             return dialCode;
40821         },
40822         
40823         reset : function()
40824         {
40825             this.setValue(this.defaultDialCode);
40826             this.validate();
40827         },
40828         
40829         hiddenEl : function()
40830         {
40831             return this.el.select('input.hidden-tel-input',true).first();
40832         },
40833         
40834         // after setting val
40835         onKeyUp : function(e){
40836             this.setValue(this.getValue());
40837         },
40838         
40839         onKeyPress : function(e){
40840             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40841                 e.stopEvent();
40842             }
40843         }
40844         
40845 });
40846 /**
40847  * @class Roo.bootstrap.MoneyField
40848  * @extends Roo.bootstrap.ComboBox
40849  * Bootstrap MoneyField class
40850  * 
40851  * @constructor
40852  * Create a new MoneyField.
40853  * @param {Object} config Configuration options
40854  */
40855
40856 Roo.bootstrap.MoneyField = function(config) {
40857     
40858     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40859     
40860 };
40861
40862 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40863     
40864     /**
40865      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40866      */
40867     allowDecimals : true,
40868     /**
40869      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40870      */
40871     decimalSeparator : ".",
40872     /**
40873      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40874      */
40875     decimalPrecision : 0,
40876     /**
40877      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40878      */
40879     allowNegative : true,
40880     /**
40881      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40882      */
40883     allowZero: true,
40884     /**
40885      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40886      */
40887     minValue : Number.NEGATIVE_INFINITY,
40888     /**
40889      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40890      */
40891     maxValue : Number.MAX_VALUE,
40892     /**
40893      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40894      */
40895     minText : "The minimum value for this field is {0}",
40896     /**
40897      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40898      */
40899     maxText : "The maximum value for this field is {0}",
40900     /**
40901      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40902      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40903      */
40904     nanText : "{0} is not a valid number",
40905     /**
40906      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40907      */
40908     castInt : true,
40909     /**
40910      * @cfg {String} defaults currency of the MoneyField
40911      * value should be in lkey
40912      */
40913     defaultCurrency : false,
40914     /**
40915      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40916      */
40917     thousandsDelimiter : false,
40918     /**
40919      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40920      */
40921     max_length: false,
40922     
40923     inputlg : 9,
40924     inputmd : 9,
40925     inputsm : 9,
40926     inputxs : 6,
40927     
40928     store : false,
40929     
40930     getAutoCreate : function()
40931     {
40932         var align = this.labelAlign || this.parentLabelAlign();
40933         
40934         var id = Roo.id();
40935
40936         var cfg = {
40937             cls: 'form-group',
40938             cn: []
40939         };
40940
40941         var input =  {
40942             tag: 'input',
40943             id : id,
40944             cls : 'form-control roo-money-amount-input',
40945             autocomplete: 'new-password'
40946         };
40947         
40948         var hiddenInput = {
40949             tag: 'input',
40950             type: 'hidden',
40951             id: Roo.id(),
40952             cls: 'hidden-number-input'
40953         };
40954         
40955         if(this.max_length) {
40956             input.maxlength = this.max_length; 
40957         }
40958         
40959         if (this.name) {
40960             hiddenInput.name = this.name;
40961         }
40962
40963         if (this.disabled) {
40964             input.disabled = true;
40965         }
40966
40967         var clg = 12 - this.inputlg;
40968         var cmd = 12 - this.inputmd;
40969         var csm = 12 - this.inputsm;
40970         var cxs = 12 - this.inputxs;
40971         
40972         var container = {
40973             tag : 'div',
40974             cls : 'row roo-money-field',
40975             cn : [
40976                 {
40977                     tag : 'div',
40978                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40979                     cn : [
40980                         {
40981                             tag : 'div',
40982                             cls: 'roo-select2-container input-group',
40983                             cn: [
40984                                 {
40985                                     tag : 'input',
40986                                     cls : 'form-control roo-money-currency-input',
40987                                     autocomplete: 'new-password',
40988                                     readOnly : 1,
40989                                     name : this.currencyName
40990                                 },
40991                                 {
40992                                     tag :'span',
40993                                     cls : 'input-group-addon',
40994                                     cn : [
40995                                         {
40996                                             tag: 'span',
40997                                             cls: 'caret'
40998                                         }
40999                                     ]
41000                                 }
41001                             ]
41002                         }
41003                     ]
41004                 },
41005                 {
41006                     tag : 'div',
41007                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
41008                     cn : [
41009                         {
41010                             tag: 'div',
41011                             cls: this.hasFeedback ? 'has-feedback' : '',
41012                             cn: [
41013                                 input
41014                             ]
41015                         }
41016                     ]
41017                 }
41018             ]
41019             
41020         };
41021         
41022         if (this.fieldLabel.length) {
41023             var indicator = {
41024                 tag: 'i',
41025                 tooltip: 'This field is required'
41026             };
41027
41028             var label = {
41029                 tag: 'label',
41030                 'for':  id,
41031                 cls: 'control-label',
41032                 cn: []
41033             };
41034
41035             var label_text = {
41036                 tag: 'span',
41037                 html: this.fieldLabel
41038             };
41039
41040             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
41041             label.cn = [
41042                 indicator,
41043                 label_text
41044             ];
41045
41046             if(this.indicatorpos == 'right') {
41047                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
41048                 label.cn = [
41049                     label_text,
41050                     indicator
41051                 ];
41052             }
41053
41054             if(align == 'left') {
41055                 container = {
41056                     tag: 'div',
41057                     cn: [
41058                         container
41059                     ]
41060                 };
41061
41062                 if(this.labelWidth > 12){
41063                     label.style = "width: " + this.labelWidth + 'px';
41064                 }
41065                 if(this.labelWidth < 13 && this.labelmd == 0){
41066                     this.labelmd = this.labelWidth;
41067                 }
41068                 if(this.labellg > 0){
41069                     label.cls += ' col-lg-' + this.labellg;
41070                     input.cls += ' col-lg-' + (12 - this.labellg);
41071                 }
41072                 if(this.labelmd > 0){
41073                     label.cls += ' col-md-' + this.labelmd;
41074                     container.cls += ' col-md-' + (12 - this.labelmd);
41075                 }
41076                 if(this.labelsm > 0){
41077                     label.cls += ' col-sm-' + this.labelsm;
41078                     container.cls += ' col-sm-' + (12 - this.labelsm);
41079                 }
41080                 if(this.labelxs > 0){
41081                     label.cls += ' col-xs-' + this.labelxs;
41082                     container.cls += ' col-xs-' + (12 - this.labelxs);
41083                 }
41084             }
41085         }
41086
41087         cfg.cn = [
41088             label,
41089             container,
41090             hiddenInput
41091         ];
41092         
41093         var settings = this;
41094
41095         ['xs','sm','md','lg'].map(function(size){
41096             if (settings[size]) {
41097                 cfg.cls += ' col-' + size + '-' + settings[size];
41098             }
41099         });
41100         
41101         return cfg;
41102     },
41103     
41104     initEvents : function()
41105     {
41106         this.indicator = this.indicatorEl();
41107         
41108         this.initCurrencyEvent();
41109         
41110         this.initNumberEvent();
41111     },
41112     
41113     initCurrencyEvent : function()
41114     {
41115         if (!this.store) {
41116             throw "can not find store for combo";
41117         }
41118         
41119         this.store = Roo.factory(this.store, Roo.data);
41120         this.store.parent = this;
41121         
41122         this.createList();
41123         
41124         this.triggerEl = this.el.select('.input-group-addon', true).first();
41125         
41126         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
41127         
41128         var _this = this;
41129         
41130         (function(){
41131             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
41132             _this.list.setWidth(lw);
41133         }).defer(100);
41134         
41135         this.list.on('mouseover', this.onViewOver, this);
41136         this.list.on('mousemove', this.onViewMove, this);
41137         this.list.on('scroll', this.onViewScroll, this);
41138         
41139         if(!this.tpl){
41140             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
41141         }
41142         
41143         this.view = new Roo.View(this.list, this.tpl, {
41144             singleSelect:true, store: this.store, selectedClass: this.selectedClass
41145         });
41146         
41147         this.view.on('click', this.onViewClick, this);
41148         
41149         this.store.on('beforeload', this.onBeforeLoad, this);
41150         this.store.on('load', this.onLoad, this);
41151         this.store.on('loadexception', this.onLoadException, this);
41152         
41153         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
41154             "up" : function(e){
41155                 this.inKeyMode = true;
41156                 this.selectPrev();
41157             },
41158
41159             "down" : function(e){
41160                 if(!this.isExpanded()){
41161                     this.onTriggerClick();
41162                 }else{
41163                     this.inKeyMode = true;
41164                     this.selectNext();
41165                 }
41166             },
41167
41168             "enter" : function(e){
41169                 this.collapse();
41170                 
41171                 if(this.fireEvent("specialkey", this, e)){
41172                     this.onViewClick(false);
41173                 }
41174                 
41175                 return true;
41176             },
41177
41178             "esc" : function(e){
41179                 this.collapse();
41180             },
41181
41182             "tab" : function(e){
41183                 this.collapse();
41184                 
41185                 if(this.fireEvent("specialkey", this, e)){
41186                     this.onViewClick(false);
41187                 }
41188                 
41189                 return true;
41190             },
41191
41192             scope : this,
41193
41194             doRelay : function(foo, bar, hname){
41195                 if(hname == 'down' || this.scope.isExpanded()){
41196                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41197                 }
41198                 return true;
41199             },
41200
41201             forceKeyDown: true
41202         });
41203         
41204         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
41205         
41206     },
41207     
41208     initNumberEvent : function(e)
41209     {
41210         this.inputEl().on("keydown" , this.fireKey,  this);
41211         this.inputEl().on("focus", this.onFocus,  this);
41212         this.inputEl().on("blur", this.onBlur,  this);
41213         
41214         this.inputEl().relayEvent('keyup', this);
41215         
41216         if(this.indicator){
41217             this.indicator.addClass('invisible');
41218         }
41219  
41220         this.originalValue = this.getValue();
41221         
41222         if(this.validationEvent == 'keyup'){
41223             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
41224             this.inputEl().on('keyup', this.filterValidation, this);
41225         }
41226         else if(this.validationEvent !== false){
41227             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
41228         }
41229         
41230         if(this.selectOnFocus){
41231             this.on("focus", this.preFocus, this);
41232             
41233         }
41234         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41235             this.inputEl().on("keypress", this.filterKeys, this);
41236         } else {
41237             this.inputEl().relayEvent('keypress', this);
41238         }
41239         
41240         var allowed = "0123456789";
41241         
41242         if(this.allowDecimals){
41243             allowed += this.decimalSeparator;
41244         }
41245         
41246         if(this.allowNegative){
41247             allowed += "-";
41248         }
41249         
41250         if(this.thousandsDelimiter) {
41251             allowed += ",";
41252         }
41253         
41254         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41255         
41256         var keyPress = function(e){
41257             
41258             var k = e.getKey();
41259             
41260             var c = e.getCharCode();
41261             
41262             if(
41263                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41264                     allowed.indexOf(String.fromCharCode(c)) === -1
41265             ){
41266                 e.stopEvent();
41267                 return;
41268             }
41269             
41270             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41271                 return;
41272             }
41273             
41274             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41275                 e.stopEvent();
41276             }
41277         };
41278         
41279         this.inputEl().on("keypress", keyPress, this);
41280         
41281     },
41282     
41283     onTriggerClick : function(e)
41284     {   
41285         if(this.disabled){
41286             return;
41287         }
41288         
41289         this.page = 0;
41290         this.loadNext = false;
41291         
41292         if(this.isExpanded()){
41293             this.collapse();
41294             return;
41295         }
41296         
41297         this.hasFocus = true;
41298         
41299         if(this.triggerAction == 'all') {
41300             this.doQuery(this.allQuery, true);
41301             return;
41302         }
41303         
41304         this.doQuery(this.getRawValue());
41305     },
41306     
41307     getCurrency : function()
41308     {   
41309         var v = this.currencyEl().getValue();
41310         
41311         return v;
41312     },
41313     
41314     restrictHeight : function()
41315     {
41316         this.list.alignTo(this.currencyEl(), this.listAlign);
41317         this.list.alignTo(this.currencyEl(), this.listAlign);
41318     },
41319     
41320     onViewClick : function(view, doFocus, el, e)
41321     {
41322         var index = this.view.getSelectedIndexes()[0];
41323         
41324         var r = this.store.getAt(index);
41325         
41326         if(r){
41327             this.onSelect(r, index);
41328         }
41329     },
41330     
41331     onSelect : function(record, index){
41332         
41333         if(this.fireEvent('beforeselect', this, record, index) !== false){
41334         
41335             this.setFromCurrencyData(index > -1 ? record.data : false);
41336             
41337             this.collapse();
41338             
41339             this.fireEvent('select', this, record, index);
41340         }
41341     },
41342     
41343     setFromCurrencyData : function(o)
41344     {
41345         var currency = '';
41346         
41347         this.lastCurrency = o;
41348         
41349         if (this.currencyField) {
41350             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41351         } else {
41352             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
41353         }
41354         
41355         this.lastSelectionText = currency;
41356         
41357         //setting default currency
41358         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41359             this.setCurrency(this.defaultCurrency);
41360             return;
41361         }
41362         
41363         this.setCurrency(currency);
41364     },
41365     
41366     setFromData : function(o)
41367     {
41368         var c = {};
41369         
41370         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41371         
41372         this.setFromCurrencyData(c);
41373         
41374         var value = '';
41375         
41376         if (this.name) {
41377             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41378         } else {
41379             Roo.log('no value set for '+ (this.name ? this.name : this.id));
41380         }
41381         
41382         this.setValue(value);
41383         
41384     },
41385     
41386     setCurrency : function(v)
41387     {   
41388         this.currencyValue = v;
41389         
41390         if(this.rendered){
41391             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41392             this.validate();
41393         }
41394     },
41395     
41396     setValue : function(v)
41397     {
41398         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41399         
41400         this.value = v;
41401         
41402         if(this.rendered){
41403             
41404             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41405             
41406             this.inputEl().dom.value = (v == '') ? '' :
41407                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41408             
41409             if(!this.allowZero && v === '0') {
41410                 this.hiddenEl().dom.value = '';
41411                 this.inputEl().dom.value = '';
41412             }
41413             
41414             this.validate();
41415         }
41416     },
41417     
41418     getRawValue : function()
41419     {
41420         var v = this.inputEl().getValue();
41421         
41422         return v;
41423     },
41424     
41425     getValue : function()
41426     {
41427         return this.fixPrecision(this.parseValue(this.getRawValue()));
41428     },
41429     
41430     parseValue : function(value)
41431     {
41432         if(this.thousandsDelimiter) {
41433             value += "";
41434             r = new RegExp(",", "g");
41435             value = value.replace(r, "");
41436         }
41437         
41438         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41439         return isNaN(value) ? '' : value;
41440         
41441     },
41442     
41443     fixPrecision : function(value)
41444     {
41445         if(this.thousandsDelimiter) {
41446             value += "";
41447             r = new RegExp(",", "g");
41448             value = value.replace(r, "");
41449         }
41450         
41451         var nan = isNaN(value);
41452         
41453         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41454             return nan ? '' : value;
41455         }
41456         return parseFloat(value).toFixed(this.decimalPrecision);
41457     },
41458     
41459     decimalPrecisionFcn : function(v)
41460     {
41461         return Math.floor(v);
41462     },
41463     
41464     validateValue : function(value)
41465     {
41466         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41467             return false;
41468         }
41469         
41470         var num = this.parseValue(value);
41471         
41472         if(isNaN(num)){
41473             this.markInvalid(String.format(this.nanText, value));
41474             return false;
41475         }
41476         
41477         if(num < this.minValue){
41478             this.markInvalid(String.format(this.minText, this.minValue));
41479             return false;
41480         }
41481         
41482         if(num > this.maxValue){
41483             this.markInvalid(String.format(this.maxText, this.maxValue));
41484             return false;
41485         }
41486         
41487         return true;
41488     },
41489     
41490     validate : function()
41491     {
41492         if(this.disabled || this.allowBlank){
41493             this.markValid();
41494             return true;
41495         }
41496         
41497         var currency = this.getCurrency();
41498         
41499         if(this.validateValue(this.getRawValue()) && currency.length){
41500             this.markValid();
41501             return true;
41502         }
41503         
41504         this.markInvalid();
41505         return false;
41506     },
41507     
41508     getName: function()
41509     {
41510         return this.name;
41511     },
41512     
41513     beforeBlur : function()
41514     {
41515         if(!this.castInt){
41516             return;
41517         }
41518         
41519         var v = this.parseValue(this.getRawValue());
41520         
41521         if(v || v == 0){
41522             this.setValue(v);
41523         }
41524     },
41525     
41526     onBlur : function()
41527     {
41528         this.beforeBlur();
41529         
41530         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41531             //this.el.removeClass(this.focusClass);
41532         }
41533         
41534         this.hasFocus = false;
41535         
41536         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41537             this.validate();
41538         }
41539         
41540         var v = this.getValue();
41541         
41542         if(String(v) !== String(this.startValue)){
41543             this.fireEvent('change', this, v, this.startValue);
41544         }
41545         
41546         this.fireEvent("blur", this);
41547     },
41548     
41549     inputEl : function()
41550     {
41551         return this.el.select('.roo-money-amount-input', true).first();
41552     },
41553     
41554     currencyEl : function()
41555     {
41556         return this.el.select('.roo-money-currency-input', true).first();
41557     },
41558     
41559     hiddenEl : function()
41560     {
41561         return this.el.select('input.hidden-number-input',true).first();
41562     }
41563     
41564 });/**
41565  * @class Roo.bootstrap.BezierSignature
41566  * @extends Roo.bootstrap.Component
41567  * Bootstrap BezierSignature class
41568  * This script refer to:
41569  *    Title: Signature Pad
41570  *    Author: szimek
41571  *    Availability: https://github.com/szimek/signature_pad
41572  *
41573  * @constructor
41574  * Create a new BezierSignature
41575  * @param {Object} config The config object
41576  */
41577
41578 Roo.bootstrap.BezierSignature = function(config){
41579     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
41580     this.addEvents({
41581         "resize" : true
41582     });
41583 };
41584
41585 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
41586 {
41587      
41588     curve_data: [],
41589     
41590     is_empty: true,
41591     
41592     mouse_btn_down: true,
41593     
41594     /**
41595      * @cfg {int} canvas height
41596      */
41597     canvas_height: '200px',
41598     
41599     /**
41600      * @cfg {float|function} Radius of a single dot.
41601      */ 
41602     dot_size: false,
41603     
41604     /**
41605      * @cfg {float} Minimum width of a line. Defaults to 0.5.
41606      */
41607     min_width: 0.5,
41608     
41609     /**
41610      * @cfg {float} Maximum width of a line. Defaults to 2.5.
41611      */
41612     max_width: 2.5,
41613     
41614     /**
41615      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
41616      */
41617     throttle: 16,
41618     
41619     /**
41620      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
41621      */
41622     min_distance: 5,
41623     
41624     /**
41625      * @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.
41626      */
41627     bg_color: 'rgba(0, 0, 0, 0)',
41628     
41629     /**
41630      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
41631      */
41632     dot_color: 'black',
41633     
41634     /**
41635      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
41636      */ 
41637     velocity_filter_weight: 0.7,
41638     
41639     /**
41640      * @cfg {function} Callback when stroke begin. 
41641      */
41642     onBegin: false,
41643     
41644     /**
41645      * @cfg {function} Callback when stroke end.
41646      */
41647     onEnd: false,
41648     
41649     getAutoCreate : function()
41650     {
41651         var cls = 'roo-signature column';
41652         
41653         if(this.cls){
41654             cls += ' ' + this.cls;
41655         }
41656         
41657         var col_sizes = [
41658             'lg',
41659             'md',
41660             'sm',
41661             'xs'
41662         ];
41663         
41664         for(var i = 0; i < col_sizes.length; i++) {
41665             if(this[col_sizes[i]]) {
41666                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
41667             }
41668         }
41669         
41670         var cfg = {
41671             tag: 'div',
41672             cls: cls,
41673             cn: [
41674                 {
41675                     tag: 'div',
41676                     cls: 'roo-signature-body',
41677                     cn: [
41678                         {
41679                             tag: 'canvas',
41680                             cls: 'roo-signature-body-canvas',
41681                             height: this.canvas_height,
41682                             width: this.canvas_width
41683                         }
41684                     ]
41685                 },
41686                 {
41687                     tag: 'input',
41688                     type: 'file',
41689                     style: 'display: none'
41690                 }
41691             ]
41692         };
41693         
41694         return cfg;
41695     },
41696     
41697     initEvents: function() 
41698     {
41699         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
41700         
41701         var canvas = this.canvasEl();
41702         
41703         // mouse && touch event swapping...
41704         canvas.dom.style.touchAction = 'none';
41705         canvas.dom.style.msTouchAction = 'none';
41706         
41707         this.mouse_btn_down = false;
41708         canvas.on('mousedown', this._handleMouseDown, this);
41709         canvas.on('mousemove', this._handleMouseMove, this);
41710         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
41711         
41712         if (window.PointerEvent) {
41713             canvas.on('pointerdown', this._handleMouseDown, this);
41714             canvas.on('pointermove', this._handleMouseMove, this);
41715             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
41716         }
41717         
41718         if ('ontouchstart' in window) {
41719             canvas.on('touchstart', this._handleTouchStart, this);
41720             canvas.on('touchmove', this._handleTouchMove, this);
41721             canvas.on('touchend', this._handleTouchEnd, this);
41722         }
41723         
41724         Roo.EventManager.onWindowResize(this.resize, this, true);
41725         
41726         // file input event
41727         this.fileEl().on('change', this.uploadImage, this);
41728         
41729         this.clear();
41730         
41731         this.resize();
41732     },
41733     
41734     resize: function(){
41735         
41736         var canvas = this.canvasEl().dom;
41737         var ctx = this.canvasElCtx();
41738         var img_data = false;
41739         
41740         if(canvas.width > 0) {
41741             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
41742         }
41743         // setting canvas width will clean img data
41744         canvas.width = 0;
41745         
41746         var style = window.getComputedStyle ? 
41747             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
41748             
41749         var padding_left = parseInt(style.paddingLeft) || 0;
41750         var padding_right = parseInt(style.paddingRight) || 0;
41751         
41752         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
41753         
41754         if(img_data) {
41755             ctx.putImageData(img_data, 0, 0);
41756         }
41757     },
41758     
41759     _handleMouseDown: function(e)
41760     {
41761         if (e.browserEvent.which === 1) {
41762             this.mouse_btn_down = true;
41763             this.strokeBegin(e);
41764         }
41765     },
41766     
41767     _handleMouseMove: function (e)
41768     {
41769         if (this.mouse_btn_down) {
41770             this.strokeMoveUpdate(e);
41771         }
41772     },
41773     
41774     _handleMouseUp: function (e)
41775     {
41776         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
41777             this.mouse_btn_down = false;
41778             this.strokeEnd(e);
41779         }
41780     },
41781     
41782     _handleTouchStart: function (e) {
41783         
41784         e.preventDefault();
41785         if (e.browserEvent.targetTouches.length === 1) {
41786             // var touch = e.browserEvent.changedTouches[0];
41787             // this.strokeBegin(touch);
41788             
41789              this.strokeBegin(e); // assume e catching the correct xy...
41790         }
41791     },
41792     
41793     _handleTouchMove: function (e) {
41794         e.preventDefault();
41795         // var touch = event.targetTouches[0];
41796         // _this._strokeMoveUpdate(touch);
41797         this.strokeMoveUpdate(e);
41798     },
41799     
41800     _handleTouchEnd: function (e) {
41801         var wasCanvasTouched = e.target === this.canvasEl().dom;
41802         if (wasCanvasTouched) {
41803             e.preventDefault();
41804             // var touch = event.changedTouches[0];
41805             // _this._strokeEnd(touch);
41806             this.strokeEnd(e);
41807         }
41808     },
41809     
41810     reset: function () {
41811         this._lastPoints = [];
41812         this._lastVelocity = 0;
41813         this._lastWidth = (this.min_width + this.max_width) / 2;
41814         this.canvasElCtx().fillStyle = this.dot_color;
41815     },
41816     
41817     strokeMoveUpdate: function(e)
41818     {
41819         this.strokeUpdate(e);
41820         
41821         if (this.throttle) {
41822             this.throttleStroke(this.strokeUpdate, this.throttle);
41823         }
41824         else {
41825             this.strokeUpdate(e);
41826         }
41827     },
41828     
41829     strokeBegin: function(e)
41830     {
41831         var newPointGroup = {
41832             color: this.dot_color,
41833             points: []
41834         };
41835         
41836         if (typeof this.onBegin === 'function') {
41837             this.onBegin(e);
41838         }
41839         
41840         this.curve_data.push(newPointGroup);
41841         this.reset();
41842         this.strokeUpdate(e);
41843     },
41844     
41845     strokeUpdate: function(e)
41846     {
41847         var rect = this.canvasEl().dom.getBoundingClientRect();
41848         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
41849         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
41850         var lastPoints = lastPointGroup.points;
41851         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
41852         var isLastPointTooClose = lastPoint
41853             ? point.distanceTo(lastPoint) <= this.min_distance
41854             : false;
41855         var color = lastPointGroup.color;
41856         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
41857             var curve = this.addPoint(point);
41858             if (!lastPoint) {
41859                 this.drawDot({color: color, point: point});
41860             }
41861             else if (curve) {
41862                 this.drawCurve({color: color, curve: curve});
41863             }
41864             lastPoints.push({
41865                 time: point.time,
41866                 x: point.x,
41867                 y: point.y
41868             });
41869         }
41870     },
41871     
41872     strokeEnd: function(e)
41873     {
41874         this.strokeUpdate(e);
41875         if (typeof this.onEnd === 'function') {
41876             this.onEnd(e);
41877         }
41878     },
41879     
41880     addPoint:  function (point) {
41881         var _lastPoints = this._lastPoints;
41882         _lastPoints.push(point);
41883         if (_lastPoints.length > 2) {
41884             if (_lastPoints.length === 3) {
41885                 _lastPoints.unshift(_lastPoints[0]);
41886             }
41887             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
41888             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
41889             _lastPoints.shift();
41890             return curve;
41891         }
41892         return null;
41893     },
41894     
41895     calculateCurveWidths: function (startPoint, endPoint) {
41896         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
41897             (1 - this.velocity_filter_weight) * this._lastVelocity;
41898
41899         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
41900         var widths = {
41901             end: newWidth,
41902             start: this._lastWidth
41903         };
41904         
41905         this._lastVelocity = velocity;
41906         this._lastWidth = newWidth;
41907         return widths;
41908     },
41909     
41910     drawDot: function (_a) {
41911         var color = _a.color, point = _a.point;
41912         var ctx = this.canvasElCtx();
41913         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
41914         ctx.beginPath();
41915         this.drawCurveSegment(point.x, point.y, width);
41916         ctx.closePath();
41917         ctx.fillStyle = color;
41918         ctx.fill();
41919     },
41920     
41921     drawCurve: function (_a) {
41922         var color = _a.color, curve = _a.curve;
41923         var ctx = this.canvasElCtx();
41924         var widthDelta = curve.endWidth - curve.startWidth;
41925         var drawSteps = Math.floor(curve.length()) * 2;
41926         ctx.beginPath();
41927         ctx.fillStyle = color;
41928         for (var i = 0; i < drawSteps; i += 1) {
41929         var t = i / drawSteps;
41930         var tt = t * t;
41931         var ttt = tt * t;
41932         var u = 1 - t;
41933         var uu = u * u;
41934         var uuu = uu * u;
41935         var x = uuu * curve.startPoint.x;
41936         x += 3 * uu * t * curve.control1.x;
41937         x += 3 * u * tt * curve.control2.x;
41938         x += ttt * curve.endPoint.x;
41939         var y = uuu * curve.startPoint.y;
41940         y += 3 * uu * t * curve.control1.y;
41941         y += 3 * u * tt * curve.control2.y;
41942         y += ttt * curve.endPoint.y;
41943         var width = curve.startWidth + ttt * widthDelta;
41944         this.drawCurveSegment(x, y, width);
41945         }
41946         ctx.closePath();
41947         ctx.fill();
41948     },
41949     
41950     drawCurveSegment: function (x, y, width) {
41951         var ctx = this.canvasElCtx();
41952         ctx.moveTo(x, y);
41953         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
41954         this.is_empty = false;
41955     },
41956     
41957     clear: function()
41958     {
41959         var ctx = this.canvasElCtx();
41960         var canvas = this.canvasEl().dom;
41961         ctx.fillStyle = this.bg_color;
41962         ctx.clearRect(0, 0, canvas.width, canvas.height);
41963         ctx.fillRect(0, 0, canvas.width, canvas.height);
41964         this.curve_data = [];
41965         this.reset();
41966         this.is_empty = true;
41967     },
41968     
41969     fileEl: function()
41970     {
41971         return  this.el.select('input',true).first();
41972     },
41973     
41974     canvasEl: function()
41975     {
41976         return this.el.select('canvas',true).first();
41977     },
41978     
41979     canvasElCtx: function()
41980     {
41981         return this.el.select('canvas',true).first().dom.getContext('2d');
41982     },
41983     
41984     getImage: function(type)
41985     {
41986         if(this.is_empty) {
41987             return false;
41988         }
41989         
41990         // encryption ?
41991         return this.canvasEl().dom.toDataURL('image/'+type, 1);
41992     },
41993     
41994     drawFromImage: function(img_src)
41995     {
41996         var img = new Image();
41997         
41998         img.onload = function(){
41999             this.canvasElCtx().drawImage(img, 0, 0);
42000         }.bind(this);
42001         
42002         img.src = img_src;
42003         
42004         this.is_empty = false;
42005     },
42006     
42007     selectImage: function()
42008     {
42009         this.fileEl().dom.click();
42010     },
42011     
42012     uploadImage: function(e)
42013     {
42014         var reader = new FileReader();
42015         
42016         reader.onload = function(e){
42017             var img = new Image();
42018             img.onload = function(){
42019                 this.reset();
42020                 this.canvasElCtx().drawImage(img, 0, 0);
42021             }.bind(this);
42022             img.src = e.target.result;
42023         }.bind(this);
42024         
42025         reader.readAsDataURL(e.target.files[0]);
42026     },
42027     
42028     // Bezier Point Constructor
42029     Point: (function () {
42030         function Point(x, y, time) {
42031             this.x = x;
42032             this.y = y;
42033             this.time = time || Date.now();
42034         }
42035         Point.prototype.distanceTo = function (start) {
42036             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
42037         };
42038         Point.prototype.equals = function (other) {
42039             return this.x === other.x && this.y === other.y && this.time === other.time;
42040         };
42041         Point.prototype.velocityFrom = function (start) {
42042             return this.time !== start.time
42043             ? this.distanceTo(start) / (this.time - start.time)
42044             : 0;
42045         };
42046         return Point;
42047     }()),
42048     
42049     
42050     // Bezier Constructor
42051     Bezier: (function () {
42052         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
42053             this.startPoint = startPoint;
42054             this.control2 = control2;
42055             this.control1 = control1;
42056             this.endPoint = endPoint;
42057             this.startWidth = startWidth;
42058             this.endWidth = endWidth;
42059         }
42060         Bezier.fromPoints = function (points, widths, scope) {
42061             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
42062             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
42063             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
42064         };
42065         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
42066             var dx1 = s1.x - s2.x;
42067             var dy1 = s1.y - s2.y;
42068             var dx2 = s2.x - s3.x;
42069             var dy2 = s2.y - s3.y;
42070             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
42071             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
42072             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
42073             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
42074             var dxm = m1.x - m2.x;
42075             var dym = m1.y - m2.y;
42076             var k = l2 / (l1 + l2);
42077             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
42078             var tx = s2.x - cm.x;
42079             var ty = s2.y - cm.y;
42080             return {
42081                 c1: new scope.Point(m1.x + tx, m1.y + ty),
42082                 c2: new scope.Point(m2.x + tx, m2.y + ty)
42083             };
42084         };
42085         Bezier.prototype.length = function () {
42086             var steps = 10;
42087             var length = 0;
42088             var px;
42089             var py;
42090             for (var i = 0; i <= steps; i += 1) {
42091                 var t = i / steps;
42092                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
42093                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
42094                 if (i > 0) {
42095                     var xdiff = cx - px;
42096                     var ydiff = cy - py;
42097                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
42098                 }
42099                 px = cx;
42100                 py = cy;
42101             }
42102             return length;
42103         };
42104         Bezier.prototype.point = function (t, start, c1, c2, end) {
42105             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
42106             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
42107             + (3.0 * c2 * (1.0 - t) * t * t)
42108             + (end * t * t * t);
42109         };
42110         return Bezier;
42111     }()),
42112     
42113     throttleStroke: function(fn, wait) {
42114       if (wait === void 0) { wait = 250; }
42115       var previous = 0;
42116       var timeout = null;
42117       var result;
42118       var storedContext;
42119       var storedArgs;
42120       var later = function () {
42121           previous = Date.now();
42122           timeout = null;
42123           result = fn.apply(storedContext, storedArgs);
42124           if (!timeout) {
42125               storedContext = null;
42126               storedArgs = [];
42127           }
42128       };
42129       return function wrapper() {
42130           var args = [];
42131           for (var _i = 0; _i < arguments.length; _i++) {
42132               args[_i] = arguments[_i];
42133           }
42134           var now = Date.now();
42135           var remaining = wait - (now - previous);
42136           storedContext = this;
42137           storedArgs = args;
42138           if (remaining <= 0 || remaining > wait) {
42139               if (timeout) {
42140                   clearTimeout(timeout);
42141                   timeout = null;
42142               }
42143               previous = now;
42144               result = fn.apply(storedContext, storedArgs);
42145               if (!timeout) {
42146                   storedContext = null;
42147                   storedArgs = [];
42148               }
42149           }
42150           else if (!timeout) {
42151               timeout = window.setTimeout(later, remaining);
42152           }
42153           return result;
42154       };
42155   }
42156   
42157 });
42158
42159  
42160
42161