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('.roo-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('.roo-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('.roo-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: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse' : 'collapse navbar-collapse roo-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                 // trying to remove so header bar can right align top?
4418                 //cfg.cls += ' navbar-nav';
4419             }
4420         } else {
4421             if (['tabs','pills'].indexOf(this.type) != -1) {
4422                 cfg.cls += ' nav-' + this.type
4423             } else {
4424                 if (this.type !== 'nav') {
4425                     Roo.log('nav type must be nav/tabs/pills')
4426                 }
4427                 cfg.cls += ' navbar-nav'
4428             }
4429         }
4430         
4431         if (this.parent() && this.parent().sidebar) {
4432             cfg = {
4433                 tag: 'ul',
4434                 cls: 'dashboard-menu sidebar-menu'
4435             };
4436             
4437             return cfg;
4438         }
4439         
4440         if (this.form === true) {
4441             cfg = {
4442                 tag: 'form',
4443                 cls: 'navbar-form form-inline'
4444             };
4445             //nav navbar-right ml-md-auto
4446             if (this.align === 'right') {
4447                 cfg.cls += ' navbar-right ml-md-auto';
4448             } else {
4449                 cfg.cls += ' navbar-left';
4450             }
4451         }
4452         
4453         if (this.align === 'right') {
4454             cfg.cls += ' navbar-right ml-md-auto';
4455         } else {
4456             cfg.cls += ' mr-auto';
4457         }
4458         
4459         if (this.inverse) {
4460             cfg.cls += ' navbar-inverse';
4461             
4462         }
4463         
4464         
4465         return cfg;
4466     },
4467     /**
4468     * sets the active Navigation item
4469     * @param {Roo.bootstrap.NavItem} the new current navitem
4470     */
4471     setActiveItem : function(item)
4472     {
4473         var prev = false;
4474         Roo.each(this.navItems, function(v){
4475             if (v == item) {
4476                 return ;
4477             }
4478             if (v.isActive()) {
4479                 v.setActive(false, true);
4480                 prev = v;
4481                 
4482             }
4483             
4484         });
4485
4486         item.setActive(true, true);
4487         this.fireEvent('changed', this, item, prev);
4488         
4489         
4490     },
4491     /**
4492     * gets the active Navigation item
4493     * @return {Roo.bootstrap.NavItem} the current navitem
4494     */
4495     getActive : function()
4496     {
4497         
4498         var prev = false;
4499         Roo.each(this.navItems, function(v){
4500             
4501             if (v.isActive()) {
4502                 prev = v;
4503                 
4504             }
4505             
4506         });
4507         return prev;
4508     },
4509     
4510     indexOfNav : function()
4511     {
4512         
4513         var prev = false;
4514         Roo.each(this.navItems, function(v,i){
4515             
4516             if (v.isActive()) {
4517                 prev = i;
4518                 
4519             }
4520             
4521         });
4522         return prev;
4523     },
4524     /**
4525     * adds a Navigation item
4526     * @param {Roo.bootstrap.NavItem} the navitem to add
4527     */
4528     addItem : function(cfg)
4529     {
4530         if (this.form && Roo.bootstrap.version == 4) {
4531             cfg.tag = 'div';
4532         }
4533         var cn = new Roo.bootstrap.NavItem(cfg);
4534         this.register(cn);
4535         cn.parentId = this.id;
4536         cn.onRender(this.el, null);
4537         return cn;
4538     },
4539     /**
4540     * register a Navigation item
4541     * @param {Roo.bootstrap.NavItem} the navitem to add
4542     */
4543     register : function(item)
4544     {
4545         this.navItems.push( item);
4546         item.navId = this.navId;
4547     
4548     },
4549     
4550     /**
4551     * clear all the Navigation item
4552     */
4553    
4554     clearAll : function()
4555     {
4556         this.navItems = [];
4557         this.el.dom.innerHTML = '';
4558     },
4559     
4560     getNavItem: function(tabId)
4561     {
4562         var ret = false;
4563         Roo.each(this.navItems, function(e) {
4564             if (e.tabId == tabId) {
4565                ret =  e;
4566                return false;
4567             }
4568             return true;
4569             
4570         });
4571         return ret;
4572     },
4573     
4574     setActiveNext : function()
4575     {
4576         var i = this.indexOfNav(this.getActive());
4577         if (i > this.navItems.length) {
4578             return;
4579         }
4580         this.setActiveItem(this.navItems[i+1]);
4581     },
4582     setActivePrev : function()
4583     {
4584         var i = this.indexOfNav(this.getActive());
4585         if (i  < 1) {
4586             return;
4587         }
4588         this.setActiveItem(this.navItems[i-1]);
4589     },
4590     clearWasActive : function(except) {
4591         Roo.each(this.navItems, function(e) {
4592             if (e.tabId != except.tabId && e.was_active) {
4593                e.was_active = false;
4594                return false;
4595             }
4596             return true;
4597             
4598         });
4599     },
4600     getWasActive : function ()
4601     {
4602         var r = false;
4603         Roo.each(this.navItems, function(e) {
4604             if (e.was_active) {
4605                r = e;
4606                return false;
4607             }
4608             return true;
4609             
4610         });
4611         return r;
4612     }
4613     
4614     
4615 });
4616
4617  
4618 Roo.apply(Roo.bootstrap.NavGroup, {
4619     
4620     groups: {},
4621      /**
4622     * register a Navigation Group
4623     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4624     */
4625     register : function(navgrp)
4626     {
4627         this.groups[navgrp.navId] = navgrp;
4628         
4629     },
4630     /**
4631     * fetch a Navigation Group based on the navigation ID
4632     * @param {string} the navgroup to add
4633     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4634     */
4635     get: function(navId) {
4636         if (typeof(this.groups[navId]) == 'undefined') {
4637             return false;
4638             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4639         }
4640         return this.groups[navId] ;
4641     }
4642     
4643     
4644     
4645 });
4646
4647  /*
4648  * - LGPL
4649  *
4650  * row
4651  * 
4652  */
4653
4654 /**
4655  * @class Roo.bootstrap.NavItem
4656  * @extends Roo.bootstrap.Component
4657  * Bootstrap Navbar.NavItem class
4658  * @cfg {String} href  link to
4659  * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
4660
4661  * @cfg {String} html content of button
4662  * @cfg {String} badge text inside badge
4663  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4664  * @cfg {String} glyphicon DEPRICATED - use fa
4665  * @cfg {String} icon DEPRICATED - use fa
4666  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4667  * @cfg {Boolean} active Is item active
4668  * @cfg {Boolean} disabled Is item disabled
4669  
4670  * @cfg {Boolean} preventDefault (true | false) default false
4671  * @cfg {String} tabId the tab that this item activates.
4672  * @cfg {String} tagtype (a|span) render as a href or span?
4673  * @cfg {Boolean} animateRef (true|false) link to element default false  
4674   
4675  * @constructor
4676  * Create a new Navbar Item
4677  * @param {Object} config The config object
4678  */
4679 Roo.bootstrap.NavItem = function(config){
4680     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4681     this.addEvents({
4682         // raw events
4683         /**
4684          * @event click
4685          * The raw click event for the entire grid.
4686          * @param {Roo.EventObject} e
4687          */
4688         "click" : true,
4689          /**
4690             * @event changed
4691             * Fires when the active item active state changes
4692             * @param {Roo.bootstrap.NavItem} this
4693             * @param {boolean} state the new state
4694              
4695          */
4696         'changed': true,
4697         /**
4698             * @event scrollto
4699             * Fires when scroll to element
4700             * @param {Roo.bootstrap.NavItem} this
4701             * @param {Object} options
4702             * @param {Roo.EventObject} e
4703              
4704          */
4705         'scrollto': true
4706     });
4707    
4708 };
4709
4710 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4711     
4712     href: false,
4713     html: '',
4714     badge: '',
4715     icon: false,
4716     fa : false,
4717     glyphicon: false,
4718     active: false,
4719     preventDefault : false,
4720     tabId : false,
4721     tagtype : 'a',
4722     tag: 'li',
4723     disabled : false,
4724     animateRef : false,
4725     was_active : false,
4726     button_weight : '',
4727     button_outline : false,
4728     
4729     navLink: false,
4730     
4731     getAutoCreate : function(){
4732          
4733         var cfg = {
4734             tag: this.tag,
4735             cls: 'nav-item'
4736         };
4737         
4738         if (this.active) {
4739             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4740         }
4741         if (this.disabled) {
4742             cfg.cls += ' disabled';
4743         }
4744         
4745         // BS4 only?
4746         if (this.button_weight.length) {
4747             cfg.tag = this.href ? 'a' : 'button';
4748             cfg.html = this.html || '';
4749             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
4750             if (this.href) {
4751                 cfg.href = this.href;
4752             }
4753             if (this.fa) {
4754                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
4755             }
4756             
4757             // menu .. should add dropdown-menu class - so no need for carat..
4758             
4759             if (this.badge !== '') {
4760                  
4761                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4762             }
4763             return cfg;
4764         }
4765         
4766         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
4767             cfg.cn = [
4768                 {
4769                     tag: this.tagtype,
4770                     href : this.href || "#",
4771                     html: this.html || ''
4772                 }
4773             ];
4774             if (this.tagtype == 'a') {
4775                 cfg.cn[0].cls = 'nav-link';
4776             }
4777             if (this.icon) {
4778                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
4779             }
4780             if (this.fa) {
4781                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
4782             }
4783             if(this.glyphicon) {
4784                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4785             }
4786             
4787             if (this.menu) {
4788                 
4789                 cfg.cn[0].html += " <span class='caret'></span>";
4790              
4791             }
4792             
4793             if (this.badge !== '') {
4794                  
4795                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4796             }
4797         }
4798         
4799         
4800         
4801         return cfg;
4802     },
4803     onRender : function(ct, position)
4804     {
4805        // Roo.log("Call onRender: " + this.xtype);
4806         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
4807             this.tag = 'div';
4808         }
4809         
4810         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
4811         this.navLink = this.el.select('.nav-link',true).first();
4812         return ret;
4813     },
4814       
4815     
4816     initEvents: function() 
4817     {
4818         if (typeof (this.menu) != 'undefined') {
4819             this.menu.parentType = this.xtype;
4820             this.menu.triggerEl = this.el;
4821             this.menu = this.addxtype(Roo.apply({}, this.menu));
4822         }
4823         
4824         this.el.select('a',true).on('click', this.onClick, this);
4825         
4826         if(this.tagtype == 'span'){
4827             this.el.select('span',true).on('click', this.onClick, this);
4828         }
4829        
4830         // at this point parent should be available..
4831         this.parent().register(this);
4832     },
4833     
4834     onClick : function(e)
4835     {
4836         if (e.getTarget('.dropdown-menu-item')) {
4837             // did you click on a menu itemm.... - then don't trigger onclick..
4838             return;
4839         }
4840         
4841         if(
4842                 this.preventDefault || 
4843                 this.href == '#' 
4844         ){
4845             Roo.log("NavItem - prevent Default?");
4846             e.preventDefault();
4847         }
4848         
4849         if (this.disabled) {
4850             return;
4851         }
4852         
4853         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4854         if (tg && tg.transition) {
4855             Roo.log("waiting for the transitionend");
4856             return;
4857         }
4858         
4859         
4860         
4861         //Roo.log("fire event clicked");
4862         if(this.fireEvent('click', this, e) === false){
4863             return;
4864         };
4865         
4866         if(this.tagtype == 'span'){
4867             return;
4868         }
4869         
4870         //Roo.log(this.href);
4871         var ael = this.el.select('a',true).first();
4872         //Roo.log(ael);
4873         
4874         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4875             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4876             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4877                 return; // ignore... - it's a 'hash' to another page.
4878             }
4879             Roo.log("NavItem - prevent Default?");
4880             e.preventDefault();
4881             this.scrollToElement(e);
4882         }
4883         
4884         
4885         var p =  this.parent();
4886    
4887         if (['tabs','pills'].indexOf(p.type)!==-1) {
4888             if (typeof(p.setActiveItem) !== 'undefined') {
4889                 p.setActiveItem(this);
4890             }
4891         }
4892         
4893         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4894         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4895             // remove the collapsed menu expand...
4896             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
4897         }
4898     },
4899     
4900     isActive: function () {
4901         return this.active
4902     },
4903     setActive : function(state, fire, is_was_active)
4904     {
4905         if (this.active && !state && this.navId) {
4906             this.was_active = true;
4907             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4908             if (nv) {
4909                 nv.clearWasActive(this);
4910             }
4911             
4912         }
4913         this.active = state;
4914         
4915         if (!state ) {
4916             this.el.removeClass('active');
4917             this.navLink ? this.navLink.removeClass('active') : false;
4918         } else if (!this.el.hasClass('active')) {
4919             
4920             this.el.addClass('active');
4921             if (Roo.bootstrap.version == 4 && this.navLink ) {
4922                 this.navLink.addClass('active');
4923             }
4924             
4925         }
4926         if (fire) {
4927             this.fireEvent('changed', this, state);
4928         }
4929         
4930         // show a panel if it's registered and related..
4931         
4932         if (!this.navId || !this.tabId || !state || is_was_active) {
4933             return;
4934         }
4935         
4936         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4937         if (!tg) {
4938             return;
4939         }
4940         var pan = tg.getPanelByName(this.tabId);
4941         if (!pan) {
4942             return;
4943         }
4944         // if we can not flip to new panel - go back to old nav highlight..
4945         if (false == tg.showPanel(pan)) {
4946             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4947             if (nv) {
4948                 var onav = nv.getWasActive();
4949                 if (onav) {
4950                     onav.setActive(true, false, true);
4951                 }
4952             }
4953             
4954         }
4955         
4956         
4957         
4958     },
4959      // this should not be here...
4960     setDisabled : function(state)
4961     {
4962         this.disabled = state;
4963         if (!state ) {
4964             this.el.removeClass('disabled');
4965         } else if (!this.el.hasClass('disabled')) {
4966             this.el.addClass('disabled');
4967         }
4968         
4969     },
4970     
4971     /**
4972      * Fetch the element to display the tooltip on.
4973      * @return {Roo.Element} defaults to this.el
4974      */
4975     tooltipEl : function()
4976     {
4977         return this.el.select('' + this.tagtype + '', true).first();
4978     },
4979     
4980     scrollToElement : function(e)
4981     {
4982         var c = document.body;
4983         
4984         /*
4985          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4986          */
4987         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4988             c = document.documentElement;
4989         }
4990         
4991         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4992         
4993         if(!target){
4994             return;
4995         }
4996
4997         var o = target.calcOffsetsTo(c);
4998         
4999         var options = {
5000             target : target,
5001             value : o[1]
5002         };
5003         
5004         this.fireEvent('scrollto', this, options, e);
5005         
5006         Roo.get(c).scrollTo('top', options.value, true);
5007         
5008         return;
5009     }
5010 });
5011  
5012
5013  /*
5014  * - LGPL
5015  *
5016  * sidebar item
5017  *
5018  *  li
5019  *    <span> icon </span>
5020  *    <span> text </span>
5021  *    <span>badge </span>
5022  */
5023
5024 /**
5025  * @class Roo.bootstrap.NavSidebarItem
5026  * @extends Roo.bootstrap.NavItem
5027  * Bootstrap Navbar.NavSidebarItem class
5028  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
5029  * {Boolean} open is the menu open
5030  * {Boolean} buttonView use button as the tigger el rather that a (default false)
5031  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
5032  * {String} buttonSize (sm|md|lg)the extra classes for the button
5033  * {Boolean} showArrow show arrow next to the text (default true)
5034  * @constructor
5035  * Create a new Navbar Button
5036  * @param {Object} config The config object
5037  */
5038 Roo.bootstrap.NavSidebarItem = function(config){
5039     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
5040     this.addEvents({
5041         // raw events
5042         /**
5043          * @event click
5044          * The raw click event for the entire grid.
5045          * @param {Roo.EventObject} e
5046          */
5047         "click" : true,
5048          /**
5049             * @event changed
5050             * Fires when the active item active state changes
5051             * @param {Roo.bootstrap.NavSidebarItem} this
5052             * @param {boolean} state the new state
5053              
5054          */
5055         'changed': true
5056     });
5057    
5058 };
5059
5060 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
5061     
5062     badgeWeight : 'default',
5063     
5064     open: false,
5065     
5066     buttonView : false,
5067     
5068     buttonWeight : 'default',
5069     
5070     buttonSize : 'md',
5071     
5072     showArrow : true,
5073     
5074     getAutoCreate : function(){
5075         
5076         
5077         var a = {
5078                 tag: 'a',
5079                 href : this.href || '#',
5080                 cls: '',
5081                 html : '',
5082                 cn : []
5083         };
5084         
5085         if(this.buttonView){
5086             a = {
5087                 tag: 'button',
5088                 href : this.href || '#',
5089                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
5090                 html : this.html,
5091                 cn : []
5092             };
5093         }
5094         
5095         var cfg = {
5096             tag: 'li',
5097             cls: '',
5098             cn: [ a ]
5099         };
5100         
5101         if (this.active) {
5102             cfg.cls += ' active';
5103         }
5104         
5105         if (this.disabled) {
5106             cfg.cls += ' disabled';
5107         }
5108         if (this.open) {
5109             cfg.cls += ' open x-open';
5110         }
5111         // left icon..
5112         if (this.glyphicon || this.icon) {
5113             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
5114             a.cn.push({ tag : 'i', cls : c }) ;
5115         }
5116         
5117         if(!this.buttonView){
5118             var span = {
5119                 tag: 'span',
5120                 html : this.html || ''
5121             };
5122
5123             a.cn.push(span);
5124             
5125         }
5126         
5127         if (this.badge !== '') {
5128             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
5129         }
5130         
5131         if (this.menu) {
5132             
5133             if(this.showArrow){
5134                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5135             }
5136             
5137             a.cls += ' dropdown-toggle treeview' ;
5138         }
5139         
5140         return cfg;
5141     },
5142     
5143     initEvents : function()
5144     { 
5145         if (typeof (this.menu) != 'undefined') {
5146             this.menu.parentType = this.xtype;
5147             this.menu.triggerEl = this.el;
5148             this.menu = this.addxtype(Roo.apply({}, this.menu));
5149         }
5150         
5151         this.el.on('click', this.onClick, this);
5152         
5153         if(this.badge !== ''){
5154             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5155         }
5156         
5157     },
5158     
5159     onClick : function(e)
5160     {
5161         if(this.disabled){
5162             e.preventDefault();
5163             return;
5164         }
5165         
5166         if(this.preventDefault){
5167             e.preventDefault();
5168         }
5169         
5170         this.fireEvent('click', this, e);
5171     },
5172     
5173     disable : function()
5174     {
5175         this.setDisabled(true);
5176     },
5177     
5178     enable : function()
5179     {
5180         this.setDisabled(false);
5181     },
5182     
5183     setDisabled : function(state)
5184     {
5185         if(this.disabled == state){
5186             return;
5187         }
5188         
5189         this.disabled = state;
5190         
5191         if (state) {
5192             this.el.addClass('disabled');
5193             return;
5194         }
5195         
5196         this.el.removeClass('disabled');
5197         
5198         return;
5199     },
5200     
5201     setActive : function(state)
5202     {
5203         if(this.active == state){
5204             return;
5205         }
5206         
5207         this.active = state;
5208         
5209         if (state) {
5210             this.el.addClass('active');
5211             return;
5212         }
5213         
5214         this.el.removeClass('active');
5215         
5216         return;
5217     },
5218     
5219     isActive: function () 
5220     {
5221         return this.active;
5222     },
5223     
5224     setBadge : function(str)
5225     {
5226         if(!this.badgeEl){
5227             return;
5228         }
5229         
5230         this.badgeEl.dom.innerHTML = str;
5231     }
5232     
5233    
5234      
5235  
5236 });
5237  
5238
5239  /*
5240  * - LGPL
5241  *
5242  * row
5243  * 
5244  */
5245
5246 /**
5247  * @class Roo.bootstrap.Row
5248  * @extends Roo.bootstrap.Component
5249  * Bootstrap Row class (contains columns...)
5250  * 
5251  * @constructor
5252  * Create a new Row
5253  * @param {Object} config The config object
5254  */
5255
5256 Roo.bootstrap.Row = function(config){
5257     Roo.bootstrap.Row.superclass.constructor.call(this, config);
5258 };
5259
5260 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
5261     
5262     getAutoCreate : function(){
5263        return {
5264             cls: 'row clearfix'
5265        };
5266     }
5267     
5268     
5269 });
5270
5271  
5272
5273  /*
5274  * - LGPL
5275  *
5276  * element
5277  * 
5278  */
5279
5280 /**
5281  * @class Roo.bootstrap.Element
5282  * @extends Roo.bootstrap.Component
5283  * Bootstrap Element class
5284  * @cfg {String} html contents of the element
5285  * @cfg {String} tag tag of the element
5286  * @cfg {String} cls class of the element
5287  * @cfg {Boolean} preventDefault (true|false) default false
5288  * @cfg {Boolean} clickable (true|false) default false
5289  * 
5290  * @constructor
5291  * Create a new Element
5292  * @param {Object} config The config object
5293  */
5294
5295 Roo.bootstrap.Element = function(config){
5296     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5297     
5298     this.addEvents({
5299         // raw events
5300         /**
5301          * @event click
5302          * When a element is chick
5303          * @param {Roo.bootstrap.Element} this
5304          * @param {Roo.EventObject} e
5305          */
5306         "click" : true
5307     });
5308 };
5309
5310 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5311     
5312     tag: 'div',
5313     cls: '',
5314     html: '',
5315     preventDefault: false, 
5316     clickable: false,
5317     
5318     getAutoCreate : function(){
5319         
5320         var cfg = {
5321             tag: this.tag,
5322             // cls: this.cls, double assign in parent class Component.js :: onRender
5323             html: this.html
5324         };
5325         
5326         return cfg;
5327     },
5328     
5329     initEvents: function() 
5330     {
5331         Roo.bootstrap.Element.superclass.initEvents.call(this);
5332         
5333         if(this.clickable){
5334             this.el.on('click', this.onClick, this);
5335         }
5336         
5337     },
5338     
5339     onClick : function(e)
5340     {
5341         if(this.preventDefault){
5342             e.preventDefault();
5343         }
5344         
5345         this.fireEvent('click', this, e);
5346     },
5347     
5348     getValue : function()
5349     {
5350         return this.el.dom.innerHTML;
5351     },
5352     
5353     setValue : function(value)
5354     {
5355         this.el.dom.innerHTML = value;
5356     }
5357    
5358 });
5359
5360  
5361
5362  /*
5363  * - LGPL
5364  *
5365  * pagination
5366  * 
5367  */
5368
5369 /**
5370  * @class Roo.bootstrap.Pagination
5371  * @extends Roo.bootstrap.Component
5372  * Bootstrap Pagination class
5373  * @cfg {String} size xs | sm | md | lg
5374  * @cfg {Boolean} inverse false | true
5375  * 
5376  * @constructor
5377  * Create a new Pagination
5378  * @param {Object} config The config object
5379  */
5380
5381 Roo.bootstrap.Pagination = function(config){
5382     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5383 };
5384
5385 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5386     
5387     cls: false,
5388     size: false,
5389     inverse: false,
5390     
5391     getAutoCreate : function(){
5392         var cfg = {
5393             tag: 'ul',
5394                 cls: 'pagination'
5395         };
5396         if (this.inverse) {
5397             cfg.cls += ' inverse';
5398         }
5399         if (this.html) {
5400             cfg.html=this.html;
5401         }
5402         if (this.cls) {
5403             cfg.cls += " " + this.cls;
5404         }
5405         return cfg;
5406     }
5407    
5408 });
5409
5410  
5411
5412  /*
5413  * - LGPL
5414  *
5415  * Pagination item
5416  * 
5417  */
5418
5419
5420 /**
5421  * @class Roo.bootstrap.PaginationItem
5422  * @extends Roo.bootstrap.Component
5423  * Bootstrap PaginationItem class
5424  * @cfg {String} html text
5425  * @cfg {String} href the link
5426  * @cfg {Boolean} preventDefault (true | false) default true
5427  * @cfg {Boolean} active (true | false) default false
5428  * @cfg {Boolean} disabled default false
5429  * 
5430  * 
5431  * @constructor
5432  * Create a new PaginationItem
5433  * @param {Object} config The config object
5434  */
5435
5436
5437 Roo.bootstrap.PaginationItem = function(config){
5438     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5439     this.addEvents({
5440         // raw events
5441         /**
5442          * @event click
5443          * The raw click event for the entire grid.
5444          * @param {Roo.EventObject} e
5445          */
5446         "click" : true
5447     });
5448 };
5449
5450 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5451     
5452     href : false,
5453     html : false,
5454     preventDefault: true,
5455     active : false,
5456     cls : false,
5457     disabled: false,
5458     
5459     getAutoCreate : function(){
5460         var cfg= {
5461             tag: 'li',
5462             cn: [
5463                 {
5464                     tag : 'a',
5465                     href : this.href ? this.href : '#',
5466                     html : this.html ? this.html : ''
5467                 }
5468             ]
5469         };
5470         
5471         if(this.cls){
5472             cfg.cls = this.cls;
5473         }
5474         
5475         if(this.disabled){
5476             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5477         }
5478         
5479         if(this.active){
5480             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5481         }
5482         
5483         return cfg;
5484     },
5485     
5486     initEvents: function() {
5487         
5488         this.el.on('click', this.onClick, this);
5489         
5490     },
5491     onClick : function(e)
5492     {
5493         Roo.log('PaginationItem on click ');
5494         if(this.preventDefault){
5495             e.preventDefault();
5496         }
5497         
5498         if(this.disabled){
5499             return;
5500         }
5501         
5502         this.fireEvent('click', this, e);
5503     }
5504    
5505 });
5506
5507  
5508
5509  /*
5510  * - LGPL
5511  *
5512  * slider
5513  * 
5514  */
5515
5516
5517 /**
5518  * @class Roo.bootstrap.Slider
5519  * @extends Roo.bootstrap.Component
5520  * Bootstrap Slider class
5521  *    
5522  * @constructor
5523  * Create a new Slider
5524  * @param {Object} config The config object
5525  */
5526
5527 Roo.bootstrap.Slider = function(config){
5528     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5529 };
5530
5531 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5532     
5533     getAutoCreate : function(){
5534         
5535         var cfg = {
5536             tag: 'div',
5537             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5538             cn: [
5539                 {
5540                     tag: 'a',
5541                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5542                 }
5543             ]
5544         };
5545         
5546         return cfg;
5547     }
5548    
5549 });
5550
5551  /*
5552  * Based on:
5553  * Ext JS Library 1.1.1
5554  * Copyright(c) 2006-2007, Ext JS, LLC.
5555  *
5556  * Originally Released Under LGPL - original licence link has changed is not relivant.
5557  *
5558  * Fork - LGPL
5559  * <script type="text/javascript">
5560  */
5561  
5562
5563 /**
5564  * @class Roo.grid.ColumnModel
5565  * @extends Roo.util.Observable
5566  * This is the default implementation of a ColumnModel used by the Grid. It defines
5567  * the columns in the grid.
5568  * <br>Usage:<br>
5569  <pre><code>
5570  var colModel = new Roo.grid.ColumnModel([
5571         {header: "Ticker", width: 60, sortable: true, locked: true},
5572         {header: "Company Name", width: 150, sortable: true},
5573         {header: "Market Cap.", width: 100, sortable: true},
5574         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5575         {header: "Employees", width: 100, sortable: true, resizable: false}
5576  ]);
5577  </code></pre>
5578  * <p>
5579  
5580  * The config options listed for this class are options which may appear in each
5581  * individual column definition.
5582  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5583  * @constructor
5584  * @param {Object} config An Array of column config objects. See this class's
5585  * config objects for details.
5586 */
5587 Roo.grid.ColumnModel = function(config){
5588         /**
5589      * The config passed into the constructor
5590      */
5591     this.config = config;
5592     this.lookup = {};
5593
5594     // if no id, create one
5595     // if the column does not have a dataIndex mapping,
5596     // map it to the order it is in the config
5597     for(var i = 0, len = config.length; i < len; i++){
5598         var c = config[i];
5599         if(typeof c.dataIndex == "undefined"){
5600             c.dataIndex = i;
5601         }
5602         if(typeof c.renderer == "string"){
5603             c.renderer = Roo.util.Format[c.renderer];
5604         }
5605         if(typeof c.id == "undefined"){
5606             c.id = Roo.id();
5607         }
5608         if(c.editor && c.editor.xtype){
5609             c.editor  = Roo.factory(c.editor, Roo.grid);
5610         }
5611         if(c.editor && c.editor.isFormField){
5612             c.editor = new Roo.grid.GridEditor(c.editor);
5613         }
5614         this.lookup[c.id] = c;
5615     }
5616
5617     /**
5618      * The width of columns which have no width specified (defaults to 100)
5619      * @type Number
5620      */
5621     this.defaultWidth = 100;
5622
5623     /**
5624      * Default sortable of columns which have no sortable specified (defaults to false)
5625      * @type Boolean
5626      */
5627     this.defaultSortable = false;
5628
5629     this.addEvents({
5630         /**
5631              * @event widthchange
5632              * Fires when the width of a column changes.
5633              * @param {ColumnModel} this
5634              * @param {Number} columnIndex The column index
5635              * @param {Number} newWidth The new width
5636              */
5637             "widthchange": true,
5638         /**
5639              * @event headerchange
5640              * Fires when the text of a header changes.
5641              * @param {ColumnModel} this
5642              * @param {Number} columnIndex The column index
5643              * @param {Number} newText The new header text
5644              */
5645             "headerchange": true,
5646         /**
5647              * @event hiddenchange
5648              * Fires when a column is hidden or "unhidden".
5649              * @param {ColumnModel} this
5650              * @param {Number} columnIndex The column index
5651              * @param {Boolean} hidden true if hidden, false otherwise
5652              */
5653             "hiddenchange": true,
5654             /**
5655          * @event columnmoved
5656          * Fires when a column is moved.
5657          * @param {ColumnModel} this
5658          * @param {Number} oldIndex
5659          * @param {Number} newIndex
5660          */
5661         "columnmoved" : true,
5662         /**
5663          * @event columlockchange
5664          * Fires when a column's locked state is changed
5665          * @param {ColumnModel} this
5666          * @param {Number} colIndex
5667          * @param {Boolean} locked true if locked
5668          */
5669         "columnlockchange" : true
5670     });
5671     Roo.grid.ColumnModel.superclass.constructor.call(this);
5672 };
5673 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5674     /**
5675      * @cfg {String} header The header text to display in the Grid view.
5676      */
5677     /**
5678      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5679      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5680      * specified, the column's index is used as an index into the Record's data Array.
5681      */
5682     /**
5683      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5684      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5685      */
5686     /**
5687      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5688      * Defaults to the value of the {@link #defaultSortable} property.
5689      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5690      */
5691     /**
5692      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5693      */
5694     /**
5695      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5696      */
5697     /**
5698      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5699      */
5700     /**
5701      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5702      */
5703     /**
5704      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5705      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5706      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5707      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5708      */
5709        /**
5710      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5711      */
5712     /**
5713      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5714      */
5715     /**
5716      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5717      */
5718     /**
5719      * @cfg {String} cursor (Optional)
5720      */
5721     /**
5722      * @cfg {String} tooltip (Optional)
5723      */
5724     /**
5725      * @cfg {Number} xs (Optional)
5726      */
5727     /**
5728      * @cfg {Number} sm (Optional)
5729      */
5730     /**
5731      * @cfg {Number} md (Optional)
5732      */
5733     /**
5734      * @cfg {Number} lg (Optional)
5735      */
5736     /**
5737      * Returns the id of the column at the specified index.
5738      * @param {Number} index The column index
5739      * @return {String} the id
5740      */
5741     getColumnId : function(index){
5742         return this.config[index].id;
5743     },
5744
5745     /**
5746      * Returns the column for a specified id.
5747      * @param {String} id The column id
5748      * @return {Object} the column
5749      */
5750     getColumnById : function(id){
5751         return this.lookup[id];
5752     },
5753
5754     
5755     /**
5756      * Returns the column for a specified dataIndex.
5757      * @param {String} dataIndex The column dataIndex
5758      * @return {Object|Boolean} the column or false if not found
5759      */
5760     getColumnByDataIndex: function(dataIndex){
5761         var index = this.findColumnIndex(dataIndex);
5762         return index > -1 ? this.config[index] : false;
5763     },
5764     
5765     /**
5766      * Returns the index for a specified column id.
5767      * @param {String} id The column id
5768      * @return {Number} the index, or -1 if not found
5769      */
5770     getIndexById : function(id){
5771         for(var i = 0, len = this.config.length; i < len; i++){
5772             if(this.config[i].id == id){
5773                 return i;
5774             }
5775         }
5776         return -1;
5777     },
5778     
5779     /**
5780      * Returns the index for a specified column dataIndex.
5781      * @param {String} dataIndex The column dataIndex
5782      * @return {Number} the index, or -1 if not found
5783      */
5784     
5785     findColumnIndex : function(dataIndex){
5786         for(var i = 0, len = this.config.length; i < len; i++){
5787             if(this.config[i].dataIndex == dataIndex){
5788                 return i;
5789             }
5790         }
5791         return -1;
5792     },
5793     
5794     
5795     moveColumn : function(oldIndex, newIndex){
5796         var c = this.config[oldIndex];
5797         this.config.splice(oldIndex, 1);
5798         this.config.splice(newIndex, 0, c);
5799         this.dataMap = null;
5800         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5801     },
5802
5803     isLocked : function(colIndex){
5804         return this.config[colIndex].locked === true;
5805     },
5806
5807     setLocked : function(colIndex, value, suppressEvent){
5808         if(this.isLocked(colIndex) == value){
5809             return;
5810         }
5811         this.config[colIndex].locked = value;
5812         if(!suppressEvent){
5813             this.fireEvent("columnlockchange", this, colIndex, value);
5814         }
5815     },
5816
5817     getTotalLockedWidth : function(){
5818         var totalWidth = 0;
5819         for(var i = 0; i < this.config.length; i++){
5820             if(this.isLocked(i) && !this.isHidden(i)){
5821                 this.totalWidth += this.getColumnWidth(i);
5822             }
5823         }
5824         return totalWidth;
5825     },
5826
5827     getLockedCount : function(){
5828         for(var i = 0, len = this.config.length; i < len; i++){
5829             if(!this.isLocked(i)){
5830                 return i;
5831             }
5832         }
5833         
5834         return this.config.length;
5835     },
5836
5837     /**
5838      * Returns the number of columns.
5839      * @return {Number}
5840      */
5841     getColumnCount : function(visibleOnly){
5842         if(visibleOnly === true){
5843             var c = 0;
5844             for(var i = 0, len = this.config.length; i < len; i++){
5845                 if(!this.isHidden(i)){
5846                     c++;
5847                 }
5848             }
5849             return c;
5850         }
5851         return this.config.length;
5852     },
5853
5854     /**
5855      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5856      * @param {Function} fn
5857      * @param {Object} scope (optional)
5858      * @return {Array} result
5859      */
5860     getColumnsBy : function(fn, scope){
5861         var r = [];
5862         for(var i = 0, len = this.config.length; i < len; i++){
5863             var c = this.config[i];
5864             if(fn.call(scope||this, c, i) === true){
5865                 r[r.length] = c;
5866             }
5867         }
5868         return r;
5869     },
5870
5871     /**
5872      * Returns true if the specified column is sortable.
5873      * @param {Number} col The column index
5874      * @return {Boolean}
5875      */
5876     isSortable : function(col){
5877         if(typeof this.config[col].sortable == "undefined"){
5878             return this.defaultSortable;
5879         }
5880         return this.config[col].sortable;
5881     },
5882
5883     /**
5884      * Returns the rendering (formatting) function defined for the column.
5885      * @param {Number} col The column index.
5886      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5887      */
5888     getRenderer : function(col){
5889         if(!this.config[col].renderer){
5890             return Roo.grid.ColumnModel.defaultRenderer;
5891         }
5892         return this.config[col].renderer;
5893     },
5894
5895     /**
5896      * Sets the rendering (formatting) function for a column.
5897      * @param {Number} col The column index
5898      * @param {Function} fn The function to use to process the cell's raw data
5899      * to return HTML markup for the grid view. The render function is called with
5900      * the following parameters:<ul>
5901      * <li>Data value.</li>
5902      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5903      * <li>css A CSS style string to apply to the table cell.</li>
5904      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5905      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5906      * <li>Row index</li>
5907      * <li>Column index</li>
5908      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5909      */
5910     setRenderer : function(col, fn){
5911         this.config[col].renderer = fn;
5912     },
5913
5914     /**
5915      * Returns the width for the specified column.
5916      * @param {Number} col The column index
5917      * @return {Number}
5918      */
5919     getColumnWidth : function(col){
5920         return this.config[col].width * 1 || this.defaultWidth;
5921     },
5922
5923     /**
5924      * Sets the width for a column.
5925      * @param {Number} col The column index
5926      * @param {Number} width The new width
5927      */
5928     setColumnWidth : function(col, width, suppressEvent){
5929         this.config[col].width = width;
5930         this.totalWidth = null;
5931         if(!suppressEvent){
5932              this.fireEvent("widthchange", this, col, width);
5933         }
5934     },
5935
5936     /**
5937      * Returns the total width of all columns.
5938      * @param {Boolean} includeHidden True to include hidden column widths
5939      * @return {Number}
5940      */
5941     getTotalWidth : function(includeHidden){
5942         if(!this.totalWidth){
5943             this.totalWidth = 0;
5944             for(var i = 0, len = this.config.length; i < len; i++){
5945                 if(includeHidden || !this.isHidden(i)){
5946                     this.totalWidth += this.getColumnWidth(i);
5947                 }
5948             }
5949         }
5950         return this.totalWidth;
5951     },
5952
5953     /**
5954      * Returns the header for the specified column.
5955      * @param {Number} col The column index
5956      * @return {String}
5957      */
5958     getColumnHeader : function(col){
5959         return this.config[col].header;
5960     },
5961
5962     /**
5963      * Sets the header for a column.
5964      * @param {Number} col The column index
5965      * @param {String} header The new header
5966      */
5967     setColumnHeader : function(col, header){
5968         this.config[col].header = header;
5969         this.fireEvent("headerchange", this, col, header);
5970     },
5971
5972     /**
5973      * Returns the tooltip for the specified column.
5974      * @param {Number} col The column index
5975      * @return {String}
5976      */
5977     getColumnTooltip : function(col){
5978             return this.config[col].tooltip;
5979     },
5980     /**
5981      * Sets the tooltip for a column.
5982      * @param {Number} col The column index
5983      * @param {String} tooltip The new tooltip
5984      */
5985     setColumnTooltip : function(col, tooltip){
5986             this.config[col].tooltip = tooltip;
5987     },
5988
5989     /**
5990      * Returns the dataIndex for the specified column.
5991      * @param {Number} col The column index
5992      * @return {Number}
5993      */
5994     getDataIndex : function(col){
5995         return this.config[col].dataIndex;
5996     },
5997
5998     /**
5999      * Sets the dataIndex for a column.
6000      * @param {Number} col The column index
6001      * @param {Number} dataIndex The new dataIndex
6002      */
6003     setDataIndex : function(col, dataIndex){
6004         this.config[col].dataIndex = dataIndex;
6005     },
6006
6007     
6008     
6009     /**
6010      * Returns true if the cell is editable.
6011      * @param {Number} colIndex The column index
6012      * @param {Number} rowIndex The row index - this is nto actually used..?
6013      * @return {Boolean}
6014      */
6015     isCellEditable : function(colIndex, rowIndex){
6016         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
6017     },
6018
6019     /**
6020      * Returns the editor defined for the cell/column.
6021      * return false or null to disable editing.
6022      * @param {Number} colIndex The column index
6023      * @param {Number} rowIndex The row index
6024      * @return {Object}
6025      */
6026     getCellEditor : function(colIndex, rowIndex){
6027         return this.config[colIndex].editor;
6028     },
6029
6030     /**
6031      * Sets if a column is editable.
6032      * @param {Number} col The column index
6033      * @param {Boolean} editable True if the column is editable
6034      */
6035     setEditable : function(col, editable){
6036         this.config[col].editable = editable;
6037     },
6038
6039
6040     /**
6041      * Returns true if the column is hidden.
6042      * @param {Number} colIndex The column index
6043      * @return {Boolean}
6044      */
6045     isHidden : function(colIndex){
6046         return this.config[colIndex].hidden;
6047     },
6048
6049
6050     /**
6051      * Returns true if the column width cannot be changed
6052      */
6053     isFixed : function(colIndex){
6054         return this.config[colIndex].fixed;
6055     },
6056
6057     /**
6058      * Returns true if the column can be resized
6059      * @return {Boolean}
6060      */
6061     isResizable : function(colIndex){
6062         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
6063     },
6064     /**
6065      * Sets if a column is hidden.
6066      * @param {Number} colIndex The column index
6067      * @param {Boolean} hidden True if the column is hidden
6068      */
6069     setHidden : function(colIndex, hidden){
6070         this.config[colIndex].hidden = hidden;
6071         this.totalWidth = null;
6072         this.fireEvent("hiddenchange", this, colIndex, hidden);
6073     },
6074
6075     /**
6076      * Sets the editor for a column.
6077      * @param {Number} col The column index
6078      * @param {Object} editor The editor object
6079      */
6080     setEditor : function(col, editor){
6081         this.config[col].editor = editor;
6082     }
6083 });
6084
6085 Roo.grid.ColumnModel.defaultRenderer = function(value)
6086 {
6087     if(typeof value == "object") {
6088         return value;
6089     }
6090         if(typeof value == "string" && value.length < 1){
6091             return "&#160;";
6092         }
6093     
6094         return String.format("{0}", value);
6095 };
6096
6097 // Alias for backwards compatibility
6098 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
6099 /*
6100  * Based on:
6101  * Ext JS Library 1.1.1
6102  * Copyright(c) 2006-2007, Ext JS, LLC.
6103  *
6104  * Originally Released Under LGPL - original licence link has changed is not relivant.
6105  *
6106  * Fork - LGPL
6107  * <script type="text/javascript">
6108  */
6109  
6110 /**
6111  * @class Roo.LoadMask
6112  * A simple utility class for generically masking elements while loading data.  If the element being masked has
6113  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
6114  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
6115  * element's UpdateManager load indicator and will be destroyed after the initial load.
6116  * @constructor
6117  * Create a new LoadMask
6118  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
6119  * @param {Object} config The config object
6120  */
6121 Roo.LoadMask = function(el, config){
6122     this.el = Roo.get(el);
6123     Roo.apply(this, config);
6124     if(this.store){
6125         this.store.on('beforeload', this.onBeforeLoad, this);
6126         this.store.on('load', this.onLoad, this);
6127         this.store.on('loadexception', this.onLoadException, this);
6128         this.removeMask = false;
6129     }else{
6130         var um = this.el.getUpdateManager();
6131         um.showLoadIndicator = false; // disable the default indicator
6132         um.on('beforeupdate', this.onBeforeLoad, this);
6133         um.on('update', this.onLoad, this);
6134         um.on('failure', this.onLoad, this);
6135         this.removeMask = true;
6136     }
6137 };
6138
6139 Roo.LoadMask.prototype = {
6140     /**
6141      * @cfg {Boolean} removeMask
6142      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6143      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
6144      */
6145     /**
6146      * @cfg {String} msg
6147      * The text to display in a centered loading message box (defaults to 'Loading...')
6148      */
6149     msg : 'Loading...',
6150     /**
6151      * @cfg {String} msgCls
6152      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6153      */
6154     msgCls : 'x-mask-loading',
6155
6156     /**
6157      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6158      * @type Boolean
6159      */
6160     disabled: false,
6161
6162     /**
6163      * Disables the mask to prevent it from being displayed
6164      */
6165     disable : function(){
6166        this.disabled = true;
6167     },
6168
6169     /**
6170      * Enables the mask so that it can be displayed
6171      */
6172     enable : function(){
6173         this.disabled = false;
6174     },
6175     
6176     onLoadException : function()
6177     {
6178         Roo.log(arguments);
6179         
6180         if (typeof(arguments[3]) != 'undefined') {
6181             Roo.MessageBox.alert("Error loading",arguments[3]);
6182         } 
6183         /*
6184         try {
6185             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6186                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6187             }   
6188         } catch(e) {
6189             
6190         }
6191         */
6192     
6193         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6194     },
6195     // private
6196     onLoad : function()
6197     {
6198         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6199     },
6200
6201     // private
6202     onBeforeLoad : function(){
6203         if(!this.disabled){
6204             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6205         }
6206     },
6207
6208     // private
6209     destroy : function(){
6210         if(this.store){
6211             this.store.un('beforeload', this.onBeforeLoad, this);
6212             this.store.un('load', this.onLoad, this);
6213             this.store.un('loadexception', this.onLoadException, this);
6214         }else{
6215             var um = this.el.getUpdateManager();
6216             um.un('beforeupdate', this.onBeforeLoad, this);
6217             um.un('update', this.onLoad, this);
6218             um.un('failure', this.onLoad, this);
6219         }
6220     }
6221 };/*
6222  * - LGPL
6223  *
6224  * table
6225  * 
6226  */
6227
6228 /**
6229  * @class Roo.bootstrap.Table
6230  * @extends Roo.bootstrap.Component
6231  * Bootstrap Table class
6232  * @cfg {String} cls table class
6233  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6234  * @cfg {String} bgcolor Specifies the background color for a table
6235  * @cfg {Number} border Specifies whether the table cells should have borders or not
6236  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6237  * @cfg {Number} cellspacing Specifies the space between cells
6238  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6239  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6240  * @cfg {String} sortable Specifies that the table should be sortable
6241  * @cfg {String} summary Specifies a summary of the content of a table
6242  * @cfg {Number} width Specifies the width of a table
6243  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6244  * 
6245  * @cfg {boolean} striped Should the rows be alternative striped
6246  * @cfg {boolean} bordered Add borders to the table
6247  * @cfg {boolean} hover Add hover highlighting
6248  * @cfg {boolean} condensed Format condensed
6249  * @cfg {boolean} responsive Format condensed
6250  * @cfg {Boolean} loadMask (true|false) default false
6251  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6252  * @cfg {Boolean} headerShow (true|false) generate thead, default true
6253  * @cfg {Boolean} rowSelection (true|false) default false
6254  * @cfg {Boolean} cellSelection (true|false) default false
6255  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6256  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
6257  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
6258  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
6259  
6260  * 
6261  * @constructor
6262  * Create a new Table
6263  * @param {Object} config The config object
6264  */
6265
6266 Roo.bootstrap.Table = function(config){
6267     Roo.bootstrap.Table.superclass.constructor.call(this, config);
6268     
6269   
6270     
6271     // BC...
6272     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6273     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6274     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6275     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6276     
6277     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6278     if (this.sm) {
6279         this.sm.grid = this;
6280         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6281         this.sm = this.selModel;
6282         this.sm.xmodule = this.xmodule || false;
6283     }
6284     
6285     if (this.cm && typeof(this.cm.config) == 'undefined') {
6286         this.colModel = new Roo.grid.ColumnModel(this.cm);
6287         this.cm = this.colModel;
6288         this.cm.xmodule = this.xmodule || false;
6289     }
6290     if (this.store) {
6291         this.store= Roo.factory(this.store, Roo.data);
6292         this.ds = this.store;
6293         this.ds.xmodule = this.xmodule || false;
6294          
6295     }
6296     if (this.footer && this.store) {
6297         this.footer.dataSource = this.ds;
6298         this.footer = Roo.factory(this.footer);
6299     }
6300     
6301     /** @private */
6302     this.addEvents({
6303         /**
6304          * @event cellclick
6305          * Fires when a cell is clicked
6306          * @param {Roo.bootstrap.Table} this
6307          * @param {Roo.Element} el
6308          * @param {Number} rowIndex
6309          * @param {Number} columnIndex
6310          * @param {Roo.EventObject} e
6311          */
6312         "cellclick" : true,
6313         /**
6314          * @event celldblclick
6315          * Fires when a cell is double clicked
6316          * @param {Roo.bootstrap.Table} this
6317          * @param {Roo.Element} el
6318          * @param {Number} rowIndex
6319          * @param {Number} columnIndex
6320          * @param {Roo.EventObject} e
6321          */
6322         "celldblclick" : true,
6323         /**
6324          * @event rowclick
6325          * Fires when a row is clicked
6326          * @param {Roo.bootstrap.Table} this
6327          * @param {Roo.Element} el
6328          * @param {Number} rowIndex
6329          * @param {Roo.EventObject} e
6330          */
6331         "rowclick" : true,
6332         /**
6333          * @event rowdblclick
6334          * Fires when a row is double clicked
6335          * @param {Roo.bootstrap.Table} this
6336          * @param {Roo.Element} el
6337          * @param {Number} rowIndex
6338          * @param {Roo.EventObject} e
6339          */
6340         "rowdblclick" : true,
6341         /**
6342          * @event mouseover
6343          * Fires when a mouseover occur
6344          * @param {Roo.bootstrap.Table} this
6345          * @param {Roo.Element} el
6346          * @param {Number} rowIndex
6347          * @param {Number} columnIndex
6348          * @param {Roo.EventObject} e
6349          */
6350         "mouseover" : true,
6351         /**
6352          * @event mouseout
6353          * Fires when a mouseout occur
6354          * @param {Roo.bootstrap.Table} this
6355          * @param {Roo.Element} el
6356          * @param {Number} rowIndex
6357          * @param {Number} columnIndex
6358          * @param {Roo.EventObject} e
6359          */
6360         "mouseout" : true,
6361         /**
6362          * @event rowclass
6363          * Fires when a row is rendered, so you can change add a style to it.
6364          * @param {Roo.bootstrap.Table} this
6365          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6366          */
6367         'rowclass' : true,
6368           /**
6369          * @event rowsrendered
6370          * Fires when all the  rows have been rendered
6371          * @param {Roo.bootstrap.Table} this
6372          */
6373         'rowsrendered' : true,
6374         /**
6375          * @event contextmenu
6376          * The raw contextmenu event for the entire grid.
6377          * @param {Roo.EventObject} e
6378          */
6379         "contextmenu" : true,
6380         /**
6381          * @event rowcontextmenu
6382          * Fires when a row is right clicked
6383          * @param {Roo.bootstrap.Table} this
6384          * @param {Number} rowIndex
6385          * @param {Roo.EventObject} e
6386          */
6387         "rowcontextmenu" : true,
6388         /**
6389          * @event cellcontextmenu
6390          * Fires when a cell is right clicked
6391          * @param {Roo.bootstrap.Table} this
6392          * @param {Number} rowIndex
6393          * @param {Number} cellIndex
6394          * @param {Roo.EventObject} e
6395          */
6396          "cellcontextmenu" : true,
6397          /**
6398          * @event headercontextmenu
6399          * Fires when a header is right clicked
6400          * @param {Roo.bootstrap.Table} this
6401          * @param {Number} columnIndex
6402          * @param {Roo.EventObject} e
6403          */
6404         "headercontextmenu" : true
6405     });
6406 };
6407
6408 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6409     
6410     cls: false,
6411     align: false,
6412     bgcolor: false,
6413     border: false,
6414     cellpadding: false,
6415     cellspacing: false,
6416     frame: false,
6417     rules: false,
6418     sortable: false,
6419     summary: false,
6420     width: false,
6421     striped : false,
6422     scrollBody : false,
6423     bordered: false,
6424     hover:  false,
6425     condensed : false,
6426     responsive : false,
6427     sm : false,
6428     cm : false,
6429     store : false,
6430     loadMask : false,
6431     footerShow : true,
6432     headerShow : true,
6433   
6434     rowSelection : false,
6435     cellSelection : false,
6436     layout : false,
6437     
6438     // Roo.Element - the tbody
6439     mainBody: false,
6440     // Roo.Element - thead element
6441     mainHead: false,
6442     
6443     container: false, // used by gridpanel...
6444     
6445     lazyLoad : false,
6446     
6447     CSS : Roo.util.CSS,
6448     
6449     auto_hide_footer : false,
6450     
6451     getAutoCreate : function()
6452     {
6453         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6454         
6455         cfg = {
6456             tag: 'table',
6457             cls : 'table',
6458             cn : []
6459         };
6460         if (this.scrollBody) {
6461             cfg.cls += ' table-body-fixed';
6462         }    
6463         if (this.striped) {
6464             cfg.cls += ' table-striped';
6465         }
6466         
6467         if (this.hover) {
6468             cfg.cls += ' table-hover';
6469         }
6470         if (this.bordered) {
6471             cfg.cls += ' table-bordered';
6472         }
6473         if (this.condensed) {
6474             cfg.cls += ' table-condensed';
6475         }
6476         if (this.responsive) {
6477             cfg.cls += ' table-responsive';
6478         }
6479         
6480         if (this.cls) {
6481             cfg.cls+=  ' ' +this.cls;
6482         }
6483         
6484         // this lot should be simplifed...
6485         var _t = this;
6486         var cp = [
6487             'align',
6488             'bgcolor',
6489             'border',
6490             'cellpadding',
6491             'cellspacing',
6492             'frame',
6493             'rules',
6494             'sortable',
6495             'summary',
6496             'width'
6497         ].forEach(function(k) {
6498             if (_t[k]) {
6499                 cfg[k] = _t[k];
6500             }
6501         });
6502         
6503         
6504         if (this.layout) {
6505             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6506         }
6507         
6508         if(this.store || this.cm){
6509             if(this.headerShow){
6510                 cfg.cn.push(this.renderHeader());
6511             }
6512             
6513             cfg.cn.push(this.renderBody());
6514             
6515             if(this.footerShow){
6516                 cfg.cn.push(this.renderFooter());
6517             }
6518             // where does this come from?
6519             //cfg.cls+=  ' TableGrid';
6520         }
6521         
6522         return { cn : [ cfg ] };
6523     },
6524     
6525     initEvents : function()
6526     {   
6527         if(!this.store || !this.cm){
6528             return;
6529         }
6530         if (this.selModel) {
6531             this.selModel.initEvents();
6532         }
6533         
6534         
6535         //Roo.log('initEvents with ds!!!!');
6536         
6537         this.mainBody = this.el.select('tbody', true).first();
6538         this.mainHead = this.el.select('thead', true).first();
6539         this.mainFoot = this.el.select('tfoot', true).first();
6540         
6541         
6542         
6543         var _this = this;
6544         
6545         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6546             e.on('click', _this.sort, _this);
6547         });
6548         
6549         this.mainBody.on("click", this.onClick, this);
6550         this.mainBody.on("dblclick", this.onDblClick, this);
6551         
6552         // why is this done????? = it breaks dialogs??
6553         //this.parent().el.setStyle('position', 'relative');
6554         
6555         
6556         if (this.footer) {
6557             this.footer.parentId = this.id;
6558             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6559             
6560             if(this.lazyLoad){
6561                 this.el.select('tfoot tr td').first().addClass('hide');
6562             }
6563         } 
6564         
6565         if(this.loadMask) {
6566             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6567         }
6568         
6569         this.store.on('load', this.onLoad, this);
6570         this.store.on('beforeload', this.onBeforeLoad, this);
6571         this.store.on('update', this.onUpdate, this);
6572         this.store.on('add', this.onAdd, this);
6573         this.store.on("clear", this.clear, this);
6574         
6575         this.el.on("contextmenu", this.onContextMenu, this);
6576         
6577         this.mainBody.on('scroll', this.onBodyScroll, this);
6578         
6579         this.cm.on("headerchange", this.onHeaderChange, this);
6580         
6581         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6582         
6583     },
6584     
6585     onContextMenu : function(e, t)
6586     {
6587         this.processEvent("contextmenu", e);
6588     },
6589     
6590     processEvent : function(name, e)
6591     {
6592         if (name != 'touchstart' ) {
6593             this.fireEvent(name, e);    
6594         }
6595         
6596         var t = e.getTarget();
6597         
6598         var cell = Roo.get(t);
6599         
6600         if(!cell){
6601             return;
6602         }
6603         
6604         if(cell.findParent('tfoot', false, true)){
6605             return;
6606         }
6607         
6608         if(cell.findParent('thead', false, true)){
6609             
6610             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6611                 cell = Roo.get(t).findParent('th', false, true);
6612                 if (!cell) {
6613                     Roo.log("failed to find th in thead?");
6614                     Roo.log(e.getTarget());
6615                     return;
6616                 }
6617             }
6618             
6619             var cellIndex = cell.dom.cellIndex;
6620             
6621             var ename = name == 'touchstart' ? 'click' : name;
6622             this.fireEvent("header" + ename, this, cellIndex, e);
6623             
6624             return;
6625         }
6626         
6627         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6628             cell = Roo.get(t).findParent('td', false, true);
6629             if (!cell) {
6630                 Roo.log("failed to find th in tbody?");
6631                 Roo.log(e.getTarget());
6632                 return;
6633             }
6634         }
6635         
6636         var row = cell.findParent('tr', false, true);
6637         var cellIndex = cell.dom.cellIndex;
6638         var rowIndex = row.dom.rowIndex - 1;
6639         
6640         if(row !== false){
6641             
6642             this.fireEvent("row" + name, this, rowIndex, e);
6643             
6644             if(cell !== false){
6645             
6646                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6647             }
6648         }
6649         
6650     },
6651     
6652     onMouseover : function(e, el)
6653     {
6654         var cell = Roo.get(el);
6655         
6656         if(!cell){
6657             return;
6658         }
6659         
6660         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6661             cell = cell.findParent('td', false, true);
6662         }
6663         
6664         var row = cell.findParent('tr', false, true);
6665         var cellIndex = cell.dom.cellIndex;
6666         var rowIndex = row.dom.rowIndex - 1; // start from 0
6667         
6668         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6669         
6670     },
6671     
6672     onMouseout : function(e, el)
6673     {
6674         var cell = Roo.get(el);
6675         
6676         if(!cell){
6677             return;
6678         }
6679         
6680         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6681             cell = cell.findParent('td', false, true);
6682         }
6683         
6684         var row = cell.findParent('tr', false, true);
6685         var cellIndex = cell.dom.cellIndex;
6686         var rowIndex = row.dom.rowIndex - 1; // start from 0
6687         
6688         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6689         
6690     },
6691     
6692     onClick : function(e, el)
6693     {
6694         var cell = Roo.get(el);
6695         
6696         if(!cell || (!this.cellSelection && !this.rowSelection)){
6697             return;
6698         }
6699         
6700         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6701             cell = cell.findParent('td', false, true);
6702         }
6703         
6704         if(!cell || typeof(cell) == 'undefined'){
6705             return;
6706         }
6707         
6708         var row = cell.findParent('tr', false, true);
6709         
6710         if(!row || typeof(row) == 'undefined'){
6711             return;
6712         }
6713         
6714         var cellIndex = cell.dom.cellIndex;
6715         var rowIndex = this.getRowIndex(row);
6716         
6717         // why??? - should these not be based on SelectionModel?
6718         if(this.cellSelection){
6719             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6720         }
6721         
6722         if(this.rowSelection){
6723             this.fireEvent('rowclick', this, row, rowIndex, e);
6724         }
6725         
6726         
6727     },
6728         
6729     onDblClick : function(e,el)
6730     {
6731         var cell = Roo.get(el);
6732         
6733         if(!cell || (!this.cellSelection && !this.rowSelection)){
6734             return;
6735         }
6736         
6737         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6738             cell = cell.findParent('td', false, true);
6739         }
6740         
6741         if(!cell || typeof(cell) == 'undefined'){
6742             return;
6743         }
6744         
6745         var row = cell.findParent('tr', false, true);
6746         
6747         if(!row || typeof(row) == 'undefined'){
6748             return;
6749         }
6750         
6751         var cellIndex = cell.dom.cellIndex;
6752         var rowIndex = this.getRowIndex(row);
6753         
6754         if(this.cellSelection){
6755             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6756         }
6757         
6758         if(this.rowSelection){
6759             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6760         }
6761     },
6762     
6763     sort : function(e,el)
6764     {
6765         var col = Roo.get(el);
6766         
6767         if(!col.hasClass('sortable')){
6768             return;
6769         }
6770         
6771         var sort = col.attr('sort');
6772         var dir = 'ASC';
6773         
6774         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6775             dir = 'DESC';
6776         }
6777         
6778         this.store.sortInfo = {field : sort, direction : dir};
6779         
6780         if (this.footer) {
6781             Roo.log("calling footer first");
6782             this.footer.onClick('first');
6783         } else {
6784         
6785             this.store.load({ params : { start : 0 } });
6786         }
6787     },
6788     
6789     renderHeader : function()
6790     {
6791         var header = {
6792             tag: 'thead',
6793             cn : []
6794         };
6795         
6796         var cm = this.cm;
6797         this.totalWidth = 0;
6798         
6799         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6800             
6801             var config = cm.config[i];
6802             
6803             var c = {
6804                 tag: 'th',
6805                 cls : 'x-hcol-' + i,
6806                 style : '',
6807                 html: cm.getColumnHeader(i)
6808             };
6809             
6810             var hh = '';
6811             
6812             if(typeof(config.sortable) != 'undefined' && config.sortable){
6813                 c.cls = 'sortable';
6814                 c.html = '<i class="glyphicon"></i>' + c.html;
6815             }
6816             
6817             // could use BS4 hidden-..-down 
6818             
6819             if(typeof(config.lgHeader) != 'undefined'){
6820                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
6821             }
6822             
6823             if(typeof(config.mdHeader) != 'undefined'){
6824                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6825             }
6826             
6827             if(typeof(config.smHeader) != 'undefined'){
6828                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6829             }
6830             
6831             if(typeof(config.xsHeader) != 'undefined'){
6832                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6833             }
6834             
6835             if(hh.length){
6836                 c.html = hh;
6837             }
6838             
6839             if(typeof(config.tooltip) != 'undefined'){
6840                 c.tooltip = config.tooltip;
6841             }
6842             
6843             if(typeof(config.colspan) != 'undefined'){
6844                 c.colspan = config.colspan;
6845             }
6846             
6847             if(typeof(config.hidden) != 'undefined' && config.hidden){
6848                 c.style += ' display:none;';
6849             }
6850             
6851             if(typeof(config.dataIndex) != 'undefined'){
6852                 c.sort = config.dataIndex;
6853             }
6854             
6855            
6856             
6857             if(typeof(config.align) != 'undefined' && config.align.length){
6858                 c.style += ' text-align:' + config.align + ';';
6859             }
6860             
6861             if(typeof(config.width) != 'undefined'){
6862                 c.style += ' width:' + config.width + 'px;';
6863                 this.totalWidth += config.width;
6864             } else {
6865                 this.totalWidth += 100; // assume minimum of 100 per column?
6866             }
6867             
6868             if(typeof(config.cls) != 'undefined'){
6869                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6870             }
6871             
6872             ['xs','sm','md','lg'].map(function(size){
6873                 
6874                 if(typeof(config[size]) == 'undefined'){
6875                     return;
6876                 }
6877                  
6878                 if (!config[size]) { // 0 = hidden
6879                     // BS 4 '0' is treated as hide that column and below.
6880                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
6881                     return;
6882                 }
6883                 
6884                 c.cls += ' col-' + size + '-' + config[size] + (
6885                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
6886                 );
6887                 
6888                 
6889             });
6890             
6891             header.cn.push(c)
6892         }
6893         
6894         return header;
6895     },
6896     
6897     renderBody : function()
6898     {
6899         var body = {
6900             tag: 'tbody',
6901             cn : [
6902                 {
6903                     tag: 'tr',
6904                     cn : [
6905                         {
6906                             tag : 'td',
6907                             colspan :  this.cm.getColumnCount()
6908                         }
6909                     ]
6910                 }
6911             ]
6912         };
6913         
6914         return body;
6915     },
6916     
6917     renderFooter : function()
6918     {
6919         var footer = {
6920             tag: 'tfoot',
6921             cn : [
6922                 {
6923                     tag: 'tr',
6924                     cn : [
6925                         {
6926                             tag : 'td',
6927                             colspan :  this.cm.getColumnCount()
6928                         }
6929                     ]
6930                 }
6931             ]
6932         };
6933         
6934         return footer;
6935     },
6936     
6937     
6938     
6939     onLoad : function()
6940     {
6941 //        Roo.log('ds onload');
6942         this.clear();
6943         
6944         var _this = this;
6945         var cm = this.cm;
6946         var ds = this.store;
6947         
6948         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6949             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6950             if (_this.store.sortInfo) {
6951                     
6952                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6953                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6954                 }
6955                 
6956                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6957                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6958                 }
6959             }
6960         });
6961         
6962         var tbody =  this.mainBody;
6963               
6964         if(ds.getCount() > 0){
6965             ds.data.each(function(d,rowIndex){
6966                 var row =  this.renderRow(cm, ds, rowIndex);
6967                 
6968                 tbody.createChild(row);
6969                 
6970                 var _this = this;
6971                 
6972                 if(row.cellObjects.length){
6973                     Roo.each(row.cellObjects, function(r){
6974                         _this.renderCellObject(r);
6975                     })
6976                 }
6977                 
6978             }, this);
6979         }
6980         
6981         var tfoot = this.el.select('tfoot', true).first();
6982         
6983         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6984             
6985             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6986             
6987             var total = this.ds.getTotalCount();
6988             
6989             if(this.footer.pageSize < total){
6990                 this.mainFoot.show();
6991             }
6992         }
6993         
6994         Roo.each(this.el.select('tbody td', true).elements, function(e){
6995             e.on('mouseover', _this.onMouseover, _this);
6996         });
6997         
6998         Roo.each(this.el.select('tbody td', true).elements, function(e){
6999             e.on('mouseout', _this.onMouseout, _this);
7000         });
7001         this.fireEvent('rowsrendered', this);
7002         
7003         this.autoSize();
7004     },
7005     
7006     
7007     onUpdate : function(ds,record)
7008     {
7009         this.refreshRow(record);
7010         this.autoSize();
7011     },
7012     
7013     onRemove : function(ds, record, index, isUpdate){
7014         if(isUpdate !== true){
7015             this.fireEvent("beforerowremoved", this, index, record);
7016         }
7017         var bt = this.mainBody.dom;
7018         
7019         var rows = this.el.select('tbody > tr', true).elements;
7020         
7021         if(typeof(rows[index]) != 'undefined'){
7022             bt.removeChild(rows[index].dom);
7023         }
7024         
7025 //        if(bt.rows[index]){
7026 //            bt.removeChild(bt.rows[index]);
7027 //        }
7028         
7029         if(isUpdate !== true){
7030             //this.stripeRows(index);
7031             //this.syncRowHeights(index, index);
7032             //this.layout();
7033             this.fireEvent("rowremoved", this, index, record);
7034         }
7035     },
7036     
7037     onAdd : function(ds, records, rowIndex)
7038     {
7039         //Roo.log('on Add called');
7040         // - note this does not handle multiple adding very well..
7041         var bt = this.mainBody.dom;
7042         for (var i =0 ; i < records.length;i++) {
7043             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
7044             //Roo.log(records[i]);
7045             //Roo.log(this.store.getAt(rowIndex+i));
7046             this.insertRow(this.store, rowIndex + i, false);
7047             return;
7048         }
7049         
7050     },
7051     
7052     
7053     refreshRow : function(record){
7054         var ds = this.store, index;
7055         if(typeof record == 'number'){
7056             index = record;
7057             record = ds.getAt(index);
7058         }else{
7059             index = ds.indexOf(record);
7060         }
7061         this.insertRow(ds, index, true);
7062         this.autoSize();
7063         this.onRemove(ds, record, index+1, true);
7064         this.autoSize();
7065         //this.syncRowHeights(index, index);
7066         //this.layout();
7067         this.fireEvent("rowupdated", this, index, record);
7068     },
7069     
7070     insertRow : function(dm, rowIndex, isUpdate){
7071         
7072         if(!isUpdate){
7073             this.fireEvent("beforerowsinserted", this, rowIndex);
7074         }
7075             //var s = this.getScrollState();
7076         var row = this.renderRow(this.cm, this.store, rowIndex);
7077         // insert before rowIndex..
7078         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
7079         
7080         var _this = this;
7081                 
7082         if(row.cellObjects.length){
7083             Roo.each(row.cellObjects, function(r){
7084                 _this.renderCellObject(r);
7085             })
7086         }
7087             
7088         if(!isUpdate){
7089             this.fireEvent("rowsinserted", this, rowIndex);
7090             //this.syncRowHeights(firstRow, lastRow);
7091             //this.stripeRows(firstRow);
7092             //this.layout();
7093         }
7094         
7095     },
7096     
7097     
7098     getRowDom : function(rowIndex)
7099     {
7100         var rows = this.el.select('tbody > tr', true).elements;
7101         
7102         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
7103         
7104     },
7105     // returns the object tree for a tr..
7106   
7107     
7108     renderRow : function(cm, ds, rowIndex) 
7109     {
7110         var d = ds.getAt(rowIndex);
7111         
7112         var row = {
7113             tag : 'tr',
7114             cls : 'x-row-' + rowIndex,
7115             cn : []
7116         };
7117             
7118         var cellObjects = [];
7119         
7120         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7121             var config = cm.config[i];
7122             
7123             var renderer = cm.getRenderer(i);
7124             var value = '';
7125             var id = false;
7126             
7127             if(typeof(renderer) !== 'undefined'){
7128                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
7129             }
7130             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
7131             // and are rendered into the cells after the row is rendered - using the id for the element.
7132             
7133             if(typeof(value) === 'object'){
7134                 id = Roo.id();
7135                 cellObjects.push({
7136                     container : id,
7137                     cfg : value 
7138                 })
7139             }
7140             
7141             var rowcfg = {
7142                 record: d,
7143                 rowIndex : rowIndex,
7144                 colIndex : i,
7145                 rowClass : ''
7146             };
7147
7148             this.fireEvent('rowclass', this, rowcfg);
7149             
7150             var td = {
7151                 tag: 'td',
7152                 cls : rowcfg.rowClass + ' x-col-' + i,
7153                 style: '',
7154                 html: (typeof(value) === 'object') ? '' : value
7155             };
7156             
7157             if (id) {
7158                 td.id = id;
7159             }
7160             
7161             if(typeof(config.colspan) != 'undefined'){
7162                 td.colspan = config.colspan;
7163             }
7164             
7165             if(typeof(config.hidden) != 'undefined' && config.hidden){
7166                 td.style += ' display:none;';
7167             }
7168             
7169             if(typeof(config.align) != 'undefined' && config.align.length){
7170                 td.style += ' text-align:' + config.align + ';';
7171             }
7172             if(typeof(config.valign) != 'undefined' && config.valign.length){
7173                 td.style += ' vertical-align:' + config.valign + ';';
7174             }
7175             
7176             if(typeof(config.width) != 'undefined'){
7177                 td.style += ' width:' +  config.width + 'px;';
7178             }
7179             
7180             if(typeof(config.cursor) != 'undefined'){
7181                 td.style += ' cursor:' +  config.cursor + ';';
7182             }
7183             
7184             if(typeof(config.cls) != 'undefined'){
7185                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7186             }
7187             
7188             ['xs','sm','md','lg'].map(function(size){
7189                 
7190                 if(typeof(config[size]) == 'undefined'){
7191                     return;
7192                 }
7193                 
7194                 
7195                   
7196                 if (!config[size]) { // 0 = hidden
7197                     // BS 4 '0' is treated as hide that column and below.
7198                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
7199                     return;
7200                 }
7201                 
7202                 td.cls += ' col-' + size + '-' + config[size] + (
7203                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
7204                 );
7205                  
7206
7207             });
7208             
7209             row.cn.push(td);
7210            
7211         }
7212         
7213         row.cellObjects = cellObjects;
7214         
7215         return row;
7216           
7217     },
7218     
7219     
7220     
7221     onBeforeLoad : function()
7222     {
7223         
7224     },
7225      /**
7226      * Remove all rows
7227      */
7228     clear : function()
7229     {
7230         this.el.select('tbody', true).first().dom.innerHTML = '';
7231     },
7232     /**
7233      * Show or hide a row.
7234      * @param {Number} rowIndex to show or hide
7235      * @param {Boolean} state hide
7236      */
7237     setRowVisibility : function(rowIndex, state)
7238     {
7239         var bt = this.mainBody.dom;
7240         
7241         var rows = this.el.select('tbody > tr', true).elements;
7242         
7243         if(typeof(rows[rowIndex]) == 'undefined'){
7244             return;
7245         }
7246         rows[rowIndex].dom.style.display = state ? '' : 'none';
7247     },
7248     
7249     
7250     getSelectionModel : function(){
7251         if(!this.selModel){
7252             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7253         }
7254         return this.selModel;
7255     },
7256     /*
7257      * Render the Roo.bootstrap object from renderder
7258      */
7259     renderCellObject : function(r)
7260     {
7261         var _this = this;
7262         
7263         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7264         
7265         var t = r.cfg.render(r.container);
7266         
7267         if(r.cfg.cn){
7268             Roo.each(r.cfg.cn, function(c){
7269                 var child = {
7270                     container: t.getChildContainer(),
7271                     cfg: c
7272                 };
7273                 _this.renderCellObject(child);
7274             })
7275         }
7276     },
7277     
7278     getRowIndex : function(row)
7279     {
7280         var rowIndex = -1;
7281         
7282         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7283             if(el != row){
7284                 return;
7285             }
7286             
7287             rowIndex = index;
7288         });
7289         
7290         return rowIndex;
7291     },
7292      /**
7293      * Returns the grid's underlying element = used by panel.Grid
7294      * @return {Element} The element
7295      */
7296     getGridEl : function(){
7297         return this.el;
7298     },
7299      /**
7300      * Forces a resize - used by panel.Grid
7301      * @return {Element} The element
7302      */
7303     autoSize : function()
7304     {
7305         //var ctr = Roo.get(this.container.dom.parentElement);
7306         var ctr = Roo.get(this.el.dom);
7307         
7308         var thd = this.getGridEl().select('thead',true).first();
7309         var tbd = this.getGridEl().select('tbody', true).first();
7310         var tfd = this.getGridEl().select('tfoot', true).first();
7311         
7312         var cw = ctr.getWidth();
7313         
7314         if (tbd) {
7315             
7316             tbd.setSize(ctr.getWidth(),
7317                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7318             );
7319             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7320             cw -= barsize;
7321         }
7322         cw = Math.max(cw, this.totalWidth);
7323         this.getGridEl().select('tr',true).setWidth(cw);
7324         // resize 'expandable coloumn?
7325         
7326         return; // we doe not have a view in this design..
7327         
7328     },
7329     onBodyScroll: function()
7330     {
7331         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7332         if(this.mainHead){
7333             this.mainHead.setStyle({
7334                 'position' : 'relative',
7335                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7336             });
7337         }
7338         
7339         if(this.lazyLoad){
7340             
7341             var scrollHeight = this.mainBody.dom.scrollHeight;
7342             
7343             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7344             
7345             var height = this.mainBody.getHeight();
7346             
7347             if(scrollHeight - height == scrollTop) {
7348                 
7349                 var total = this.ds.getTotalCount();
7350                 
7351                 if(this.footer.cursor + this.footer.pageSize < total){
7352                     
7353                     this.footer.ds.load({
7354                         params : {
7355                             start : this.footer.cursor + this.footer.pageSize,
7356                             limit : this.footer.pageSize
7357                         },
7358                         add : true
7359                     });
7360                 }
7361             }
7362             
7363         }
7364     },
7365     
7366     onHeaderChange : function()
7367     {
7368         var header = this.renderHeader();
7369         var table = this.el.select('table', true).first();
7370         
7371         this.mainHead.remove();
7372         this.mainHead = table.createChild(header, this.mainBody, false);
7373     },
7374     
7375     onHiddenChange : function(colModel, colIndex, hidden)
7376     {
7377         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7378         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7379         
7380         this.CSS.updateRule(thSelector, "display", "");
7381         this.CSS.updateRule(tdSelector, "display", "");
7382         
7383         if(hidden){
7384             this.CSS.updateRule(thSelector, "display", "none");
7385             this.CSS.updateRule(tdSelector, "display", "none");
7386         }
7387         
7388         this.onHeaderChange();
7389         this.onLoad();
7390     },
7391     
7392     setColumnWidth: function(col_index, width)
7393     {
7394         // width = "md-2 xs-2..."
7395         if(!this.colModel.config[col_index]) {
7396             return;
7397         }
7398         
7399         var w = width.split(" ");
7400         
7401         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7402         
7403         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7404         
7405         
7406         for(var j = 0; j < w.length; j++) {
7407             
7408             if(!w[j]) {
7409                 continue;
7410             }
7411             
7412             var size_cls = w[j].split("-");
7413             
7414             if(!Number.isInteger(size_cls[1] * 1)) {
7415                 continue;
7416             }
7417             
7418             if(!this.colModel.config[col_index][size_cls[0]]) {
7419                 continue;
7420             }
7421             
7422             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7423                 continue;
7424             }
7425             
7426             h_row[0].classList.replace(
7427                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7428                 "col-"+size_cls[0]+"-"+size_cls[1]
7429             );
7430             
7431             for(var i = 0; i < rows.length; i++) {
7432                 
7433                 var size_cls = w[j].split("-");
7434                 
7435                 if(!Number.isInteger(size_cls[1] * 1)) {
7436                     continue;
7437                 }
7438                 
7439                 if(!this.colModel.config[col_index][size_cls[0]]) {
7440                     continue;
7441                 }
7442                 
7443                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7444                     continue;
7445                 }
7446                 
7447                 rows[i].classList.replace(
7448                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7449                     "col-"+size_cls[0]+"-"+size_cls[1]
7450                 );
7451             }
7452             
7453             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7454         }
7455     }
7456 });
7457
7458  
7459
7460  /*
7461  * - LGPL
7462  *
7463  * table cell
7464  * 
7465  */
7466
7467 /**
7468  * @class Roo.bootstrap.TableCell
7469  * @extends Roo.bootstrap.Component
7470  * Bootstrap TableCell class
7471  * @cfg {String} html cell contain text
7472  * @cfg {String} cls cell class
7473  * @cfg {String} tag cell tag (td|th) default td
7474  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7475  * @cfg {String} align Aligns the content in a cell
7476  * @cfg {String} axis Categorizes cells
7477  * @cfg {String} bgcolor Specifies the background color of a cell
7478  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7479  * @cfg {Number} colspan Specifies the number of columns a cell should span
7480  * @cfg {String} headers Specifies one or more header cells a cell is related to
7481  * @cfg {Number} height Sets the height of a cell
7482  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7483  * @cfg {Number} rowspan Sets the number of rows a cell should span
7484  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7485  * @cfg {String} valign Vertical aligns the content in a cell
7486  * @cfg {Number} width Specifies the width of a cell
7487  * 
7488  * @constructor
7489  * Create a new TableCell
7490  * @param {Object} config The config object
7491  */
7492
7493 Roo.bootstrap.TableCell = function(config){
7494     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7495 };
7496
7497 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7498     
7499     html: false,
7500     cls: false,
7501     tag: false,
7502     abbr: false,
7503     align: false,
7504     axis: false,
7505     bgcolor: false,
7506     charoff: false,
7507     colspan: false,
7508     headers: false,
7509     height: false,
7510     nowrap: false,
7511     rowspan: false,
7512     scope: false,
7513     valign: false,
7514     width: false,
7515     
7516     
7517     getAutoCreate : function(){
7518         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7519         
7520         cfg = {
7521             tag: 'td'
7522         };
7523         
7524         if(this.tag){
7525             cfg.tag = this.tag;
7526         }
7527         
7528         if (this.html) {
7529             cfg.html=this.html
7530         }
7531         if (this.cls) {
7532             cfg.cls=this.cls
7533         }
7534         if (this.abbr) {
7535             cfg.abbr=this.abbr
7536         }
7537         if (this.align) {
7538             cfg.align=this.align
7539         }
7540         if (this.axis) {
7541             cfg.axis=this.axis
7542         }
7543         if (this.bgcolor) {
7544             cfg.bgcolor=this.bgcolor
7545         }
7546         if (this.charoff) {
7547             cfg.charoff=this.charoff
7548         }
7549         if (this.colspan) {
7550             cfg.colspan=this.colspan
7551         }
7552         if (this.headers) {
7553             cfg.headers=this.headers
7554         }
7555         if (this.height) {
7556             cfg.height=this.height
7557         }
7558         if (this.nowrap) {
7559             cfg.nowrap=this.nowrap
7560         }
7561         if (this.rowspan) {
7562             cfg.rowspan=this.rowspan
7563         }
7564         if (this.scope) {
7565             cfg.scope=this.scope
7566         }
7567         if (this.valign) {
7568             cfg.valign=this.valign
7569         }
7570         if (this.width) {
7571             cfg.width=this.width
7572         }
7573         
7574         
7575         return cfg;
7576     }
7577    
7578 });
7579
7580  
7581
7582  /*
7583  * - LGPL
7584  *
7585  * table row
7586  * 
7587  */
7588
7589 /**
7590  * @class Roo.bootstrap.TableRow
7591  * @extends Roo.bootstrap.Component
7592  * Bootstrap TableRow class
7593  * @cfg {String} cls row class
7594  * @cfg {String} align Aligns the content in a table row
7595  * @cfg {String} bgcolor Specifies a background color for a table row
7596  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7597  * @cfg {String} valign Vertical aligns the content in a table row
7598  * 
7599  * @constructor
7600  * Create a new TableRow
7601  * @param {Object} config The config object
7602  */
7603
7604 Roo.bootstrap.TableRow = function(config){
7605     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7606 };
7607
7608 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7609     
7610     cls: false,
7611     align: false,
7612     bgcolor: false,
7613     charoff: false,
7614     valign: false,
7615     
7616     getAutoCreate : function(){
7617         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7618         
7619         cfg = {
7620             tag: 'tr'
7621         };
7622             
7623         if(this.cls){
7624             cfg.cls = this.cls;
7625         }
7626         if(this.align){
7627             cfg.align = this.align;
7628         }
7629         if(this.bgcolor){
7630             cfg.bgcolor = this.bgcolor;
7631         }
7632         if(this.charoff){
7633             cfg.charoff = this.charoff;
7634         }
7635         if(this.valign){
7636             cfg.valign = this.valign;
7637         }
7638         
7639         return cfg;
7640     }
7641    
7642 });
7643
7644  
7645
7646  /*
7647  * - LGPL
7648  *
7649  * table body
7650  * 
7651  */
7652
7653 /**
7654  * @class Roo.bootstrap.TableBody
7655  * @extends Roo.bootstrap.Component
7656  * Bootstrap TableBody class
7657  * @cfg {String} cls element class
7658  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7659  * @cfg {String} align Aligns the content inside the element
7660  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7661  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7662  * 
7663  * @constructor
7664  * Create a new TableBody
7665  * @param {Object} config The config object
7666  */
7667
7668 Roo.bootstrap.TableBody = function(config){
7669     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7670 };
7671
7672 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7673     
7674     cls: false,
7675     tag: false,
7676     align: false,
7677     charoff: false,
7678     valign: false,
7679     
7680     getAutoCreate : function(){
7681         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7682         
7683         cfg = {
7684             tag: 'tbody'
7685         };
7686             
7687         if (this.cls) {
7688             cfg.cls=this.cls
7689         }
7690         if(this.tag){
7691             cfg.tag = this.tag;
7692         }
7693         
7694         if(this.align){
7695             cfg.align = this.align;
7696         }
7697         if(this.charoff){
7698             cfg.charoff = this.charoff;
7699         }
7700         if(this.valign){
7701             cfg.valign = this.valign;
7702         }
7703         
7704         return cfg;
7705     }
7706     
7707     
7708 //    initEvents : function()
7709 //    {
7710 //        
7711 //        if(!this.store){
7712 //            return;
7713 //        }
7714 //        
7715 //        this.store = Roo.factory(this.store, Roo.data);
7716 //        this.store.on('load', this.onLoad, this);
7717 //        
7718 //        this.store.load();
7719 //        
7720 //    },
7721 //    
7722 //    onLoad: function () 
7723 //    {   
7724 //        this.fireEvent('load', this);
7725 //    }
7726 //    
7727 //   
7728 });
7729
7730  
7731
7732  /*
7733  * Based on:
7734  * Ext JS Library 1.1.1
7735  * Copyright(c) 2006-2007, Ext JS, LLC.
7736  *
7737  * Originally Released Under LGPL - original licence link has changed is not relivant.
7738  *
7739  * Fork - LGPL
7740  * <script type="text/javascript">
7741  */
7742
7743 // as we use this in bootstrap.
7744 Roo.namespace('Roo.form');
7745  /**
7746  * @class Roo.form.Action
7747  * Internal Class used to handle form actions
7748  * @constructor
7749  * @param {Roo.form.BasicForm} el The form element or its id
7750  * @param {Object} config Configuration options
7751  */
7752
7753  
7754  
7755 // define the action interface
7756 Roo.form.Action = function(form, options){
7757     this.form = form;
7758     this.options = options || {};
7759 };
7760 /**
7761  * Client Validation Failed
7762  * @const 
7763  */
7764 Roo.form.Action.CLIENT_INVALID = 'client';
7765 /**
7766  * Server Validation Failed
7767  * @const 
7768  */
7769 Roo.form.Action.SERVER_INVALID = 'server';
7770  /**
7771  * Connect to Server Failed
7772  * @const 
7773  */
7774 Roo.form.Action.CONNECT_FAILURE = 'connect';
7775 /**
7776  * Reading Data from Server Failed
7777  * @const 
7778  */
7779 Roo.form.Action.LOAD_FAILURE = 'load';
7780
7781 Roo.form.Action.prototype = {
7782     type : 'default',
7783     failureType : undefined,
7784     response : undefined,
7785     result : undefined,
7786
7787     // interface method
7788     run : function(options){
7789
7790     },
7791
7792     // interface method
7793     success : function(response){
7794
7795     },
7796
7797     // interface method
7798     handleResponse : function(response){
7799
7800     },
7801
7802     // default connection failure
7803     failure : function(response){
7804         
7805         this.response = response;
7806         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7807         this.form.afterAction(this, false);
7808     },
7809
7810     processResponse : function(response){
7811         this.response = response;
7812         if(!response.responseText){
7813             return true;
7814         }
7815         this.result = this.handleResponse(response);
7816         return this.result;
7817     },
7818
7819     // utility functions used internally
7820     getUrl : function(appendParams){
7821         var url = this.options.url || this.form.url || this.form.el.dom.action;
7822         if(appendParams){
7823             var p = this.getParams();
7824             if(p){
7825                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7826             }
7827         }
7828         return url;
7829     },
7830
7831     getMethod : function(){
7832         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7833     },
7834
7835     getParams : function(){
7836         var bp = this.form.baseParams;
7837         var p = this.options.params;
7838         if(p){
7839             if(typeof p == "object"){
7840                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7841             }else if(typeof p == 'string' && bp){
7842                 p += '&' + Roo.urlEncode(bp);
7843             }
7844         }else if(bp){
7845             p = Roo.urlEncode(bp);
7846         }
7847         return p;
7848     },
7849
7850     createCallback : function(){
7851         return {
7852             success: this.success,
7853             failure: this.failure,
7854             scope: this,
7855             timeout: (this.form.timeout*1000),
7856             upload: this.form.fileUpload ? this.success : undefined
7857         };
7858     }
7859 };
7860
7861 Roo.form.Action.Submit = function(form, options){
7862     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7863 };
7864
7865 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7866     type : 'submit',
7867
7868     haveProgress : false,
7869     uploadComplete : false,
7870     
7871     // uploadProgress indicator.
7872     uploadProgress : function()
7873     {
7874         if (!this.form.progressUrl) {
7875             return;
7876         }
7877         
7878         if (!this.haveProgress) {
7879             Roo.MessageBox.progress("Uploading", "Uploading");
7880         }
7881         if (this.uploadComplete) {
7882            Roo.MessageBox.hide();
7883            return;
7884         }
7885         
7886         this.haveProgress = true;
7887    
7888         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7889         
7890         var c = new Roo.data.Connection();
7891         c.request({
7892             url : this.form.progressUrl,
7893             params: {
7894                 id : uid
7895             },
7896             method: 'GET',
7897             success : function(req){
7898                //console.log(data);
7899                 var rdata = false;
7900                 var edata;
7901                 try  {
7902                    rdata = Roo.decode(req.responseText)
7903                 } catch (e) {
7904                     Roo.log("Invalid data from server..");
7905                     Roo.log(edata);
7906                     return;
7907                 }
7908                 if (!rdata || !rdata.success) {
7909                     Roo.log(rdata);
7910                     Roo.MessageBox.alert(Roo.encode(rdata));
7911                     return;
7912                 }
7913                 var data = rdata.data;
7914                 
7915                 if (this.uploadComplete) {
7916                    Roo.MessageBox.hide();
7917                    return;
7918                 }
7919                    
7920                 if (data){
7921                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7922                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7923                     );
7924                 }
7925                 this.uploadProgress.defer(2000,this);
7926             },
7927        
7928             failure: function(data) {
7929                 Roo.log('progress url failed ');
7930                 Roo.log(data);
7931             },
7932             scope : this
7933         });
7934            
7935     },
7936     
7937     
7938     run : function()
7939     {
7940         // run get Values on the form, so it syncs any secondary forms.
7941         this.form.getValues();
7942         
7943         var o = this.options;
7944         var method = this.getMethod();
7945         var isPost = method == 'POST';
7946         if(o.clientValidation === false || this.form.isValid()){
7947             
7948             if (this.form.progressUrl) {
7949                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7950                     (new Date() * 1) + '' + Math.random());
7951                     
7952             } 
7953             
7954             
7955             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7956                 form:this.form.el.dom,
7957                 url:this.getUrl(!isPost),
7958                 method: method,
7959                 params:isPost ? this.getParams() : null,
7960                 isUpload: this.form.fileUpload,
7961                 formData : this.form.formData
7962             }));
7963             
7964             this.uploadProgress();
7965
7966         }else if (o.clientValidation !== false){ // client validation failed
7967             this.failureType = Roo.form.Action.CLIENT_INVALID;
7968             this.form.afterAction(this, false);
7969         }
7970     },
7971
7972     success : function(response)
7973     {
7974         this.uploadComplete= true;
7975         if (this.haveProgress) {
7976             Roo.MessageBox.hide();
7977         }
7978         
7979         
7980         var result = this.processResponse(response);
7981         if(result === true || result.success){
7982             this.form.afterAction(this, true);
7983             return;
7984         }
7985         if(result.errors){
7986             this.form.markInvalid(result.errors);
7987             this.failureType = Roo.form.Action.SERVER_INVALID;
7988         }
7989         this.form.afterAction(this, false);
7990     },
7991     failure : function(response)
7992     {
7993         this.uploadComplete= true;
7994         if (this.haveProgress) {
7995             Roo.MessageBox.hide();
7996         }
7997         
7998         this.response = response;
7999         this.failureType = Roo.form.Action.CONNECT_FAILURE;
8000         this.form.afterAction(this, false);
8001     },
8002     
8003     handleResponse : function(response){
8004         if(this.form.errorReader){
8005             var rs = this.form.errorReader.read(response);
8006             var errors = [];
8007             if(rs.records){
8008                 for(var i = 0, len = rs.records.length; i < len; i++) {
8009                     var r = rs.records[i];
8010                     errors[i] = r.data;
8011                 }
8012             }
8013             if(errors.length < 1){
8014                 errors = null;
8015             }
8016             return {
8017                 success : rs.success,
8018                 errors : errors
8019             };
8020         }
8021         var ret = false;
8022         try {
8023             ret = Roo.decode(response.responseText);
8024         } catch (e) {
8025             ret = {
8026                 success: false,
8027                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
8028                 errors : []
8029             };
8030         }
8031         return ret;
8032         
8033     }
8034 });
8035
8036
8037 Roo.form.Action.Load = function(form, options){
8038     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
8039     this.reader = this.form.reader;
8040 };
8041
8042 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
8043     type : 'load',
8044
8045     run : function(){
8046         
8047         Roo.Ajax.request(Roo.apply(
8048                 this.createCallback(), {
8049                     method:this.getMethod(),
8050                     url:this.getUrl(false),
8051                     params:this.getParams()
8052         }));
8053     },
8054
8055     success : function(response){
8056         
8057         var result = this.processResponse(response);
8058         if(result === true || !result.success || !result.data){
8059             this.failureType = Roo.form.Action.LOAD_FAILURE;
8060             this.form.afterAction(this, false);
8061             return;
8062         }
8063         this.form.clearInvalid();
8064         this.form.setValues(result.data);
8065         this.form.afterAction(this, true);
8066     },
8067
8068     handleResponse : function(response){
8069         if(this.form.reader){
8070             var rs = this.form.reader.read(response);
8071             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
8072             return {
8073                 success : rs.success,
8074                 data : data
8075             };
8076         }
8077         return Roo.decode(response.responseText);
8078     }
8079 });
8080
8081 Roo.form.Action.ACTION_TYPES = {
8082     'load' : Roo.form.Action.Load,
8083     'submit' : Roo.form.Action.Submit
8084 };/*
8085  * - LGPL
8086  *
8087  * form
8088  *
8089  */
8090
8091 /**
8092  * @class Roo.bootstrap.Form
8093  * @extends Roo.bootstrap.Component
8094  * Bootstrap Form class
8095  * @cfg {String} method  GET | POST (default POST)
8096  * @cfg {String} labelAlign top | left (default top)
8097  * @cfg {String} align left  | right - for navbars
8098  * @cfg {Boolean} loadMask load mask when submit (default true)
8099
8100  *
8101  * @constructor
8102  * Create a new Form
8103  * @param {Object} config The config object
8104  */
8105
8106
8107 Roo.bootstrap.Form = function(config){
8108     
8109     Roo.bootstrap.Form.superclass.constructor.call(this, config);
8110     
8111     Roo.bootstrap.Form.popover.apply();
8112     
8113     this.addEvents({
8114         /**
8115          * @event clientvalidation
8116          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
8117          * @param {Form} this
8118          * @param {Boolean} valid true if the form has passed client-side validation
8119          */
8120         clientvalidation: true,
8121         /**
8122          * @event beforeaction
8123          * Fires before any action is performed. Return false to cancel the action.
8124          * @param {Form} this
8125          * @param {Action} action The action to be performed
8126          */
8127         beforeaction: true,
8128         /**
8129          * @event actionfailed
8130          * Fires when an action fails.
8131          * @param {Form} this
8132          * @param {Action} action The action that failed
8133          */
8134         actionfailed : true,
8135         /**
8136          * @event actioncomplete
8137          * Fires when an action is completed.
8138          * @param {Form} this
8139          * @param {Action} action The action that completed
8140          */
8141         actioncomplete : true
8142     });
8143 };
8144
8145 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
8146
8147      /**
8148      * @cfg {String} method
8149      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8150      */
8151     method : 'POST',
8152     /**
8153      * @cfg {String} url
8154      * The URL to use for form actions if one isn't supplied in the action options.
8155      */
8156     /**
8157      * @cfg {Boolean} fileUpload
8158      * Set to true if this form is a file upload.
8159      */
8160
8161     /**
8162      * @cfg {Object} baseParams
8163      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8164      */
8165
8166     /**
8167      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8168      */
8169     timeout: 30,
8170     /**
8171      * @cfg {Sting} align (left|right) for navbar forms
8172      */
8173     align : 'left',
8174
8175     // private
8176     activeAction : null,
8177
8178     /**
8179      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8180      * element by passing it or its id or mask the form itself by passing in true.
8181      * @type Mixed
8182      */
8183     waitMsgTarget : false,
8184
8185     loadMask : true,
8186     
8187     /**
8188      * @cfg {Boolean} errorMask (true|false) default false
8189      */
8190     errorMask : false,
8191     
8192     /**
8193      * @cfg {Number} maskOffset Default 100
8194      */
8195     maskOffset : 100,
8196     
8197     /**
8198      * @cfg {Boolean} maskBody
8199      */
8200     maskBody : false,
8201
8202     getAutoCreate : function(){
8203
8204         var cfg = {
8205             tag: 'form',
8206             method : this.method || 'POST',
8207             id : this.id || Roo.id(),
8208             cls : ''
8209         };
8210         if (this.parent().xtype.match(/^Nav/)) {
8211             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8212
8213         }
8214
8215         if (this.labelAlign == 'left' ) {
8216             cfg.cls += ' form-horizontal';
8217         }
8218
8219
8220         return cfg;
8221     },
8222     initEvents : function()
8223     {
8224         this.el.on('submit', this.onSubmit, this);
8225         // this was added as random key presses on the form where triggering form submit.
8226         this.el.on('keypress', function(e) {
8227             if (e.getCharCode() != 13) {
8228                 return true;
8229             }
8230             // we might need to allow it for textareas.. and some other items.
8231             // check e.getTarget().
8232
8233             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8234                 return true;
8235             }
8236
8237             Roo.log("keypress blocked");
8238
8239             e.preventDefault();
8240             return false;
8241         });
8242         
8243     },
8244     // private
8245     onSubmit : function(e){
8246         e.stopEvent();
8247     },
8248
8249      /**
8250      * Returns true if client-side validation on the form is successful.
8251      * @return Boolean
8252      */
8253     isValid : function(){
8254         var items = this.getItems();
8255         var valid = true;
8256         var target = false;
8257         
8258         items.each(function(f){
8259             
8260             if(f.validate()){
8261                 return;
8262             }
8263             
8264             Roo.log('invalid field: ' + f.name);
8265             
8266             valid = false;
8267
8268             if(!target && f.el.isVisible(true)){
8269                 target = f;
8270             }
8271            
8272         });
8273         
8274         if(this.errorMask && !valid){
8275             Roo.bootstrap.Form.popover.mask(this, target);
8276         }
8277         
8278         return valid;
8279     },
8280     
8281     /**
8282      * Returns true if any fields in this form have changed since their original load.
8283      * @return Boolean
8284      */
8285     isDirty : function(){
8286         var dirty = false;
8287         var items = this.getItems();
8288         items.each(function(f){
8289            if(f.isDirty()){
8290                dirty = true;
8291                return false;
8292            }
8293            return true;
8294         });
8295         return dirty;
8296     },
8297      /**
8298      * Performs a predefined action (submit or load) or custom actions you define on this form.
8299      * @param {String} actionName The name of the action type
8300      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8301      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8302      * accept other config options):
8303      * <pre>
8304 Property          Type             Description
8305 ----------------  ---------------  ----------------------------------------------------------------------------------
8306 url               String           The url for the action (defaults to the form's url)
8307 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8308 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8309 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8310                                    validate the form on the client (defaults to false)
8311      * </pre>
8312      * @return {BasicForm} this
8313      */
8314     doAction : function(action, options){
8315         if(typeof action == 'string'){
8316             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8317         }
8318         if(this.fireEvent('beforeaction', this, action) !== false){
8319             this.beforeAction(action);
8320             action.run.defer(100, action);
8321         }
8322         return this;
8323     },
8324
8325     // private
8326     beforeAction : function(action){
8327         var o = action.options;
8328         
8329         if(this.loadMask){
8330             
8331             if(this.maskBody){
8332                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8333             } else {
8334                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8335             }
8336         }
8337         // not really supported yet.. ??
8338
8339         //if(this.waitMsgTarget === true){
8340         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8341         //}else if(this.waitMsgTarget){
8342         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8343         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8344         //}else {
8345         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8346        // }
8347
8348     },
8349
8350     // private
8351     afterAction : function(action, success){
8352         this.activeAction = null;
8353         var o = action.options;
8354
8355         if(this.loadMask){
8356             
8357             if(this.maskBody){
8358                 Roo.get(document.body).unmask();
8359             } else {
8360                 this.el.unmask();
8361             }
8362         }
8363         
8364         //if(this.waitMsgTarget === true){
8365 //            this.el.unmask();
8366         //}else if(this.waitMsgTarget){
8367         //    this.waitMsgTarget.unmask();
8368         //}else{
8369         //    Roo.MessageBox.updateProgress(1);
8370         //    Roo.MessageBox.hide();
8371        // }
8372         //
8373         if(success){
8374             if(o.reset){
8375                 this.reset();
8376             }
8377             Roo.callback(o.success, o.scope, [this, action]);
8378             this.fireEvent('actioncomplete', this, action);
8379
8380         }else{
8381
8382             // failure condition..
8383             // we have a scenario where updates need confirming.
8384             // eg. if a locking scenario exists..
8385             // we look for { errors : { needs_confirm : true }} in the response.
8386             if (
8387                 (typeof(action.result) != 'undefined')  &&
8388                 (typeof(action.result.errors) != 'undefined')  &&
8389                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8390            ){
8391                 var _t = this;
8392                 Roo.log("not supported yet");
8393                  /*
8394
8395                 Roo.MessageBox.confirm(
8396                     "Change requires confirmation",
8397                     action.result.errorMsg,
8398                     function(r) {
8399                         if (r != 'yes') {
8400                             return;
8401                         }
8402                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8403                     }
8404
8405                 );
8406                 */
8407
8408
8409                 return;
8410             }
8411
8412             Roo.callback(o.failure, o.scope, [this, action]);
8413             // show an error message if no failed handler is set..
8414             if (!this.hasListener('actionfailed')) {
8415                 Roo.log("need to add dialog support");
8416                 /*
8417                 Roo.MessageBox.alert("Error",
8418                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8419                         action.result.errorMsg :
8420                         "Saving Failed, please check your entries or try again"
8421                 );
8422                 */
8423             }
8424
8425             this.fireEvent('actionfailed', this, action);
8426         }
8427
8428     },
8429     /**
8430      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8431      * @param {String} id The value to search for
8432      * @return Field
8433      */
8434     findField : function(id){
8435         var items = this.getItems();
8436         var field = items.get(id);
8437         if(!field){
8438              items.each(function(f){
8439                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8440                     field = f;
8441                     return false;
8442                 }
8443                 return true;
8444             });
8445         }
8446         return field || null;
8447     },
8448      /**
8449      * Mark fields in this form invalid in bulk.
8450      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8451      * @return {BasicForm} this
8452      */
8453     markInvalid : function(errors){
8454         if(errors instanceof Array){
8455             for(var i = 0, len = errors.length; i < len; i++){
8456                 var fieldError = errors[i];
8457                 var f = this.findField(fieldError.id);
8458                 if(f){
8459                     f.markInvalid(fieldError.msg);
8460                 }
8461             }
8462         }else{
8463             var field, id;
8464             for(id in errors){
8465                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8466                     field.markInvalid(errors[id]);
8467                 }
8468             }
8469         }
8470         //Roo.each(this.childForms || [], function (f) {
8471         //    f.markInvalid(errors);
8472         //});
8473
8474         return this;
8475     },
8476
8477     /**
8478      * Set values for fields in this form in bulk.
8479      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8480      * @return {BasicForm} this
8481      */
8482     setValues : function(values){
8483         if(values instanceof Array){ // array of objects
8484             for(var i = 0, len = values.length; i < len; i++){
8485                 var v = values[i];
8486                 var f = this.findField(v.id);
8487                 if(f){
8488                     f.setValue(v.value);
8489                     if(this.trackResetOnLoad){
8490                         f.originalValue = f.getValue();
8491                     }
8492                 }
8493             }
8494         }else{ // object hash
8495             var field, id;
8496             for(id in values){
8497                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8498
8499                     if (field.setFromData &&
8500                         field.valueField &&
8501                         field.displayField &&
8502                         // combos' with local stores can
8503                         // be queried via setValue()
8504                         // to set their value..
8505                         (field.store && !field.store.isLocal)
8506                         ) {
8507                         // it's a combo
8508                         var sd = { };
8509                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8510                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8511                         field.setFromData(sd);
8512
8513                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8514                         
8515                         field.setFromData(values);
8516                         
8517                     } else {
8518                         field.setValue(values[id]);
8519                     }
8520
8521
8522                     if(this.trackResetOnLoad){
8523                         field.originalValue = field.getValue();
8524                     }
8525                 }
8526             }
8527         }
8528
8529         //Roo.each(this.childForms || [], function (f) {
8530         //    f.setValues(values);
8531         //});
8532
8533         return this;
8534     },
8535
8536     /**
8537      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8538      * they are returned as an array.
8539      * @param {Boolean} asString
8540      * @return {Object}
8541      */
8542     getValues : function(asString){
8543         //if (this.childForms) {
8544             // copy values from the child forms
8545         //    Roo.each(this.childForms, function (f) {
8546         //        this.setValues(f.getValues());
8547         //    }, this);
8548         //}
8549
8550
8551
8552         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8553         if(asString === true){
8554             return fs;
8555         }
8556         return Roo.urlDecode(fs);
8557     },
8558
8559     /**
8560      * Returns the fields in this form as an object with key/value pairs.
8561      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8562      * @return {Object}
8563      */
8564     getFieldValues : function(with_hidden)
8565     {
8566         var items = this.getItems();
8567         var ret = {};
8568         items.each(function(f){
8569             
8570             if (!f.getName()) {
8571                 return;
8572             }
8573             
8574             var v = f.getValue();
8575             
8576             if (f.inputType =='radio') {
8577                 if (typeof(ret[f.getName()]) == 'undefined') {
8578                     ret[f.getName()] = ''; // empty..
8579                 }
8580
8581                 if (!f.el.dom.checked) {
8582                     return;
8583
8584                 }
8585                 v = f.el.dom.value;
8586
8587             }
8588             
8589             if(f.xtype == 'MoneyField'){
8590                 ret[f.currencyName] = f.getCurrency();
8591             }
8592
8593             // not sure if this supported any more..
8594             if ((typeof(v) == 'object') && f.getRawValue) {
8595                 v = f.getRawValue() ; // dates..
8596             }
8597             // combo boxes where name != hiddenName...
8598             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8599                 ret[f.name] = f.getRawValue();
8600             }
8601             ret[f.getName()] = v;
8602         });
8603
8604         return ret;
8605     },
8606
8607     /**
8608      * Clears all invalid messages in this form.
8609      * @return {BasicForm} this
8610      */
8611     clearInvalid : function(){
8612         var items = this.getItems();
8613
8614         items.each(function(f){
8615            f.clearInvalid();
8616         });
8617
8618         return this;
8619     },
8620
8621     /**
8622      * Resets this form.
8623      * @return {BasicForm} this
8624      */
8625     reset : function(){
8626         var items = this.getItems();
8627         items.each(function(f){
8628             f.reset();
8629         });
8630
8631         Roo.each(this.childForms || [], function (f) {
8632             f.reset();
8633         });
8634
8635
8636         return this;
8637     },
8638     
8639     getItems : function()
8640     {
8641         var r=new Roo.util.MixedCollection(false, function(o){
8642             return o.id || (o.id = Roo.id());
8643         });
8644         var iter = function(el) {
8645             if (el.inputEl) {
8646                 r.add(el);
8647             }
8648             if (!el.items) {
8649                 return;
8650             }
8651             Roo.each(el.items,function(e) {
8652                 iter(e);
8653             });
8654         };
8655
8656         iter(this);
8657         return r;
8658     },
8659     
8660     hideFields : function(items)
8661     {
8662         Roo.each(items, function(i){
8663             
8664             var f = this.findField(i);
8665             
8666             if(!f){
8667                 return;
8668             }
8669             
8670             f.hide();
8671             
8672         }, this);
8673     },
8674     
8675     showFields : function(items)
8676     {
8677         Roo.each(items, function(i){
8678             
8679             var f = this.findField(i);
8680             
8681             if(!f){
8682                 return;
8683             }
8684             
8685             f.show();
8686             
8687         }, this);
8688     }
8689
8690 });
8691
8692 Roo.apply(Roo.bootstrap.Form, {
8693     
8694     popover : {
8695         
8696         padding : 5,
8697         
8698         isApplied : false,
8699         
8700         isMasked : false,
8701         
8702         form : false,
8703         
8704         target : false,
8705         
8706         toolTip : false,
8707         
8708         intervalID : false,
8709         
8710         maskEl : false,
8711         
8712         apply : function()
8713         {
8714             if(this.isApplied){
8715                 return;
8716             }
8717             
8718             this.maskEl = {
8719                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8720                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8721                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8722                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8723             };
8724             
8725             this.maskEl.top.enableDisplayMode("block");
8726             this.maskEl.left.enableDisplayMode("block");
8727             this.maskEl.bottom.enableDisplayMode("block");
8728             this.maskEl.right.enableDisplayMode("block");
8729             
8730             this.toolTip = new Roo.bootstrap.Tooltip({
8731                 cls : 'roo-form-error-popover',
8732                 alignment : {
8733                     'left' : ['r-l', [-2,0], 'right'],
8734                     'right' : ['l-r', [2,0], 'left'],
8735                     'bottom' : ['tl-bl', [0,2], 'top'],
8736                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8737                 }
8738             });
8739             
8740             this.toolTip.render(Roo.get(document.body));
8741
8742             this.toolTip.el.enableDisplayMode("block");
8743             
8744             Roo.get(document.body).on('click', function(){
8745                 this.unmask();
8746             }, this);
8747             
8748             Roo.get(document.body).on('touchstart', function(){
8749                 this.unmask();
8750             }, this);
8751             
8752             this.isApplied = true
8753         },
8754         
8755         mask : function(form, target)
8756         {
8757             this.form = form;
8758             
8759             this.target = target;
8760             
8761             if(!this.form.errorMask || !target.el){
8762                 return;
8763             }
8764             
8765             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8766             
8767             Roo.log(scrollable);
8768             
8769             var ot = this.target.el.calcOffsetsTo(scrollable);
8770             
8771             var scrollTo = ot[1] - this.form.maskOffset;
8772             
8773             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8774             
8775             scrollable.scrollTo('top', scrollTo);
8776             
8777             var box = this.target.el.getBox();
8778             Roo.log(box);
8779             var zIndex = Roo.bootstrap.Modal.zIndex++;
8780
8781             
8782             this.maskEl.top.setStyle('position', 'absolute');
8783             this.maskEl.top.setStyle('z-index', zIndex);
8784             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8785             this.maskEl.top.setLeft(0);
8786             this.maskEl.top.setTop(0);
8787             this.maskEl.top.show();
8788             
8789             this.maskEl.left.setStyle('position', 'absolute');
8790             this.maskEl.left.setStyle('z-index', zIndex);
8791             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8792             this.maskEl.left.setLeft(0);
8793             this.maskEl.left.setTop(box.y - this.padding);
8794             this.maskEl.left.show();
8795
8796             this.maskEl.bottom.setStyle('position', 'absolute');
8797             this.maskEl.bottom.setStyle('z-index', zIndex);
8798             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8799             this.maskEl.bottom.setLeft(0);
8800             this.maskEl.bottom.setTop(box.bottom + this.padding);
8801             this.maskEl.bottom.show();
8802
8803             this.maskEl.right.setStyle('position', 'absolute');
8804             this.maskEl.right.setStyle('z-index', zIndex);
8805             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8806             this.maskEl.right.setLeft(box.right + this.padding);
8807             this.maskEl.right.setTop(box.y - this.padding);
8808             this.maskEl.right.show();
8809
8810             this.toolTip.bindEl = this.target.el;
8811
8812             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8813
8814             var tip = this.target.blankText;
8815
8816             if(this.target.getValue() !== '' ) {
8817                 
8818                 if (this.target.invalidText.length) {
8819                     tip = this.target.invalidText;
8820                 } else if (this.target.regexText.length){
8821                     tip = this.target.regexText;
8822                 }
8823             }
8824
8825             this.toolTip.show(tip);
8826
8827             this.intervalID = window.setInterval(function() {
8828                 Roo.bootstrap.Form.popover.unmask();
8829             }, 10000);
8830
8831             window.onwheel = function(){ return false;};
8832             
8833             (function(){ this.isMasked = true; }).defer(500, this);
8834             
8835         },
8836         
8837         unmask : function()
8838         {
8839             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8840                 return;
8841             }
8842             
8843             this.maskEl.top.setStyle('position', 'absolute');
8844             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8845             this.maskEl.top.hide();
8846
8847             this.maskEl.left.setStyle('position', 'absolute');
8848             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8849             this.maskEl.left.hide();
8850
8851             this.maskEl.bottom.setStyle('position', 'absolute');
8852             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8853             this.maskEl.bottom.hide();
8854
8855             this.maskEl.right.setStyle('position', 'absolute');
8856             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8857             this.maskEl.right.hide();
8858             
8859             this.toolTip.hide();
8860             
8861             this.toolTip.el.hide();
8862             
8863             window.onwheel = function(){ return true;};
8864             
8865             if(this.intervalID){
8866                 window.clearInterval(this.intervalID);
8867                 this.intervalID = false;
8868             }
8869             
8870             this.isMasked = false;
8871             
8872         }
8873         
8874     }
8875     
8876 });
8877
8878 /*
8879  * Based on:
8880  * Ext JS Library 1.1.1
8881  * Copyright(c) 2006-2007, Ext JS, LLC.
8882  *
8883  * Originally Released Under LGPL - original licence link has changed is not relivant.
8884  *
8885  * Fork - LGPL
8886  * <script type="text/javascript">
8887  */
8888 /**
8889  * @class Roo.form.VTypes
8890  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8891  * @singleton
8892  */
8893 Roo.form.VTypes = function(){
8894     // closure these in so they are only created once.
8895     var alpha = /^[a-zA-Z_]+$/;
8896     var alphanum = /^[a-zA-Z0-9_]+$/;
8897     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8898     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8899
8900     // All these messages and functions are configurable
8901     return {
8902         /**
8903          * The function used to validate email addresses
8904          * @param {String} value The email address
8905          */
8906         'email' : function(v){
8907             return email.test(v);
8908         },
8909         /**
8910          * The error text to display when the email validation function returns false
8911          * @type String
8912          */
8913         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8914         /**
8915          * The keystroke filter mask to be applied on email input
8916          * @type RegExp
8917          */
8918         'emailMask' : /[a-z0-9_\.\-@]/i,
8919
8920         /**
8921          * The function used to validate URLs
8922          * @param {String} value The URL
8923          */
8924         'url' : function(v){
8925             return url.test(v);
8926         },
8927         /**
8928          * The error text to display when the url validation function returns false
8929          * @type String
8930          */
8931         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8932         
8933         /**
8934          * The function used to validate alpha values
8935          * @param {String} value The value
8936          */
8937         'alpha' : function(v){
8938             return alpha.test(v);
8939         },
8940         /**
8941          * The error text to display when the alpha validation function returns false
8942          * @type String
8943          */
8944         'alphaText' : 'This field should only contain letters and _',
8945         /**
8946          * The keystroke filter mask to be applied on alpha input
8947          * @type RegExp
8948          */
8949         'alphaMask' : /[a-z_]/i,
8950
8951         /**
8952          * The function used to validate alphanumeric values
8953          * @param {String} value The value
8954          */
8955         'alphanum' : function(v){
8956             return alphanum.test(v);
8957         },
8958         /**
8959          * The error text to display when the alphanumeric validation function returns false
8960          * @type String
8961          */
8962         'alphanumText' : 'This field should only contain letters, numbers and _',
8963         /**
8964          * The keystroke filter mask to be applied on alphanumeric input
8965          * @type RegExp
8966          */
8967         'alphanumMask' : /[a-z0-9_]/i
8968     };
8969 }();/*
8970  * - LGPL
8971  *
8972  * Input
8973  * 
8974  */
8975
8976 /**
8977  * @class Roo.bootstrap.Input
8978  * @extends Roo.bootstrap.Component
8979  * Bootstrap Input class
8980  * @cfg {Boolean} disabled is it disabled
8981  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8982  * @cfg {String} name name of the input
8983  * @cfg {string} fieldLabel - the label associated
8984  * @cfg {string} placeholder - placeholder to put in text.
8985  * @cfg {string}  before - input group add on before
8986  * @cfg {string} after - input group add on after
8987  * @cfg {string} size - (lg|sm) or leave empty..
8988  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8989  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8990  * @cfg {Number} md colspan out of 12 for computer-sized screens
8991  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8992  * @cfg {string} value default value of the input
8993  * @cfg {Number} labelWidth set the width of label 
8994  * @cfg {Number} labellg set the width of label (1-12)
8995  * @cfg {Number} labelmd set the width of label (1-12)
8996  * @cfg {Number} labelsm set the width of label (1-12)
8997  * @cfg {Number} labelxs set the width of label (1-12)
8998  * @cfg {String} labelAlign (top|left)
8999  * @cfg {Boolean} readOnly Specifies that the field should be read-only
9000  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
9001  * @cfg {String} indicatorpos (left|right) default left
9002  * @cfg {String} capture (user|camera) use for file input only. (default empty)
9003  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
9004
9005  * @cfg {String} align (left|center|right) Default left
9006  * @cfg {Boolean} forceFeedback (true|false) Default false
9007  * 
9008  * @constructor
9009  * Create a new Input
9010  * @param {Object} config The config object
9011  */
9012
9013 Roo.bootstrap.Input = function(config){
9014     
9015     Roo.bootstrap.Input.superclass.constructor.call(this, config);
9016     
9017     this.addEvents({
9018         /**
9019          * @event focus
9020          * Fires when this field receives input focus.
9021          * @param {Roo.form.Field} this
9022          */
9023         focus : true,
9024         /**
9025          * @event blur
9026          * Fires when this field loses input focus.
9027          * @param {Roo.form.Field} this
9028          */
9029         blur : true,
9030         /**
9031          * @event specialkey
9032          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
9033          * {@link Roo.EventObject#getKey} to determine which key was pressed.
9034          * @param {Roo.form.Field} this
9035          * @param {Roo.EventObject} e The event object
9036          */
9037         specialkey : true,
9038         /**
9039          * @event change
9040          * Fires just before the field blurs if the field value has changed.
9041          * @param {Roo.form.Field} this
9042          * @param {Mixed} newValue The new value
9043          * @param {Mixed} oldValue The original value
9044          */
9045         change : true,
9046         /**
9047          * @event invalid
9048          * Fires after the field has been marked as invalid.
9049          * @param {Roo.form.Field} this
9050          * @param {String} msg The validation message
9051          */
9052         invalid : true,
9053         /**
9054          * @event valid
9055          * Fires after the field has been validated with no errors.
9056          * @param {Roo.form.Field} this
9057          */
9058         valid : true,
9059          /**
9060          * @event keyup
9061          * Fires after the key up
9062          * @param {Roo.form.Field} this
9063          * @param {Roo.EventObject}  e The event Object
9064          */
9065         keyup : true
9066     });
9067 };
9068
9069 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
9070      /**
9071      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
9072       automatic validation (defaults to "keyup").
9073      */
9074     validationEvent : "keyup",
9075      /**
9076      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
9077      */
9078     validateOnBlur : true,
9079     /**
9080      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
9081      */
9082     validationDelay : 250,
9083      /**
9084      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
9085      */
9086     focusClass : "x-form-focus",  // not needed???
9087     
9088        
9089     /**
9090      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9091      */
9092     invalidClass : "has-warning",
9093     
9094     /**
9095      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9096      */
9097     validClass : "has-success",
9098     
9099     /**
9100      * @cfg {Boolean} hasFeedback (true|false) default true
9101      */
9102     hasFeedback : true,
9103     
9104     /**
9105      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9106      */
9107     invalidFeedbackClass : "glyphicon-warning-sign",
9108     
9109     /**
9110      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9111      */
9112     validFeedbackClass : "glyphicon-ok",
9113     
9114     /**
9115      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
9116      */
9117     selectOnFocus : false,
9118     
9119      /**
9120      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
9121      */
9122     maskRe : null,
9123        /**
9124      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
9125      */
9126     vtype : null,
9127     
9128       /**
9129      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
9130      */
9131     disableKeyFilter : false,
9132     
9133        /**
9134      * @cfg {Boolean} disabled True to disable the field (defaults to false).
9135      */
9136     disabled : false,
9137      /**
9138      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
9139      */
9140     allowBlank : true,
9141     /**
9142      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9143      */
9144     blankText : "Please complete this mandatory field",
9145     
9146      /**
9147      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9148      */
9149     minLength : 0,
9150     /**
9151      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9152      */
9153     maxLength : Number.MAX_VALUE,
9154     /**
9155      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9156      */
9157     minLengthText : "The minimum length for this field is {0}",
9158     /**
9159      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9160      */
9161     maxLengthText : "The maximum length for this field is {0}",
9162   
9163     
9164     /**
9165      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9166      * If available, this function will be called only after the basic validators all return true, and will be passed the
9167      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9168      */
9169     validator : null,
9170     /**
9171      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9172      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9173      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
9174      */
9175     regex : null,
9176     /**
9177      * @cfg {String} regexText -- Depricated - use Invalid Text
9178      */
9179     regexText : "",
9180     
9181     /**
9182      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9183      */
9184     invalidText : "",
9185     
9186     
9187     
9188     autocomplete: false,
9189     
9190     
9191     fieldLabel : '',
9192     inputType : 'text',
9193     
9194     name : false,
9195     placeholder: false,
9196     before : false,
9197     after : false,
9198     size : false,
9199     hasFocus : false,
9200     preventMark: false,
9201     isFormField : true,
9202     value : '',
9203     labelWidth : 2,
9204     labelAlign : false,
9205     readOnly : false,
9206     align : false,
9207     formatedValue : false,
9208     forceFeedback : false,
9209     
9210     indicatorpos : 'left',
9211     
9212     labellg : 0,
9213     labelmd : 0,
9214     labelsm : 0,
9215     labelxs : 0,
9216     
9217     capture : '',
9218     accept : '',
9219     
9220     parentLabelAlign : function()
9221     {
9222         var parent = this;
9223         while (parent.parent()) {
9224             parent = parent.parent();
9225             if (typeof(parent.labelAlign) !='undefined') {
9226                 return parent.labelAlign;
9227             }
9228         }
9229         return 'left';
9230         
9231     },
9232     
9233     getAutoCreate : function()
9234     {
9235         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9236         
9237         var id = Roo.id();
9238         
9239         var cfg = {};
9240         
9241         if(this.inputType != 'hidden'){
9242             cfg.cls = 'form-group' //input-group
9243         }
9244         
9245         var input =  {
9246             tag: 'input',
9247             id : id,
9248             type : this.inputType,
9249             value : this.value,
9250             cls : 'form-control',
9251             placeholder : this.placeholder || '',
9252             autocomplete : this.autocomplete || 'new-password'
9253         };
9254         
9255         if(this.capture.length){
9256             input.capture = this.capture;
9257         }
9258         
9259         if(this.accept.length){
9260             input.accept = this.accept + "/*";
9261         }
9262         
9263         if(this.align){
9264             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9265         }
9266         
9267         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9268             input.maxLength = this.maxLength;
9269         }
9270         
9271         if (this.disabled) {
9272             input.disabled=true;
9273         }
9274         
9275         if (this.readOnly) {
9276             input.readonly=true;
9277         }
9278         
9279         if (this.name) {
9280             input.name = this.name;
9281         }
9282         
9283         if (this.size) {
9284             input.cls += ' input-' + this.size;
9285         }
9286         
9287         var settings=this;
9288         ['xs','sm','md','lg'].map(function(size){
9289             if (settings[size]) {
9290                 cfg.cls += ' col-' + size + '-' + settings[size];
9291             }
9292         });
9293         
9294         var inputblock = input;
9295         
9296         var feedback = {
9297             tag: 'span',
9298             cls: 'glyphicon form-control-feedback'
9299         };
9300             
9301         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9302             
9303             inputblock = {
9304                 cls : 'has-feedback',
9305                 cn :  [
9306                     input,
9307                     feedback
9308                 ] 
9309             };  
9310         }
9311         
9312         if (this.before || this.after) {
9313             
9314             inputblock = {
9315                 cls : 'input-group',
9316                 cn :  [] 
9317             };
9318             
9319             if (this.before && typeof(this.before) == 'string') {
9320                 
9321                 inputblock.cn.push({
9322                     tag :'span',
9323                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9324                     html : this.before
9325                 });
9326             }
9327             if (this.before && typeof(this.before) == 'object') {
9328                 this.before = Roo.factory(this.before);
9329                 
9330                 inputblock.cn.push({
9331                     tag :'span',
9332                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9333                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9334                 });
9335             }
9336             
9337             inputblock.cn.push(input);
9338             
9339             if (this.after && typeof(this.after) == 'string') {
9340                 inputblock.cn.push({
9341                     tag :'span',
9342                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9343                     html : this.after
9344                 });
9345             }
9346             if (this.after && typeof(this.after) == 'object') {
9347                 this.after = Roo.factory(this.after);
9348                 
9349                 inputblock.cn.push({
9350                     tag :'span',
9351                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
9352                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9353                 });
9354             }
9355             
9356             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9357                 inputblock.cls += ' has-feedback';
9358                 inputblock.cn.push(feedback);
9359             }
9360         };
9361         var indicator = {
9362             tag : 'i',
9363             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9364             tooltip : 'This field is required'
9365         };
9366         if (Roo.bootstrap.version == 4) {
9367             indicator = {
9368                 tag : 'i',
9369                 style : 'display-none'
9370             };
9371         }
9372         if (align ==='left' && this.fieldLabel.length) {
9373             
9374             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
9375             
9376             cfg.cn = [
9377                 indicator,
9378                 {
9379                     tag: 'label',
9380                     'for' :  id,
9381                     cls : 'control-label col-form-label',
9382                     html : this.fieldLabel
9383
9384                 },
9385                 {
9386                     cls : "", 
9387                     cn: [
9388                         inputblock
9389                     ]
9390                 }
9391             ];
9392             
9393             var labelCfg = cfg.cn[1];
9394             var contentCfg = cfg.cn[2];
9395             
9396             if(this.indicatorpos == 'right'){
9397                 cfg.cn = [
9398                     {
9399                         tag: 'label',
9400                         'for' :  id,
9401                         cls : 'control-label col-form-label',
9402                         cn : [
9403                             {
9404                                 tag : 'span',
9405                                 html : this.fieldLabel
9406                             },
9407                             indicator
9408                         ]
9409                     },
9410                     {
9411                         cls : "",
9412                         cn: [
9413                             inputblock
9414                         ]
9415                     }
9416
9417                 ];
9418                 
9419                 labelCfg = cfg.cn[0];
9420                 contentCfg = cfg.cn[1];
9421             
9422             }
9423             
9424             if(this.labelWidth > 12){
9425                 labelCfg.style = "width: " + this.labelWidth + 'px';
9426             }
9427             
9428             if(this.labelWidth < 13 && this.labelmd == 0){
9429                 this.labelmd = this.labelWidth;
9430             }
9431             
9432             if(this.labellg > 0){
9433                 labelCfg.cls += ' col-lg-' + this.labellg;
9434                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9435             }
9436             
9437             if(this.labelmd > 0){
9438                 labelCfg.cls += ' col-md-' + this.labelmd;
9439                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9440             }
9441             
9442             if(this.labelsm > 0){
9443                 labelCfg.cls += ' col-sm-' + this.labelsm;
9444                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9445             }
9446             
9447             if(this.labelxs > 0){
9448                 labelCfg.cls += ' col-xs-' + this.labelxs;
9449                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9450             }
9451             
9452             
9453         } else if ( this.fieldLabel.length) {
9454                 
9455             cfg.cn = [
9456                 {
9457                     tag : 'i',
9458                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9459                     tooltip : 'This field is required'
9460                 },
9461                 {
9462                     tag: 'label',
9463                    //cls : 'input-group-addon',
9464                     html : this.fieldLabel
9465
9466                 },
9467
9468                inputblock
9469
9470            ];
9471            
9472            if(this.indicatorpos == 'right'){
9473                 
9474                 cfg.cn = [
9475                     {
9476                         tag: 'label',
9477                        //cls : 'input-group-addon',
9478                         html : this.fieldLabel
9479
9480                     },
9481                     {
9482                         tag : 'i',
9483                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9484                         tooltip : 'This field is required'
9485                     },
9486
9487                    inputblock
9488
9489                ];
9490
9491             }
9492
9493         } else {
9494             
9495             cfg.cn = [
9496
9497                     inputblock
9498
9499             ];
9500                 
9501                 
9502         };
9503         
9504         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9505            cfg.cls += ' navbar-form';
9506         }
9507         
9508         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
9509             // on BS4 we do this only if not form 
9510             cfg.cls += ' navbar-form';
9511             cfg.tag = 'li';
9512         }
9513         
9514         return cfg;
9515         
9516     },
9517     /**
9518      * return the real input element.
9519      */
9520     inputEl: function ()
9521     {
9522         return this.el.select('input.form-control',true).first();
9523     },
9524     
9525     tooltipEl : function()
9526     {
9527         return this.inputEl();
9528     },
9529     
9530     indicatorEl : function()
9531     {
9532         if (Roo.bootstrap.version == 4) {
9533             return false; // not enabled in v4 yet.
9534         }
9535         
9536         var indicator = this.el.select('i.roo-required-indicator',true).first();
9537         
9538         if(!indicator){
9539             return false;
9540         }
9541         
9542         return indicator;
9543         
9544     },
9545     
9546     setDisabled : function(v)
9547     {
9548         var i  = this.inputEl().dom;
9549         if (!v) {
9550             i.removeAttribute('disabled');
9551             return;
9552             
9553         }
9554         i.setAttribute('disabled','true');
9555     },
9556     initEvents : function()
9557     {
9558           
9559         this.inputEl().on("keydown" , this.fireKey,  this);
9560         this.inputEl().on("focus", this.onFocus,  this);
9561         this.inputEl().on("blur", this.onBlur,  this);
9562         
9563         this.inputEl().relayEvent('keyup', this);
9564         
9565         this.indicator = this.indicatorEl();
9566         
9567         if(this.indicator){
9568             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9569         }
9570  
9571         // reference to original value for reset
9572         this.originalValue = this.getValue();
9573         //Roo.form.TextField.superclass.initEvents.call(this);
9574         if(this.validationEvent == 'keyup'){
9575             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9576             this.inputEl().on('keyup', this.filterValidation, this);
9577         }
9578         else if(this.validationEvent !== false){
9579             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9580         }
9581         
9582         if(this.selectOnFocus){
9583             this.on("focus", this.preFocus, this);
9584             
9585         }
9586         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9587             this.inputEl().on("keypress", this.filterKeys, this);
9588         } else {
9589             this.inputEl().relayEvent('keypress', this);
9590         }
9591        /* if(this.grow){
9592             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9593             this.el.on("click", this.autoSize,  this);
9594         }
9595         */
9596         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9597             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9598         }
9599         
9600         if (typeof(this.before) == 'object') {
9601             this.before.render(this.el.select('.roo-input-before',true).first());
9602         }
9603         if (typeof(this.after) == 'object') {
9604             this.after.render(this.el.select('.roo-input-after',true).first());
9605         }
9606         
9607         this.inputEl().on('change', this.onChange, this);
9608         
9609     },
9610     filterValidation : function(e){
9611         if(!e.isNavKeyPress()){
9612             this.validationTask.delay(this.validationDelay);
9613         }
9614     },
9615      /**
9616      * Validates the field value
9617      * @return {Boolean} True if the value is valid, else false
9618      */
9619     validate : function(){
9620         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9621         if(this.disabled || this.validateValue(this.getRawValue())){
9622             this.markValid();
9623             return true;
9624         }
9625         
9626         this.markInvalid();
9627         return false;
9628     },
9629     
9630     
9631     /**
9632      * Validates a value according to the field's validation rules and marks the field as invalid
9633      * if the validation fails
9634      * @param {Mixed} value The value to validate
9635      * @return {Boolean} True if the value is valid, else false
9636      */
9637     validateValue : function(value)
9638     {
9639         if(this.getVisibilityEl().hasClass('hidden')){
9640             return true;
9641         }
9642         
9643         if(value.length < 1)  { // if it's blank
9644             if(this.allowBlank){
9645                 return true;
9646             }
9647             return false;
9648         }
9649         
9650         if(value.length < this.minLength){
9651             return false;
9652         }
9653         if(value.length > this.maxLength){
9654             return false;
9655         }
9656         if(this.vtype){
9657             var vt = Roo.form.VTypes;
9658             if(!vt[this.vtype](value, this)){
9659                 return false;
9660             }
9661         }
9662         if(typeof this.validator == "function"){
9663             var msg = this.validator(value);
9664             if(msg !== true){
9665                 return false;
9666             }
9667             if (typeof(msg) == 'string') {
9668                 this.invalidText = msg;
9669             }
9670         }
9671         
9672         if(this.regex && !this.regex.test(value)){
9673             return false;
9674         }
9675         
9676         return true;
9677     },
9678     
9679      // private
9680     fireKey : function(e){
9681         //Roo.log('field ' + e.getKey());
9682         if(e.isNavKeyPress()){
9683             this.fireEvent("specialkey", this, e);
9684         }
9685     },
9686     focus : function (selectText){
9687         if(this.rendered){
9688             this.inputEl().focus();
9689             if(selectText === true){
9690                 this.inputEl().dom.select();
9691             }
9692         }
9693         return this;
9694     } ,
9695     
9696     onFocus : function(){
9697         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9698            // this.el.addClass(this.focusClass);
9699         }
9700         if(!this.hasFocus){
9701             this.hasFocus = true;
9702             this.startValue = this.getValue();
9703             this.fireEvent("focus", this);
9704         }
9705     },
9706     
9707     beforeBlur : Roo.emptyFn,
9708
9709     
9710     // private
9711     onBlur : function(){
9712         this.beforeBlur();
9713         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9714             //this.el.removeClass(this.focusClass);
9715         }
9716         this.hasFocus = false;
9717         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9718             this.validate();
9719         }
9720         var v = this.getValue();
9721         if(String(v) !== String(this.startValue)){
9722             this.fireEvent('change', this, v, this.startValue);
9723         }
9724         this.fireEvent("blur", this);
9725     },
9726     
9727     onChange : function(e)
9728     {
9729         var v = this.getValue();
9730         if(String(v) !== String(this.startValue)){
9731             this.fireEvent('change', this, v, this.startValue);
9732         }
9733         
9734     },
9735     
9736     /**
9737      * Resets the current field value to the originally loaded value and clears any validation messages
9738      */
9739     reset : function(){
9740         this.setValue(this.originalValue);
9741         this.validate();
9742     },
9743      /**
9744      * Returns the name of the field
9745      * @return {Mixed} name The name field
9746      */
9747     getName: function(){
9748         return this.name;
9749     },
9750      /**
9751      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9752      * @return {Mixed} value The field value
9753      */
9754     getValue : function(){
9755         
9756         var v = this.inputEl().getValue();
9757         
9758         return v;
9759     },
9760     /**
9761      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9762      * @return {Mixed} value The field value
9763      */
9764     getRawValue : function(){
9765         var v = this.inputEl().getValue();
9766         
9767         return v;
9768     },
9769     
9770     /**
9771      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9772      * @param {Mixed} value The value to set
9773      */
9774     setRawValue : function(v){
9775         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9776     },
9777     
9778     selectText : function(start, end){
9779         var v = this.getRawValue();
9780         if(v.length > 0){
9781             start = start === undefined ? 0 : start;
9782             end = end === undefined ? v.length : end;
9783             var d = this.inputEl().dom;
9784             if(d.setSelectionRange){
9785                 d.setSelectionRange(start, end);
9786             }else if(d.createTextRange){
9787                 var range = d.createTextRange();
9788                 range.moveStart("character", start);
9789                 range.moveEnd("character", v.length-end);
9790                 range.select();
9791             }
9792         }
9793     },
9794     
9795     /**
9796      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9797      * @param {Mixed} value The value to set
9798      */
9799     setValue : function(v){
9800         this.value = v;
9801         if(this.rendered){
9802             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9803             this.validate();
9804         }
9805     },
9806     
9807     /*
9808     processValue : function(value){
9809         if(this.stripCharsRe){
9810             var newValue = value.replace(this.stripCharsRe, '');
9811             if(newValue !== value){
9812                 this.setRawValue(newValue);
9813                 return newValue;
9814             }
9815         }
9816         return value;
9817     },
9818   */
9819     preFocus : function(){
9820         
9821         if(this.selectOnFocus){
9822             this.inputEl().dom.select();
9823         }
9824     },
9825     filterKeys : function(e){
9826         var k = e.getKey();
9827         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9828             return;
9829         }
9830         var c = e.getCharCode(), cc = String.fromCharCode(c);
9831         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9832             return;
9833         }
9834         if(!this.maskRe.test(cc)){
9835             e.stopEvent();
9836         }
9837     },
9838      /**
9839      * Clear any invalid styles/messages for this field
9840      */
9841     clearInvalid : function(){
9842         
9843         if(!this.el || this.preventMark){ // not rendered
9844             return;
9845         }
9846         
9847         
9848         this.el.removeClass([this.invalidClass, 'is-invalid']);
9849         
9850         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9851             
9852             var feedback = this.el.select('.form-control-feedback', true).first();
9853             
9854             if(feedback){
9855                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9856             }
9857             
9858         }
9859         
9860         if(this.indicator){
9861             this.indicator.removeClass('visible');
9862             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9863         }
9864         
9865         this.fireEvent('valid', this);
9866     },
9867     
9868      /**
9869      * Mark this field as valid
9870      */
9871     markValid : function()
9872     {
9873         if(!this.el  || this.preventMark){ // not rendered...
9874             return;
9875         }
9876         
9877         this.el.removeClass([this.invalidClass, this.validClass]);
9878         this.inputEl().removeClass(['is-valid', 'is-invalid']);
9879
9880         var feedback = this.el.select('.form-control-feedback', true).first();
9881             
9882         if(feedback){
9883             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9884         }
9885         
9886         if(this.indicator){
9887             this.indicator.removeClass('visible');
9888             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9889         }
9890         
9891         if(this.disabled){
9892             return;
9893         }
9894         
9895         if(this.allowBlank && !this.getRawValue().length){
9896             return;
9897         }
9898         if (Roo.bootstrap.version == 3) {
9899             this.el.addClass(this.validClass);
9900         } else {
9901             this.inputEl().addClass('is-valid');
9902         }
9903
9904         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9905             
9906             var feedback = this.el.select('.form-control-feedback', true).first();
9907             
9908             if(feedback){
9909                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9910                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9911             }
9912             
9913         }
9914         
9915         this.fireEvent('valid', this);
9916     },
9917     
9918      /**
9919      * Mark this field as invalid
9920      * @param {String} msg The validation message
9921      */
9922     markInvalid : function(msg)
9923     {
9924         if(!this.el  || this.preventMark){ // not rendered
9925             return;
9926         }
9927         
9928         this.el.removeClass([this.invalidClass, this.validClass]);
9929         this.inputEl().removeClass(['is-valid', 'is-invalid']);
9930         
9931         var feedback = this.el.select('.form-control-feedback', true).first();
9932             
9933         if(feedback){
9934             this.el.select('.form-control-feedback', true).first().removeClass(
9935                     [this.invalidFeedbackClass, this.validFeedbackClass]);
9936         }
9937
9938         if(this.disabled){
9939             return;
9940         }
9941         
9942         if(this.allowBlank && !this.getRawValue().length){
9943             return;
9944         }
9945         
9946         if(this.indicator){
9947             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9948             this.indicator.addClass('visible');
9949         }
9950         if (Roo.bootstrap.version == 3) {
9951             this.el.addClass(this.invalidClass);
9952         } else {
9953             this.inputEl().addClass('is-invalid');
9954         }
9955         
9956         
9957         
9958         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9959             
9960             var feedback = this.el.select('.form-control-feedback', true).first();
9961             
9962             if(feedback){
9963                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9964                 
9965                 if(this.getValue().length || this.forceFeedback){
9966                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9967                 }
9968                 
9969             }
9970             
9971         }
9972         
9973         this.fireEvent('invalid', this, msg);
9974     },
9975     // private
9976     SafariOnKeyDown : function(event)
9977     {
9978         // this is a workaround for a password hang bug on chrome/ webkit.
9979         if (this.inputEl().dom.type != 'password') {
9980             return;
9981         }
9982         
9983         var isSelectAll = false;
9984         
9985         if(this.inputEl().dom.selectionEnd > 0){
9986             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9987         }
9988         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9989             event.preventDefault();
9990             this.setValue('');
9991             return;
9992         }
9993         
9994         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9995             
9996             event.preventDefault();
9997             // this is very hacky as keydown always get's upper case.
9998             //
9999             var cc = String.fromCharCode(event.getCharCode());
10000             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
10001             
10002         }
10003     },
10004     adjustWidth : function(tag, w){
10005         tag = tag.toLowerCase();
10006         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
10007             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
10008                 if(tag == 'input'){
10009                     return w + 2;
10010                 }
10011                 if(tag == 'textarea'){
10012                     return w-2;
10013                 }
10014             }else if(Roo.isOpera){
10015                 if(tag == 'input'){
10016                     return w + 2;
10017                 }
10018                 if(tag == 'textarea'){
10019                     return w-2;
10020                 }
10021             }
10022         }
10023         return w;
10024     },
10025     
10026     setFieldLabel : function(v)
10027     {
10028         if(!this.rendered){
10029             return;
10030         }
10031         
10032         if(this.indicatorEl()){
10033             var ar = this.el.select('label > span',true);
10034             
10035             if (ar.elements.length) {
10036                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10037                 this.fieldLabel = v;
10038                 return;
10039             }
10040             
10041             var br = this.el.select('label',true);
10042             
10043             if(br.elements.length) {
10044                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10045                 this.fieldLabel = v;
10046                 return;
10047             }
10048             
10049             Roo.log('Cannot Found any of label > span || label in input');
10050             return;
10051         }
10052         
10053         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10054         this.fieldLabel = v;
10055         
10056         
10057     }
10058 });
10059
10060  
10061 /*
10062  * - LGPL
10063  *
10064  * Input
10065  * 
10066  */
10067
10068 /**
10069  * @class Roo.bootstrap.TextArea
10070  * @extends Roo.bootstrap.Input
10071  * Bootstrap TextArea class
10072  * @cfg {Number} cols Specifies the visible width of a text area
10073  * @cfg {Number} rows Specifies the visible number of lines in a text area
10074  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
10075  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
10076  * @cfg {string} html text
10077  * 
10078  * @constructor
10079  * Create a new TextArea
10080  * @param {Object} config The config object
10081  */
10082
10083 Roo.bootstrap.TextArea = function(config){
10084     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
10085    
10086 };
10087
10088 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
10089      
10090     cols : false,
10091     rows : 5,
10092     readOnly : false,
10093     warp : 'soft',
10094     resize : false,
10095     value: false,
10096     html: false,
10097     
10098     getAutoCreate : function(){
10099         
10100         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10101         
10102         var id = Roo.id();
10103         
10104         var cfg = {};
10105         
10106         if(this.inputType != 'hidden'){
10107             cfg.cls = 'form-group' //input-group
10108         }
10109         
10110         var input =  {
10111             tag: 'textarea',
10112             id : id,
10113             warp : this.warp,
10114             rows : this.rows,
10115             value : this.value || '',
10116             html: this.html || '',
10117             cls : 'form-control',
10118             placeholder : this.placeholder || '' 
10119             
10120         };
10121         
10122         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10123             input.maxLength = this.maxLength;
10124         }
10125         
10126         if(this.resize){
10127             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
10128         }
10129         
10130         if(this.cols){
10131             input.cols = this.cols;
10132         }
10133         
10134         if (this.readOnly) {
10135             input.readonly = true;
10136         }
10137         
10138         if (this.name) {
10139             input.name = this.name;
10140         }
10141         
10142         if (this.size) {
10143             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
10144         }
10145         
10146         var settings=this;
10147         ['xs','sm','md','lg'].map(function(size){
10148             if (settings[size]) {
10149                 cfg.cls += ' col-' + size + '-' + settings[size];
10150             }
10151         });
10152         
10153         var inputblock = input;
10154         
10155         if(this.hasFeedback && !this.allowBlank){
10156             
10157             var feedback = {
10158                 tag: 'span',
10159                 cls: 'glyphicon form-control-feedback'
10160             };
10161
10162             inputblock = {
10163                 cls : 'has-feedback',
10164                 cn :  [
10165                     input,
10166                     feedback
10167                 ] 
10168             };  
10169         }
10170         
10171         
10172         if (this.before || this.after) {
10173             
10174             inputblock = {
10175                 cls : 'input-group',
10176                 cn :  [] 
10177             };
10178             if (this.before) {
10179                 inputblock.cn.push({
10180                     tag :'span',
10181                     cls : 'input-group-addon',
10182                     html : this.before
10183                 });
10184             }
10185             
10186             inputblock.cn.push(input);
10187             
10188             if(this.hasFeedback && !this.allowBlank){
10189                 inputblock.cls += ' has-feedback';
10190                 inputblock.cn.push(feedback);
10191             }
10192             
10193             if (this.after) {
10194                 inputblock.cn.push({
10195                     tag :'span',
10196                     cls : 'input-group-addon',
10197                     html : this.after
10198                 });
10199             }
10200             
10201         }
10202         
10203         if (align ==='left' && this.fieldLabel.length) {
10204             cfg.cn = [
10205                 {
10206                     tag: 'label',
10207                     'for' :  id,
10208                     cls : 'control-label',
10209                     html : this.fieldLabel
10210                 },
10211                 {
10212                     cls : "",
10213                     cn: [
10214                         inputblock
10215                     ]
10216                 }
10217
10218             ];
10219             
10220             if(this.labelWidth > 12){
10221                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10222             }
10223
10224             if(this.labelWidth < 13 && this.labelmd == 0){
10225                 this.labelmd = this.labelWidth;
10226             }
10227
10228             if(this.labellg > 0){
10229                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10230                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10231             }
10232
10233             if(this.labelmd > 0){
10234                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10235                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10236             }
10237
10238             if(this.labelsm > 0){
10239                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10240                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10241             }
10242
10243             if(this.labelxs > 0){
10244                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10245                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10246             }
10247             
10248         } else if ( this.fieldLabel.length) {
10249             cfg.cn = [
10250
10251                {
10252                    tag: 'label',
10253                    //cls : 'input-group-addon',
10254                    html : this.fieldLabel
10255
10256                },
10257
10258                inputblock
10259
10260            ];
10261
10262         } else {
10263
10264             cfg.cn = [
10265
10266                 inputblock
10267
10268             ];
10269                 
10270         }
10271         
10272         if (this.disabled) {
10273             input.disabled=true;
10274         }
10275         
10276         return cfg;
10277         
10278     },
10279     /**
10280      * return the real textarea element.
10281      */
10282     inputEl: function ()
10283     {
10284         return this.el.select('textarea.form-control',true).first();
10285     },
10286     
10287     /**
10288      * Clear any invalid styles/messages for this field
10289      */
10290     clearInvalid : function()
10291     {
10292         
10293         if(!this.el || this.preventMark){ // not rendered
10294             return;
10295         }
10296         
10297         var label = this.el.select('label', true).first();
10298         var icon = this.el.select('i.fa-star', true).first();
10299         
10300         if(label && icon){
10301             icon.remove();
10302         }
10303         this.el.removeClass( this.validClass);
10304         this.inputEl().removeClass('is-invalid');
10305          
10306         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10307             
10308             var feedback = this.el.select('.form-control-feedback', true).first();
10309             
10310             if(feedback){
10311                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10312             }
10313             
10314         }
10315         
10316         this.fireEvent('valid', this);
10317     },
10318     
10319      /**
10320      * Mark this field as valid
10321      */
10322     markValid : function()
10323     {
10324         if(!this.el  || this.preventMark){ // not rendered
10325             return;
10326         }
10327         
10328         this.el.removeClass([this.invalidClass, this.validClass]);
10329         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10330         
10331         var feedback = this.el.select('.form-control-feedback', true).first();
10332             
10333         if(feedback){
10334             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10335         }
10336
10337         if(this.disabled || this.allowBlank){
10338             return;
10339         }
10340         
10341         var label = this.el.select('label', true).first();
10342         var icon = this.el.select('i.fa-star', true).first();
10343         
10344         if(label && icon){
10345             icon.remove();
10346         }
10347         if (Roo.bootstrap.version == 3) {
10348             this.el.addClass(this.validClass);
10349         } else {
10350             this.inputEl().addClass('is-valid');
10351         }
10352         
10353         
10354         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10355             
10356             var feedback = this.el.select('.form-control-feedback', true).first();
10357             
10358             if(feedback){
10359                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10360                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10361             }
10362             
10363         }
10364         
10365         this.fireEvent('valid', this);
10366     },
10367     
10368      /**
10369      * Mark this field as invalid
10370      * @param {String} msg The validation message
10371      */
10372     markInvalid : function(msg)
10373     {
10374         if(!this.el  || this.preventMark){ // not rendered
10375             return;
10376         }
10377         
10378         this.el.removeClass([this.invalidClass, this.validClass]);
10379         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10380         
10381         var feedback = this.el.select('.form-control-feedback', true).first();
10382             
10383         if(feedback){
10384             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10385         }
10386
10387         if(this.disabled || this.allowBlank){
10388             return;
10389         }
10390         
10391         var label = this.el.select('label', true).first();
10392         var icon = this.el.select('i.fa-star', true).first();
10393         
10394         if(!this.getValue().length && label && !icon){
10395             this.el.createChild({
10396                 tag : 'i',
10397                 cls : 'text-danger fa fa-lg fa-star',
10398                 tooltip : 'This field is required',
10399                 style : 'margin-right:5px;'
10400             }, label, true);
10401         }
10402         
10403         if (Roo.bootstrap.version == 3) {
10404             this.el.addClass(this.invalidClass);
10405         } else {
10406             this.inputEl().addClass('is-invalid');
10407         }
10408         
10409         // fixme ... this may be depricated need to test..
10410         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10411             
10412             var feedback = this.el.select('.form-control-feedback', true).first();
10413             
10414             if(feedback){
10415                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10416                 
10417                 if(this.getValue().length || this.forceFeedback){
10418                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10419                 }
10420                 
10421             }
10422             
10423         }
10424         
10425         this.fireEvent('invalid', this, msg);
10426     }
10427 });
10428
10429  
10430 /*
10431  * - LGPL
10432  *
10433  * trigger field - base class for combo..
10434  * 
10435  */
10436  
10437 /**
10438  * @class Roo.bootstrap.TriggerField
10439  * @extends Roo.bootstrap.Input
10440  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10441  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10442  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10443  * for which you can provide a custom implementation.  For example:
10444  * <pre><code>
10445 var trigger = new Roo.bootstrap.TriggerField();
10446 trigger.onTriggerClick = myTriggerFn;
10447 trigger.applyTo('my-field');
10448 </code></pre>
10449  *
10450  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10451  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10452  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10453  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10454  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
10455
10456  * @constructor
10457  * Create a new TriggerField.
10458  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10459  * to the base TextField)
10460  */
10461 Roo.bootstrap.TriggerField = function(config){
10462     this.mimicing = false;
10463     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10464 };
10465
10466 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10467     /**
10468      * @cfg {String} triggerClass A CSS class to apply to the trigger
10469      */
10470      /**
10471      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10472      */
10473     hideTrigger:false,
10474
10475     /**
10476      * @cfg {Boolean} removable (true|false) special filter default false
10477      */
10478     removable : false,
10479     
10480     /** @cfg {Boolean} grow @hide */
10481     /** @cfg {Number} growMin @hide */
10482     /** @cfg {Number} growMax @hide */
10483
10484     /**
10485      * @hide 
10486      * @method
10487      */
10488     autoSize: Roo.emptyFn,
10489     // private
10490     monitorTab : true,
10491     // private
10492     deferHeight : true,
10493
10494     
10495     actionMode : 'wrap',
10496     
10497     caret : false,
10498     
10499     
10500     getAutoCreate : function(){
10501        
10502         var align = this.labelAlign || this.parentLabelAlign();
10503         
10504         var id = Roo.id();
10505         
10506         var cfg = {
10507             cls: 'form-group' //input-group
10508         };
10509         
10510         
10511         var input =  {
10512             tag: 'input',
10513             id : id,
10514             type : this.inputType,
10515             cls : 'form-control',
10516             autocomplete: 'new-password',
10517             placeholder : this.placeholder || '' 
10518             
10519         };
10520         if (this.name) {
10521             input.name = this.name;
10522         }
10523         if (this.size) {
10524             input.cls += ' input-' + this.size;
10525         }
10526         
10527         if (this.disabled) {
10528             input.disabled=true;
10529         }
10530         
10531         var inputblock = input;
10532         
10533         if(this.hasFeedback && !this.allowBlank){
10534             
10535             var feedback = {
10536                 tag: 'span',
10537                 cls: 'glyphicon form-control-feedback'
10538             };
10539             
10540             if(this.removable && !this.editable && !this.tickable){
10541                 inputblock = {
10542                     cls : 'has-feedback',
10543                     cn :  [
10544                         inputblock,
10545                         {
10546                             tag: 'button',
10547                             html : 'x',
10548                             cls : 'roo-combo-removable-btn close'
10549                         },
10550                         feedback
10551                     ] 
10552                 };
10553             } else {
10554                 inputblock = {
10555                     cls : 'has-feedback',
10556                     cn :  [
10557                         inputblock,
10558                         feedback
10559                     ] 
10560                 };
10561             }
10562
10563         } else {
10564             if(this.removable && !this.editable && !this.tickable){
10565                 inputblock = {
10566                     cls : 'roo-removable',
10567                     cn :  [
10568                         inputblock,
10569                         {
10570                             tag: 'button',
10571                             html : 'x',
10572                             cls : 'roo-combo-removable-btn close'
10573                         }
10574                     ] 
10575                 };
10576             }
10577         }
10578         
10579         if (this.before || this.after) {
10580             
10581             inputblock = {
10582                 cls : 'input-group',
10583                 cn :  [] 
10584             };
10585             if (this.before) {
10586                 inputblock.cn.push({
10587                     tag :'span',
10588                     cls : 'input-group-addon input-group-prepend input-group-text',
10589                     html : this.before
10590                 });
10591             }
10592             
10593             inputblock.cn.push(input);
10594             
10595             if(this.hasFeedback && !this.allowBlank){
10596                 inputblock.cls += ' has-feedback';
10597                 inputblock.cn.push(feedback);
10598             }
10599             
10600             if (this.after) {
10601                 inputblock.cn.push({
10602                     tag :'span',
10603                     cls : 'input-group-addon input-group-append input-group-text',
10604                     html : this.after
10605                 });
10606             }
10607             
10608         };
10609         
10610       
10611         
10612         var ibwrap = inputblock;
10613         
10614         if(this.multiple){
10615             ibwrap = {
10616                 tag: 'ul',
10617                 cls: 'roo-select2-choices',
10618                 cn:[
10619                     {
10620                         tag: 'li',
10621                         cls: 'roo-select2-search-field',
10622                         cn: [
10623
10624                             inputblock
10625                         ]
10626                     }
10627                 ]
10628             };
10629                 
10630         }
10631         
10632         var combobox = {
10633             cls: 'roo-select2-container input-group',
10634             cn: [
10635                  {
10636                     tag: 'input',
10637                     type : 'hidden',
10638                     cls: 'form-hidden-field'
10639                 },
10640                 ibwrap
10641             ]
10642         };
10643         
10644         if(!this.multiple && this.showToggleBtn){
10645             
10646             var caret = {
10647                         tag: 'span',
10648                         cls: 'caret'
10649              };
10650             if (this.caret != false) {
10651                 caret = {
10652                      tag: 'i',
10653                      cls: 'fa fa-' + this.caret
10654                 };
10655                 
10656             }
10657             
10658             combobox.cn.push({
10659                 tag :'span',
10660                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10661                 cn : [
10662                     Roo.bootstrap.version == 3 ? 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.dropdown-toggle',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                 cls: 'caret'
15381             };
15382             
15383             if (this.caret != false) {
15384                 caret = {
15385                      tag: 'i',
15386                      cls: 'fa fa-' + this.caret
15387                 };
15388                 
15389             }
15390             
15391             combobox.cn.push({
15392                 tag :'span',
15393                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15394                 cn : [
15395                     Roo.bootstrap.version == 3 ? caret : '',
15396                     {
15397                         tag: 'span',
15398                         cls: 'combobox-clear',
15399                         cn  : [
15400                             {
15401                                 tag : 'i',
15402                                 cls: 'icon-remove'
15403                             }
15404                         ]
15405                     }
15406                 ]
15407
15408             })
15409         }
15410         
15411         if(this.multiple){
15412             combobox.cls += ' roo-select2-container-multi';
15413         }
15414         
15415         var align = this.labelAlign || this.parentLabelAlign();
15416         
15417         if (align ==='left' && this.fieldLabel.length) {
15418
15419             cfg.cn = [
15420                 {
15421                    tag : 'i',
15422                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15423                    tooltip : 'This field is required'
15424                 },
15425                 {
15426                     tag: 'label',
15427                     cls : 'control-label col-form-label',
15428                     html : this.fieldLabel
15429
15430                 },
15431                 {
15432                     cls : '', 
15433                     cn: [
15434                         combobox
15435                     ]
15436                 }
15437             ];
15438             
15439             var labelCfg = cfg.cn[1];
15440             var contentCfg = cfg.cn[2];
15441             
15442
15443             if(this.indicatorpos == 'right'){
15444                 cfg.cn = [
15445                     {
15446                         tag: 'label',
15447                         'for' :  id,
15448                         cls : 'control-label col-form-label',
15449                         cn : [
15450                             {
15451                                 tag : 'span',
15452                                 html : this.fieldLabel
15453                             },
15454                             {
15455                                 tag : 'i',
15456                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15457                                 tooltip : 'This field is required'
15458                             }
15459                         ]
15460                     },
15461                     {
15462                         cls : "",
15463                         cn: [
15464                             combobox
15465                         ]
15466                     }
15467
15468                 ];
15469                 
15470                 labelCfg = cfg.cn[0];
15471                 contentCfg = cfg.cn[1];
15472             }
15473             
15474            
15475             
15476             if(this.labelWidth > 12){
15477                 labelCfg.style = "width: " + this.labelWidth + 'px';
15478             }
15479             
15480             if(this.labelWidth < 13 && this.labelmd == 0){
15481                 this.labelmd = this.labelWidth;
15482             }
15483             
15484             if(this.labellg > 0){
15485                 labelCfg.cls += ' col-lg-' + this.labellg;
15486                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15487             }
15488             
15489             if(this.labelmd > 0){
15490                 labelCfg.cls += ' col-md-' + this.labelmd;
15491                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15492             }
15493             
15494             if(this.labelsm > 0){
15495                 labelCfg.cls += ' col-sm-' + this.labelsm;
15496                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15497             }
15498             
15499             if(this.labelxs > 0){
15500                 labelCfg.cls += ' col-xs-' + this.labelxs;
15501                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15502             }
15503                 
15504                 
15505         } else if ( this.fieldLabel.length) {
15506             cfg.cn = [
15507                 {
15508                    tag : 'i',
15509                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15510                    tooltip : 'This field is required'
15511                 },
15512                 {
15513                     tag: 'label',
15514                     cls : 'control-label',
15515                     html : this.fieldLabel
15516
15517                 },
15518                 {
15519                     cls : '', 
15520                     cn: [
15521                         combobox
15522                     ]
15523                 }
15524             ];
15525             
15526             if(this.indicatorpos == 'right'){
15527                 cfg.cn = [
15528                     {
15529                         tag: 'label',
15530                         cls : 'control-label',
15531                         html : this.fieldLabel,
15532                         cn : [
15533                             {
15534                                tag : 'i',
15535                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15536                                tooltip : 'This field is required'
15537                             }
15538                         ]
15539                     },
15540                     {
15541                         cls : '', 
15542                         cn: [
15543                             combobox
15544                         ]
15545                     }
15546                 ];
15547             }
15548         } else {
15549             cfg.cn = combobox;    
15550         }
15551         
15552         
15553         var settings = this;
15554         
15555         ['xs','sm','md','lg'].map(function(size){
15556             if (settings[size]) {
15557                 cfg.cls += ' col-' + size + '-' + settings[size];
15558             }
15559         });
15560         
15561         return cfg;
15562     },
15563     
15564     initTouchView : function()
15565     {
15566         this.renderTouchView();
15567         
15568         this.touchViewEl.on('scroll', function(){
15569             this.el.dom.scrollTop = 0;
15570         }, this);
15571         
15572         this.originalValue = this.getValue();
15573         
15574         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15575         
15576         this.inputEl().on("click", this.showTouchView, this);
15577         if (this.triggerEl) {
15578             this.triggerEl.on("click", this.showTouchView, this);
15579         }
15580         
15581         
15582         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15583         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15584         
15585         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15586         
15587         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15588         this.store.on('load', this.onTouchViewLoad, this);
15589         this.store.on('loadexception', this.onTouchViewLoadException, this);
15590         
15591         if(this.hiddenName){
15592             
15593             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15594             
15595             this.hiddenField.dom.value =
15596                 this.hiddenValue !== undefined ? this.hiddenValue :
15597                 this.value !== undefined ? this.value : '';
15598         
15599             this.el.dom.removeAttribute('name');
15600             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15601         }
15602         
15603         if(this.multiple){
15604             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15605             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15606         }
15607         
15608         if(this.removable && !this.multiple){
15609             var close = this.closeTriggerEl();
15610             if(close){
15611                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15612                 close.on('click', this.removeBtnClick, this, close);
15613             }
15614         }
15615         /*
15616          * fix the bug in Safari iOS8
15617          */
15618         this.inputEl().on("focus", function(e){
15619             document.activeElement.blur();
15620         }, this);
15621         
15622         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15623         
15624         return;
15625         
15626         
15627     },
15628     
15629     renderTouchView : function()
15630     {
15631         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15632         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15633         
15634         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15635         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15636         
15637         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15638         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15639         this.touchViewBodyEl.setStyle('overflow', 'auto');
15640         
15641         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15642         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15643         
15644         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15645         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15646         
15647     },
15648     
15649     showTouchView : function()
15650     {
15651         if(this.disabled){
15652             return;
15653         }
15654         
15655         this.touchViewHeaderEl.hide();
15656
15657         if(this.modalTitle.length){
15658             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15659             this.touchViewHeaderEl.show();
15660         }
15661
15662         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15663         this.touchViewEl.show();
15664
15665         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15666         
15667         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15668         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15669
15670         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15671
15672         if(this.modalTitle.length){
15673             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15674         }
15675         
15676         this.touchViewBodyEl.setHeight(bodyHeight);
15677
15678         if(this.animate){
15679             var _this = this;
15680             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15681         }else{
15682             this.touchViewEl.addClass('in');
15683         }
15684         
15685         if(this._touchViewMask){
15686             Roo.get(document.body).addClass("x-body-masked");
15687             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15688             this._touchViewMask.setStyle('z-index', 10000);
15689             this._touchViewMask.addClass('show');
15690         }
15691         
15692         this.doTouchViewQuery();
15693         
15694     },
15695     
15696     hideTouchView : function()
15697     {
15698         this.touchViewEl.removeClass('in');
15699
15700         if(this.animate){
15701             var _this = this;
15702             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15703         }else{
15704             this.touchViewEl.setStyle('display', 'none');
15705         }
15706         
15707         if(this._touchViewMask){
15708             this._touchViewMask.removeClass('show');
15709             Roo.get(document.body).removeClass("x-body-masked");
15710         }
15711     },
15712     
15713     setTouchViewValue : function()
15714     {
15715         if(this.multiple){
15716             this.clearItem();
15717         
15718             var _this = this;
15719
15720             Roo.each(this.tickItems, function(o){
15721                 this.addItem(o);
15722             }, this);
15723         }
15724         
15725         this.hideTouchView();
15726     },
15727     
15728     doTouchViewQuery : function()
15729     {
15730         var qe = {
15731             query: '',
15732             forceAll: true,
15733             combo: this,
15734             cancel:false
15735         };
15736         
15737         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15738             return false;
15739         }
15740         
15741         if(!this.alwaysQuery || this.mode == 'local'){
15742             this.onTouchViewLoad();
15743             return;
15744         }
15745         
15746         this.store.load();
15747     },
15748     
15749     onTouchViewBeforeLoad : function(combo,opts)
15750     {
15751         return;
15752     },
15753
15754     // private
15755     onTouchViewLoad : function()
15756     {
15757         if(this.store.getCount() < 1){
15758             this.onTouchViewEmptyResults();
15759             return;
15760         }
15761         
15762         this.clearTouchView();
15763         
15764         var rawValue = this.getRawValue();
15765         
15766         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15767         
15768         this.tickItems = [];
15769         
15770         this.store.data.each(function(d, rowIndex){
15771             var row = this.touchViewListGroup.createChild(template);
15772             
15773             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15774                 row.addClass(d.data.cls);
15775             }
15776             
15777             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15778                 var cfg = {
15779                     data : d.data,
15780                     html : d.data[this.displayField]
15781                 };
15782                 
15783                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15784                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15785                 }
15786             }
15787             row.removeClass('selected');
15788             if(!this.multiple && this.valueField &&
15789                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15790             {
15791                 // radio buttons..
15792                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15793                 row.addClass('selected');
15794             }
15795             
15796             if(this.multiple && this.valueField &&
15797                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15798             {
15799                 
15800                 // checkboxes...
15801                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15802                 this.tickItems.push(d.data);
15803             }
15804             
15805             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15806             
15807         }, this);
15808         
15809         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15810         
15811         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15812
15813         if(this.modalTitle.length){
15814             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15815         }
15816
15817         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15818         
15819         if(this.mobile_restrict_height && listHeight < bodyHeight){
15820             this.touchViewBodyEl.setHeight(listHeight);
15821         }
15822         
15823         var _this = this;
15824         
15825         if(firstChecked && listHeight > bodyHeight){
15826             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15827         }
15828         
15829     },
15830     
15831     onTouchViewLoadException : function()
15832     {
15833         this.hideTouchView();
15834     },
15835     
15836     onTouchViewEmptyResults : function()
15837     {
15838         this.clearTouchView();
15839         
15840         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15841         
15842         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15843         
15844     },
15845     
15846     clearTouchView : function()
15847     {
15848         this.touchViewListGroup.dom.innerHTML = '';
15849     },
15850     
15851     onTouchViewClick : function(e, el, o)
15852     {
15853         e.preventDefault();
15854         
15855         var row = o.row;
15856         var rowIndex = o.rowIndex;
15857         
15858         var r = this.store.getAt(rowIndex);
15859         
15860         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15861             
15862             if(!this.multiple){
15863                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15864                     c.dom.removeAttribute('checked');
15865                 }, this);
15866
15867                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15868
15869                 this.setFromData(r.data);
15870
15871                 var close = this.closeTriggerEl();
15872
15873                 if(close){
15874                     close.show();
15875                 }
15876
15877                 this.hideTouchView();
15878
15879                 this.fireEvent('select', this, r, rowIndex);
15880
15881                 return;
15882             }
15883
15884             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15885                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15886                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15887                 return;
15888             }
15889
15890             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15891             this.addItem(r.data);
15892             this.tickItems.push(r.data);
15893         }
15894     },
15895     
15896     getAutoCreateNativeIOS : function()
15897     {
15898         var cfg = {
15899             cls: 'form-group' //input-group,
15900         };
15901         
15902         var combobox =  {
15903             tag: 'select',
15904             cls : 'roo-ios-select'
15905         };
15906         
15907         if (this.name) {
15908             combobox.name = this.name;
15909         }
15910         
15911         if (this.disabled) {
15912             combobox.disabled = true;
15913         }
15914         
15915         var settings = this;
15916         
15917         ['xs','sm','md','lg'].map(function(size){
15918             if (settings[size]) {
15919                 cfg.cls += ' col-' + size + '-' + settings[size];
15920             }
15921         });
15922         
15923         cfg.cn = combobox;
15924         
15925         return cfg;
15926         
15927     },
15928     
15929     initIOSView : function()
15930     {
15931         this.store.on('load', this.onIOSViewLoad, this);
15932         
15933         return;
15934     },
15935     
15936     onIOSViewLoad : function()
15937     {
15938         if(this.store.getCount() < 1){
15939             return;
15940         }
15941         
15942         this.clearIOSView();
15943         
15944         if(this.allowBlank) {
15945             
15946             var default_text = '-- SELECT --';
15947             
15948             if(this.placeholder.length){
15949                 default_text = this.placeholder;
15950             }
15951             
15952             if(this.emptyTitle.length){
15953                 default_text += ' - ' + this.emptyTitle + ' -';
15954             }
15955             
15956             var opt = this.inputEl().createChild({
15957                 tag: 'option',
15958                 value : 0,
15959                 html : default_text
15960             });
15961             
15962             var o = {};
15963             o[this.valueField] = 0;
15964             o[this.displayField] = default_text;
15965             
15966             this.ios_options.push({
15967                 data : o,
15968                 el : opt
15969             });
15970             
15971         }
15972         
15973         this.store.data.each(function(d, rowIndex){
15974             
15975             var html = '';
15976             
15977             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15978                 html = d.data[this.displayField];
15979             }
15980             
15981             var value = '';
15982             
15983             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15984                 value = d.data[this.valueField];
15985             }
15986             
15987             var option = {
15988                 tag: 'option',
15989                 value : value,
15990                 html : html
15991             };
15992             
15993             if(this.value == d.data[this.valueField]){
15994                 option['selected'] = true;
15995             }
15996             
15997             var opt = this.inputEl().createChild(option);
15998             
15999             this.ios_options.push({
16000                 data : d.data,
16001                 el : opt
16002             });
16003             
16004         }, this);
16005         
16006         this.inputEl().on('change', function(){
16007            this.fireEvent('select', this);
16008         }, this);
16009         
16010     },
16011     
16012     clearIOSView: function()
16013     {
16014         this.inputEl().dom.innerHTML = '';
16015         
16016         this.ios_options = [];
16017     },
16018     
16019     setIOSValue: function(v)
16020     {
16021         this.value = v;
16022         
16023         if(!this.ios_options){
16024             return;
16025         }
16026         
16027         Roo.each(this.ios_options, function(opts){
16028            
16029            opts.el.dom.removeAttribute('selected');
16030            
16031            if(opts.data[this.valueField] != v){
16032                return;
16033            }
16034            
16035            opts.el.dom.setAttribute('selected', true);
16036            
16037         }, this);
16038     }
16039
16040     /** 
16041     * @cfg {Boolean} grow 
16042     * @hide 
16043     */
16044     /** 
16045     * @cfg {Number} growMin 
16046     * @hide 
16047     */
16048     /** 
16049     * @cfg {Number} growMax 
16050     * @hide 
16051     */
16052     /**
16053      * @hide
16054      * @method autoSize
16055      */
16056 });
16057
16058 Roo.apply(Roo.bootstrap.ComboBox,  {
16059     
16060     header : {
16061         tag: 'div',
16062         cls: 'modal-header',
16063         cn: [
16064             {
16065                 tag: 'h4',
16066                 cls: 'modal-title'
16067             }
16068         ]
16069     },
16070     
16071     body : {
16072         tag: 'div',
16073         cls: 'modal-body',
16074         cn: [
16075             {
16076                 tag: 'ul',
16077                 cls: 'list-group'
16078             }
16079         ]
16080     },
16081     
16082     listItemRadio : {
16083         tag: 'li',
16084         cls: 'list-group-item',
16085         cn: [
16086             {
16087                 tag: 'span',
16088                 cls: 'roo-combobox-list-group-item-value'
16089             },
16090             {
16091                 tag: 'div',
16092                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
16093                 cn: [
16094                     {
16095                         tag: 'input',
16096                         type: 'radio'
16097                     },
16098                     {
16099                         tag: 'label'
16100                     }
16101                 ]
16102             }
16103         ]
16104     },
16105     
16106     listItemCheckbox : {
16107         tag: 'li',
16108         cls: 'list-group-item',
16109         cn: [
16110             {
16111                 tag: 'span',
16112                 cls: 'roo-combobox-list-group-item-value'
16113             },
16114             {
16115                 tag: 'div',
16116                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
16117                 cn: [
16118                     {
16119                         tag: 'input',
16120                         type: 'checkbox'
16121                     },
16122                     {
16123                         tag: 'label'
16124                     }
16125                 ]
16126             }
16127         ]
16128     },
16129     
16130     emptyResult : {
16131         tag: 'div',
16132         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
16133     },
16134     
16135     footer : {
16136         tag: 'div',
16137         cls: 'modal-footer',
16138         cn: [
16139             {
16140                 tag: 'div',
16141                 cls: 'row',
16142                 cn: [
16143                     {
16144                         tag: 'div',
16145                         cls: 'col-xs-6 text-left',
16146                         cn: {
16147                             tag: 'button',
16148                             cls: 'btn btn-danger roo-touch-view-cancel',
16149                             html: 'Cancel'
16150                         }
16151                     },
16152                     {
16153                         tag: 'div',
16154                         cls: 'col-xs-6 text-right',
16155                         cn: {
16156                             tag: 'button',
16157                             cls: 'btn btn-success roo-touch-view-ok',
16158                             html: 'OK'
16159                         }
16160                     }
16161                 ]
16162             }
16163         ]
16164         
16165     }
16166 });
16167
16168 Roo.apply(Roo.bootstrap.ComboBox,  {
16169     
16170     touchViewTemplate : {
16171         tag: 'div',
16172         cls: 'modal fade roo-combobox-touch-view',
16173         cn: [
16174             {
16175                 tag: 'div',
16176                 cls: 'modal-dialog',
16177                 style : 'position:fixed', // we have to fix position....
16178                 cn: [
16179                     {
16180                         tag: 'div',
16181                         cls: 'modal-content',
16182                         cn: [
16183                             Roo.bootstrap.ComboBox.header,
16184                             Roo.bootstrap.ComboBox.body,
16185                             Roo.bootstrap.ComboBox.footer
16186                         ]
16187                     }
16188                 ]
16189             }
16190         ]
16191     }
16192 });/*
16193  * Based on:
16194  * Ext JS Library 1.1.1
16195  * Copyright(c) 2006-2007, Ext JS, LLC.
16196  *
16197  * Originally Released Under LGPL - original licence link has changed is not relivant.
16198  *
16199  * Fork - LGPL
16200  * <script type="text/javascript">
16201  */
16202
16203 /**
16204  * @class Roo.View
16205  * @extends Roo.util.Observable
16206  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
16207  * This class also supports single and multi selection modes. <br>
16208  * Create a data model bound view:
16209  <pre><code>
16210  var store = new Roo.data.Store(...);
16211
16212  var view = new Roo.View({
16213     el : "my-element",
16214     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
16215  
16216     singleSelect: true,
16217     selectedClass: "ydataview-selected",
16218     store: store
16219  });
16220
16221  // listen for node click?
16222  view.on("click", function(vw, index, node, e){
16223  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16224  });
16225
16226  // load XML data
16227  dataModel.load("foobar.xml");
16228  </code></pre>
16229  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16230  * <br><br>
16231  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16232  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16233  * 
16234  * Note: old style constructor is still suported (container, template, config)
16235  * 
16236  * @constructor
16237  * Create a new View
16238  * @param {Object} config The config object
16239  * 
16240  */
16241 Roo.View = function(config, depreciated_tpl, depreciated_config){
16242     
16243     this.parent = false;
16244     
16245     if (typeof(depreciated_tpl) == 'undefined') {
16246         // new way.. - universal constructor.
16247         Roo.apply(this, config);
16248         this.el  = Roo.get(this.el);
16249     } else {
16250         // old format..
16251         this.el  = Roo.get(config);
16252         this.tpl = depreciated_tpl;
16253         Roo.apply(this, depreciated_config);
16254     }
16255     this.wrapEl  = this.el.wrap().wrap();
16256     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16257     
16258     
16259     if(typeof(this.tpl) == "string"){
16260         this.tpl = new Roo.Template(this.tpl);
16261     } else {
16262         // support xtype ctors..
16263         this.tpl = new Roo.factory(this.tpl, Roo);
16264     }
16265     
16266     
16267     this.tpl.compile();
16268     
16269     /** @private */
16270     this.addEvents({
16271         /**
16272          * @event beforeclick
16273          * Fires before a click is processed. Returns false to cancel the default action.
16274          * @param {Roo.View} this
16275          * @param {Number} index The index of the target node
16276          * @param {HTMLElement} node The target node
16277          * @param {Roo.EventObject} e The raw event object
16278          */
16279             "beforeclick" : true,
16280         /**
16281          * @event click
16282          * Fires when a template node is clicked.
16283          * @param {Roo.View} this
16284          * @param {Number} index The index of the target node
16285          * @param {HTMLElement} node The target node
16286          * @param {Roo.EventObject} e The raw event object
16287          */
16288             "click" : true,
16289         /**
16290          * @event dblclick
16291          * Fires when a template node is double clicked.
16292          * @param {Roo.View} this
16293          * @param {Number} index The index of the target node
16294          * @param {HTMLElement} node The target node
16295          * @param {Roo.EventObject} e The raw event object
16296          */
16297             "dblclick" : true,
16298         /**
16299          * @event contextmenu
16300          * Fires when a template node is right clicked.
16301          * @param {Roo.View} this
16302          * @param {Number} index The index of the target node
16303          * @param {HTMLElement} node The target node
16304          * @param {Roo.EventObject} e The raw event object
16305          */
16306             "contextmenu" : true,
16307         /**
16308          * @event selectionchange
16309          * Fires when the selected nodes change.
16310          * @param {Roo.View} this
16311          * @param {Array} selections Array of the selected nodes
16312          */
16313             "selectionchange" : true,
16314     
16315         /**
16316          * @event beforeselect
16317          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16318          * @param {Roo.View} this
16319          * @param {HTMLElement} node The node to be selected
16320          * @param {Array} selections Array of currently selected nodes
16321          */
16322             "beforeselect" : true,
16323         /**
16324          * @event preparedata
16325          * Fires on every row to render, to allow you to change the data.
16326          * @param {Roo.View} this
16327          * @param {Object} data to be rendered (change this)
16328          */
16329           "preparedata" : true
16330           
16331           
16332         });
16333
16334
16335
16336     this.el.on({
16337         "click": this.onClick,
16338         "dblclick": this.onDblClick,
16339         "contextmenu": this.onContextMenu,
16340         scope:this
16341     });
16342
16343     this.selections = [];
16344     this.nodes = [];
16345     this.cmp = new Roo.CompositeElementLite([]);
16346     if(this.store){
16347         this.store = Roo.factory(this.store, Roo.data);
16348         this.setStore(this.store, true);
16349     }
16350     
16351     if ( this.footer && this.footer.xtype) {
16352            
16353          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16354         
16355         this.footer.dataSource = this.store;
16356         this.footer.container = fctr;
16357         this.footer = Roo.factory(this.footer, Roo);
16358         fctr.insertFirst(this.el);
16359         
16360         // this is a bit insane - as the paging toolbar seems to detach the el..
16361 //        dom.parentNode.parentNode.parentNode
16362          // they get detached?
16363     }
16364     
16365     
16366     Roo.View.superclass.constructor.call(this);
16367     
16368     
16369 };
16370
16371 Roo.extend(Roo.View, Roo.util.Observable, {
16372     
16373      /**
16374      * @cfg {Roo.data.Store} store Data store to load data from.
16375      */
16376     store : false,
16377     
16378     /**
16379      * @cfg {String|Roo.Element} el The container element.
16380      */
16381     el : '',
16382     
16383     /**
16384      * @cfg {String|Roo.Template} tpl The template used by this View 
16385      */
16386     tpl : false,
16387     /**
16388      * @cfg {String} dataName the named area of the template to use as the data area
16389      *                          Works with domtemplates roo-name="name"
16390      */
16391     dataName: false,
16392     /**
16393      * @cfg {String} selectedClass The css class to add to selected nodes
16394      */
16395     selectedClass : "x-view-selected",
16396      /**
16397      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16398      */
16399     emptyText : "",
16400     
16401     /**
16402      * @cfg {String} text to display on mask (default Loading)
16403      */
16404     mask : false,
16405     /**
16406      * @cfg {Boolean} multiSelect Allow multiple selection
16407      */
16408     multiSelect : false,
16409     /**
16410      * @cfg {Boolean} singleSelect Allow single selection
16411      */
16412     singleSelect:  false,
16413     
16414     /**
16415      * @cfg {Boolean} toggleSelect - selecting 
16416      */
16417     toggleSelect : false,
16418     
16419     /**
16420      * @cfg {Boolean} tickable - selecting 
16421      */
16422     tickable : false,
16423     
16424     /**
16425      * Returns the element this view is bound to.
16426      * @return {Roo.Element}
16427      */
16428     getEl : function(){
16429         return this.wrapEl;
16430     },
16431     
16432     
16433
16434     /**
16435      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16436      */
16437     refresh : function(){
16438         //Roo.log('refresh');
16439         var t = this.tpl;
16440         
16441         // if we are using something like 'domtemplate', then
16442         // the what gets used is:
16443         // t.applySubtemplate(NAME, data, wrapping data..)
16444         // the outer template then get' applied with
16445         //     the store 'extra data'
16446         // and the body get's added to the
16447         //      roo-name="data" node?
16448         //      <span class='roo-tpl-{name}'></span> ?????
16449         
16450         
16451         
16452         this.clearSelections();
16453         this.el.update("");
16454         var html = [];
16455         var records = this.store.getRange();
16456         if(records.length < 1) {
16457             
16458             // is this valid??  = should it render a template??
16459             
16460             this.el.update(this.emptyText);
16461             return;
16462         }
16463         var el = this.el;
16464         if (this.dataName) {
16465             this.el.update(t.apply(this.store.meta)); //????
16466             el = this.el.child('.roo-tpl-' + this.dataName);
16467         }
16468         
16469         for(var i = 0, len = records.length; i < len; i++){
16470             var data = this.prepareData(records[i].data, i, records[i]);
16471             this.fireEvent("preparedata", this, data, i, records[i]);
16472             
16473             var d = Roo.apply({}, data);
16474             
16475             if(this.tickable){
16476                 Roo.apply(d, {'roo-id' : Roo.id()});
16477                 
16478                 var _this = this;
16479             
16480                 Roo.each(this.parent.item, function(item){
16481                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16482                         return;
16483                     }
16484                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16485                 });
16486             }
16487             
16488             html[html.length] = Roo.util.Format.trim(
16489                 this.dataName ?
16490                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16491                     t.apply(d)
16492             );
16493         }
16494         
16495         
16496         
16497         el.update(html.join(""));
16498         this.nodes = el.dom.childNodes;
16499         this.updateIndexes(0);
16500     },
16501     
16502
16503     /**
16504      * Function to override to reformat the data that is sent to
16505      * the template for each node.
16506      * DEPRICATED - use the preparedata event handler.
16507      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16508      * a JSON object for an UpdateManager bound view).
16509      */
16510     prepareData : function(data, index, record)
16511     {
16512         this.fireEvent("preparedata", this, data, index, record);
16513         return data;
16514     },
16515
16516     onUpdate : function(ds, record){
16517         // Roo.log('on update');   
16518         this.clearSelections();
16519         var index = this.store.indexOf(record);
16520         var n = this.nodes[index];
16521         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16522         n.parentNode.removeChild(n);
16523         this.updateIndexes(index, index);
16524     },
16525
16526     
16527     
16528 // --------- FIXME     
16529     onAdd : function(ds, records, index)
16530     {
16531         //Roo.log(['on Add', ds, records, index] );        
16532         this.clearSelections();
16533         if(this.nodes.length == 0){
16534             this.refresh();
16535             return;
16536         }
16537         var n = this.nodes[index];
16538         for(var i = 0, len = records.length; i < len; i++){
16539             var d = this.prepareData(records[i].data, i, records[i]);
16540             if(n){
16541                 this.tpl.insertBefore(n, d);
16542             }else{
16543                 
16544                 this.tpl.append(this.el, d);
16545             }
16546         }
16547         this.updateIndexes(index);
16548     },
16549
16550     onRemove : function(ds, record, index){
16551        // Roo.log('onRemove');
16552         this.clearSelections();
16553         var el = this.dataName  ?
16554             this.el.child('.roo-tpl-' + this.dataName) :
16555             this.el; 
16556         
16557         el.dom.removeChild(this.nodes[index]);
16558         this.updateIndexes(index);
16559     },
16560
16561     /**
16562      * Refresh an individual node.
16563      * @param {Number} index
16564      */
16565     refreshNode : function(index){
16566         this.onUpdate(this.store, this.store.getAt(index));
16567     },
16568
16569     updateIndexes : function(startIndex, endIndex){
16570         var ns = this.nodes;
16571         startIndex = startIndex || 0;
16572         endIndex = endIndex || ns.length - 1;
16573         for(var i = startIndex; i <= endIndex; i++){
16574             ns[i].nodeIndex = i;
16575         }
16576     },
16577
16578     /**
16579      * Changes the data store this view uses and refresh the view.
16580      * @param {Store} store
16581      */
16582     setStore : function(store, initial){
16583         if(!initial && this.store){
16584             this.store.un("datachanged", this.refresh);
16585             this.store.un("add", this.onAdd);
16586             this.store.un("remove", this.onRemove);
16587             this.store.un("update", this.onUpdate);
16588             this.store.un("clear", this.refresh);
16589             this.store.un("beforeload", this.onBeforeLoad);
16590             this.store.un("load", this.onLoad);
16591             this.store.un("loadexception", this.onLoad);
16592         }
16593         if(store){
16594           
16595             store.on("datachanged", this.refresh, this);
16596             store.on("add", this.onAdd, this);
16597             store.on("remove", this.onRemove, this);
16598             store.on("update", this.onUpdate, this);
16599             store.on("clear", this.refresh, this);
16600             store.on("beforeload", this.onBeforeLoad, this);
16601             store.on("load", this.onLoad, this);
16602             store.on("loadexception", this.onLoad, this);
16603         }
16604         
16605         if(store){
16606             this.refresh();
16607         }
16608     },
16609     /**
16610      * onbeforeLoad - masks the loading area.
16611      *
16612      */
16613     onBeforeLoad : function(store,opts)
16614     {
16615          //Roo.log('onBeforeLoad');   
16616         if (!opts.add) {
16617             this.el.update("");
16618         }
16619         this.el.mask(this.mask ? this.mask : "Loading" ); 
16620     },
16621     onLoad : function ()
16622     {
16623         this.el.unmask();
16624     },
16625     
16626
16627     /**
16628      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16629      * @param {HTMLElement} node
16630      * @return {HTMLElement} The template node
16631      */
16632     findItemFromChild : function(node){
16633         var el = this.dataName  ?
16634             this.el.child('.roo-tpl-' + this.dataName,true) :
16635             this.el.dom; 
16636         
16637         if(!node || node.parentNode == el){
16638                     return node;
16639             }
16640             var p = node.parentNode;
16641             while(p && p != el){
16642             if(p.parentNode == el){
16643                 return p;
16644             }
16645             p = p.parentNode;
16646         }
16647             return null;
16648     },
16649
16650     /** @ignore */
16651     onClick : function(e){
16652         var item = this.findItemFromChild(e.getTarget());
16653         if(item){
16654             var index = this.indexOf(item);
16655             if(this.onItemClick(item, index, e) !== false){
16656                 this.fireEvent("click", this, index, item, e);
16657             }
16658         }else{
16659             this.clearSelections();
16660         }
16661     },
16662
16663     /** @ignore */
16664     onContextMenu : function(e){
16665         var item = this.findItemFromChild(e.getTarget());
16666         if(item){
16667             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16668         }
16669     },
16670
16671     /** @ignore */
16672     onDblClick : function(e){
16673         var item = this.findItemFromChild(e.getTarget());
16674         if(item){
16675             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16676         }
16677     },
16678
16679     onItemClick : function(item, index, e)
16680     {
16681         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16682             return false;
16683         }
16684         if (this.toggleSelect) {
16685             var m = this.isSelected(item) ? 'unselect' : 'select';
16686             //Roo.log(m);
16687             var _t = this;
16688             _t[m](item, true, false);
16689             return true;
16690         }
16691         if(this.multiSelect || this.singleSelect){
16692             if(this.multiSelect && e.shiftKey && this.lastSelection){
16693                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16694             }else{
16695                 this.select(item, this.multiSelect && e.ctrlKey);
16696                 this.lastSelection = item;
16697             }
16698             
16699             if(!this.tickable){
16700                 e.preventDefault();
16701             }
16702             
16703         }
16704         return true;
16705     },
16706
16707     /**
16708      * Get the number of selected nodes.
16709      * @return {Number}
16710      */
16711     getSelectionCount : function(){
16712         return this.selections.length;
16713     },
16714
16715     /**
16716      * Get the currently selected nodes.
16717      * @return {Array} An array of HTMLElements
16718      */
16719     getSelectedNodes : function(){
16720         return this.selections;
16721     },
16722
16723     /**
16724      * Get the indexes of the selected nodes.
16725      * @return {Array}
16726      */
16727     getSelectedIndexes : function(){
16728         var indexes = [], s = this.selections;
16729         for(var i = 0, len = s.length; i < len; i++){
16730             indexes.push(s[i].nodeIndex);
16731         }
16732         return indexes;
16733     },
16734
16735     /**
16736      * Clear all selections
16737      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16738      */
16739     clearSelections : function(suppressEvent){
16740         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16741             this.cmp.elements = this.selections;
16742             this.cmp.removeClass(this.selectedClass);
16743             this.selections = [];
16744             if(!suppressEvent){
16745                 this.fireEvent("selectionchange", this, this.selections);
16746             }
16747         }
16748     },
16749
16750     /**
16751      * Returns true if the passed node is selected
16752      * @param {HTMLElement/Number} node The node or node index
16753      * @return {Boolean}
16754      */
16755     isSelected : function(node){
16756         var s = this.selections;
16757         if(s.length < 1){
16758             return false;
16759         }
16760         node = this.getNode(node);
16761         return s.indexOf(node) !== -1;
16762     },
16763
16764     /**
16765      * Selects nodes.
16766      * @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
16767      * @param {Boolean} keepExisting (optional) true to keep existing selections
16768      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16769      */
16770     select : function(nodeInfo, keepExisting, suppressEvent){
16771         if(nodeInfo instanceof Array){
16772             if(!keepExisting){
16773                 this.clearSelections(true);
16774             }
16775             for(var i = 0, len = nodeInfo.length; i < len; i++){
16776                 this.select(nodeInfo[i], true, true);
16777             }
16778             return;
16779         } 
16780         var node = this.getNode(nodeInfo);
16781         if(!node || this.isSelected(node)){
16782             return; // already selected.
16783         }
16784         if(!keepExisting){
16785             this.clearSelections(true);
16786         }
16787         
16788         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16789             Roo.fly(node).addClass(this.selectedClass);
16790             this.selections.push(node);
16791             if(!suppressEvent){
16792                 this.fireEvent("selectionchange", this, this.selections);
16793             }
16794         }
16795         
16796         
16797     },
16798       /**
16799      * Unselects nodes.
16800      * @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
16801      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16802      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16803      */
16804     unselect : function(nodeInfo, keepExisting, suppressEvent)
16805     {
16806         if(nodeInfo instanceof Array){
16807             Roo.each(this.selections, function(s) {
16808                 this.unselect(s, nodeInfo);
16809             }, this);
16810             return;
16811         }
16812         var node = this.getNode(nodeInfo);
16813         if(!node || !this.isSelected(node)){
16814             //Roo.log("not selected");
16815             return; // not selected.
16816         }
16817         // fireevent???
16818         var ns = [];
16819         Roo.each(this.selections, function(s) {
16820             if (s == node ) {
16821                 Roo.fly(node).removeClass(this.selectedClass);
16822
16823                 return;
16824             }
16825             ns.push(s);
16826         },this);
16827         
16828         this.selections= ns;
16829         this.fireEvent("selectionchange", this, this.selections);
16830     },
16831
16832     /**
16833      * Gets a template node.
16834      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16835      * @return {HTMLElement} The node or null if it wasn't found
16836      */
16837     getNode : function(nodeInfo){
16838         if(typeof nodeInfo == "string"){
16839             return document.getElementById(nodeInfo);
16840         }else if(typeof nodeInfo == "number"){
16841             return this.nodes[nodeInfo];
16842         }
16843         return nodeInfo;
16844     },
16845
16846     /**
16847      * Gets a range template nodes.
16848      * @param {Number} startIndex
16849      * @param {Number} endIndex
16850      * @return {Array} An array of nodes
16851      */
16852     getNodes : function(start, end){
16853         var ns = this.nodes;
16854         start = start || 0;
16855         end = typeof end == "undefined" ? ns.length - 1 : end;
16856         var nodes = [];
16857         if(start <= end){
16858             for(var i = start; i <= end; i++){
16859                 nodes.push(ns[i]);
16860             }
16861         } else{
16862             for(var i = start; i >= end; i--){
16863                 nodes.push(ns[i]);
16864             }
16865         }
16866         return nodes;
16867     },
16868
16869     /**
16870      * Finds the index of the passed node
16871      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16872      * @return {Number} The index of the node or -1
16873      */
16874     indexOf : function(node){
16875         node = this.getNode(node);
16876         if(typeof node.nodeIndex == "number"){
16877             return node.nodeIndex;
16878         }
16879         var ns = this.nodes;
16880         for(var i = 0, len = ns.length; i < len; i++){
16881             if(ns[i] == node){
16882                 return i;
16883             }
16884         }
16885         return -1;
16886     }
16887 });
16888 /*
16889  * - LGPL
16890  *
16891  * based on jquery fullcalendar
16892  * 
16893  */
16894
16895 Roo.bootstrap = Roo.bootstrap || {};
16896 /**
16897  * @class Roo.bootstrap.Calendar
16898  * @extends Roo.bootstrap.Component
16899  * Bootstrap Calendar class
16900  * @cfg {Boolean} loadMask (true|false) default false
16901  * @cfg {Object} header generate the user specific header of the calendar, default false
16902
16903  * @constructor
16904  * Create a new Container
16905  * @param {Object} config The config object
16906  */
16907
16908
16909
16910 Roo.bootstrap.Calendar = function(config){
16911     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16912      this.addEvents({
16913         /**
16914              * @event select
16915              * Fires when a date is selected
16916              * @param {DatePicker} this
16917              * @param {Date} date The selected date
16918              */
16919         'select': true,
16920         /**
16921              * @event monthchange
16922              * Fires when the displayed month changes 
16923              * @param {DatePicker} this
16924              * @param {Date} date The selected month
16925              */
16926         'monthchange': true,
16927         /**
16928              * @event evententer
16929              * Fires when mouse over an event
16930              * @param {Calendar} this
16931              * @param {event} Event
16932              */
16933         'evententer': true,
16934         /**
16935              * @event eventleave
16936              * Fires when the mouse leaves an
16937              * @param {Calendar} this
16938              * @param {event}
16939              */
16940         'eventleave': true,
16941         /**
16942              * @event eventclick
16943              * Fires when the mouse click an
16944              * @param {Calendar} this
16945              * @param {event}
16946              */
16947         'eventclick': true
16948         
16949     });
16950
16951 };
16952
16953 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16954     
16955      /**
16956      * @cfg {Number} startDay
16957      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16958      */
16959     startDay : 0,
16960     
16961     loadMask : false,
16962     
16963     header : false,
16964       
16965     getAutoCreate : function(){
16966         
16967         
16968         var fc_button = function(name, corner, style, content ) {
16969             return Roo.apply({},{
16970                 tag : 'span',
16971                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16972                          (corner.length ?
16973                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16974                             ''
16975                         ),
16976                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16977                 unselectable: 'on'
16978             });
16979         };
16980         
16981         var header = {};
16982         
16983         if(!this.header){
16984             header = {
16985                 tag : 'table',
16986                 cls : 'fc-header',
16987                 style : 'width:100%',
16988                 cn : [
16989                     {
16990                         tag: 'tr',
16991                         cn : [
16992                             {
16993                                 tag : 'td',
16994                                 cls : 'fc-header-left',
16995                                 cn : [
16996                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16997                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16998                                     { tag: 'span', cls: 'fc-header-space' },
16999                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
17000
17001
17002                                 ]
17003                             },
17004
17005                             {
17006                                 tag : 'td',
17007                                 cls : 'fc-header-center',
17008                                 cn : [
17009                                     {
17010                                         tag: 'span',
17011                                         cls: 'fc-header-title',
17012                                         cn : {
17013                                             tag: 'H2',
17014                                             html : 'month / year'
17015                                         }
17016                                     }
17017
17018                                 ]
17019                             },
17020                             {
17021                                 tag : 'td',
17022                                 cls : 'fc-header-right',
17023                                 cn : [
17024                               /*      fc_button('month', 'left', '', 'month' ),
17025                                     fc_button('week', '', '', 'week' ),
17026                                     fc_button('day', 'right', '', 'day' )
17027                                 */    
17028
17029                                 ]
17030                             }
17031
17032                         ]
17033                     }
17034                 ]
17035             };
17036         }
17037         
17038         header = this.header;
17039         
17040        
17041         var cal_heads = function() {
17042             var ret = [];
17043             // fixme - handle this.
17044             
17045             for (var i =0; i < Date.dayNames.length; i++) {
17046                 var d = Date.dayNames[i];
17047                 ret.push({
17048                     tag: 'th',
17049                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
17050                     html : d.substring(0,3)
17051                 });
17052                 
17053             }
17054             ret[0].cls += ' fc-first';
17055             ret[6].cls += ' fc-last';
17056             return ret;
17057         };
17058         var cal_cell = function(n) {
17059             return  {
17060                 tag: 'td',
17061                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
17062                 cn : [
17063                     {
17064                         cn : [
17065                             {
17066                                 cls: 'fc-day-number',
17067                                 html: 'D'
17068                             },
17069                             {
17070                                 cls: 'fc-day-content',
17071                              
17072                                 cn : [
17073                                      {
17074                                         style: 'position: relative;' // height: 17px;
17075                                     }
17076                                 ]
17077                             }
17078                             
17079                             
17080                         ]
17081                     }
17082                 ]
17083                 
17084             }
17085         };
17086         var cal_rows = function() {
17087             
17088             var ret = [];
17089             for (var r = 0; r < 6; r++) {
17090                 var row= {
17091                     tag : 'tr',
17092                     cls : 'fc-week',
17093                     cn : []
17094                 };
17095                 
17096                 for (var i =0; i < Date.dayNames.length; i++) {
17097                     var d = Date.dayNames[i];
17098                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
17099
17100                 }
17101                 row.cn[0].cls+=' fc-first';
17102                 row.cn[0].cn[0].style = 'min-height:90px';
17103                 row.cn[6].cls+=' fc-last';
17104                 ret.push(row);
17105                 
17106             }
17107             ret[0].cls += ' fc-first';
17108             ret[4].cls += ' fc-prev-last';
17109             ret[5].cls += ' fc-last';
17110             return ret;
17111             
17112         };
17113         
17114         var cal_table = {
17115             tag: 'table',
17116             cls: 'fc-border-separate',
17117             style : 'width:100%',
17118             cellspacing  : 0,
17119             cn : [
17120                 { 
17121                     tag: 'thead',
17122                     cn : [
17123                         { 
17124                             tag: 'tr',
17125                             cls : 'fc-first fc-last',
17126                             cn : cal_heads()
17127                         }
17128                     ]
17129                 },
17130                 { 
17131                     tag: 'tbody',
17132                     cn : cal_rows()
17133                 }
17134                   
17135             ]
17136         };
17137          
17138          var cfg = {
17139             cls : 'fc fc-ltr',
17140             cn : [
17141                 header,
17142                 {
17143                     cls : 'fc-content',
17144                     style : "position: relative;",
17145                     cn : [
17146                         {
17147                             cls : 'fc-view fc-view-month fc-grid',
17148                             style : 'position: relative',
17149                             unselectable : 'on',
17150                             cn : [
17151                                 {
17152                                     cls : 'fc-event-container',
17153                                     style : 'position:absolute;z-index:8;top:0;left:0;'
17154                                 },
17155                                 cal_table
17156                             ]
17157                         }
17158                     ]
17159     
17160                 }
17161            ] 
17162             
17163         };
17164         
17165          
17166         
17167         return cfg;
17168     },
17169     
17170     
17171     initEvents : function()
17172     {
17173         if(!this.store){
17174             throw "can not find store for calendar";
17175         }
17176         
17177         var mark = {
17178             tag: "div",
17179             cls:"x-dlg-mask",
17180             style: "text-align:center",
17181             cn: [
17182                 {
17183                     tag: "div",
17184                     style: "background-color:white;width:50%;margin:250 auto",
17185                     cn: [
17186                         {
17187                             tag: "img",
17188                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
17189                         },
17190                         {
17191                             tag: "span",
17192                             html: "Loading"
17193                         }
17194                         
17195                     ]
17196                 }
17197             ]
17198         };
17199         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17200         
17201         var size = this.el.select('.fc-content', true).first().getSize();
17202         this.maskEl.setSize(size.width, size.height);
17203         this.maskEl.enableDisplayMode("block");
17204         if(!this.loadMask){
17205             this.maskEl.hide();
17206         }
17207         
17208         this.store = Roo.factory(this.store, Roo.data);
17209         this.store.on('load', this.onLoad, this);
17210         this.store.on('beforeload', this.onBeforeLoad, this);
17211         
17212         this.resize();
17213         
17214         this.cells = this.el.select('.fc-day',true);
17215         //Roo.log(this.cells);
17216         this.textNodes = this.el.query('.fc-day-number');
17217         this.cells.addClassOnOver('fc-state-hover');
17218         
17219         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17220         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17221         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17222         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17223         
17224         this.on('monthchange', this.onMonthChange, this);
17225         
17226         this.update(new Date().clearTime());
17227     },
17228     
17229     resize : function() {
17230         var sz  = this.el.getSize();
17231         
17232         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17233         this.el.select('.fc-day-content div',true).setHeight(34);
17234     },
17235     
17236     
17237     // private
17238     showPrevMonth : function(e){
17239         this.update(this.activeDate.add("mo", -1));
17240     },
17241     showToday : function(e){
17242         this.update(new Date().clearTime());
17243     },
17244     // private
17245     showNextMonth : function(e){
17246         this.update(this.activeDate.add("mo", 1));
17247     },
17248
17249     // private
17250     showPrevYear : function(){
17251         this.update(this.activeDate.add("y", -1));
17252     },
17253
17254     // private
17255     showNextYear : function(){
17256         this.update(this.activeDate.add("y", 1));
17257     },
17258
17259     
17260    // private
17261     update : function(date)
17262     {
17263         var vd = this.activeDate;
17264         this.activeDate = date;
17265 //        if(vd && this.el){
17266 //            var t = date.getTime();
17267 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17268 //                Roo.log('using add remove');
17269 //                
17270 //                this.fireEvent('monthchange', this, date);
17271 //                
17272 //                this.cells.removeClass("fc-state-highlight");
17273 //                this.cells.each(function(c){
17274 //                   if(c.dateValue == t){
17275 //                       c.addClass("fc-state-highlight");
17276 //                       setTimeout(function(){
17277 //                            try{c.dom.firstChild.focus();}catch(e){}
17278 //                       }, 50);
17279 //                       return false;
17280 //                   }
17281 //                   return true;
17282 //                });
17283 //                return;
17284 //            }
17285 //        }
17286         
17287         var days = date.getDaysInMonth();
17288         
17289         var firstOfMonth = date.getFirstDateOfMonth();
17290         var startingPos = firstOfMonth.getDay()-this.startDay;
17291         
17292         if(startingPos < this.startDay){
17293             startingPos += 7;
17294         }
17295         
17296         var pm = date.add(Date.MONTH, -1);
17297         var prevStart = pm.getDaysInMonth()-startingPos;
17298 //        
17299         this.cells = this.el.select('.fc-day',true);
17300         this.textNodes = this.el.query('.fc-day-number');
17301         this.cells.addClassOnOver('fc-state-hover');
17302         
17303         var cells = this.cells.elements;
17304         var textEls = this.textNodes;
17305         
17306         Roo.each(cells, function(cell){
17307             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17308         });
17309         
17310         days += startingPos;
17311
17312         // convert everything to numbers so it's fast
17313         var day = 86400000;
17314         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17315         //Roo.log(d);
17316         //Roo.log(pm);
17317         //Roo.log(prevStart);
17318         
17319         var today = new Date().clearTime().getTime();
17320         var sel = date.clearTime().getTime();
17321         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17322         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17323         var ddMatch = this.disabledDatesRE;
17324         var ddText = this.disabledDatesText;
17325         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17326         var ddaysText = this.disabledDaysText;
17327         var format = this.format;
17328         
17329         var setCellClass = function(cal, cell){
17330             cell.row = 0;
17331             cell.events = [];
17332             cell.more = [];
17333             //Roo.log('set Cell Class');
17334             cell.title = "";
17335             var t = d.getTime();
17336             
17337             //Roo.log(d);
17338             
17339             cell.dateValue = t;
17340             if(t == today){
17341                 cell.className += " fc-today";
17342                 cell.className += " fc-state-highlight";
17343                 cell.title = cal.todayText;
17344             }
17345             if(t == sel){
17346                 // disable highlight in other month..
17347                 //cell.className += " fc-state-highlight";
17348                 
17349             }
17350             // disabling
17351             if(t < min) {
17352                 cell.className = " fc-state-disabled";
17353                 cell.title = cal.minText;
17354                 return;
17355             }
17356             if(t > max) {
17357                 cell.className = " fc-state-disabled";
17358                 cell.title = cal.maxText;
17359                 return;
17360             }
17361             if(ddays){
17362                 if(ddays.indexOf(d.getDay()) != -1){
17363                     cell.title = ddaysText;
17364                     cell.className = " fc-state-disabled";
17365                 }
17366             }
17367             if(ddMatch && format){
17368                 var fvalue = d.dateFormat(format);
17369                 if(ddMatch.test(fvalue)){
17370                     cell.title = ddText.replace("%0", fvalue);
17371                     cell.className = " fc-state-disabled";
17372                 }
17373             }
17374             
17375             if (!cell.initialClassName) {
17376                 cell.initialClassName = cell.dom.className;
17377             }
17378             
17379             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17380         };
17381
17382         var i = 0;
17383         
17384         for(; i < startingPos; i++) {
17385             textEls[i].innerHTML = (++prevStart);
17386             d.setDate(d.getDate()+1);
17387             
17388             cells[i].className = "fc-past fc-other-month";
17389             setCellClass(this, cells[i]);
17390         }
17391         
17392         var intDay = 0;
17393         
17394         for(; i < days; i++){
17395             intDay = i - startingPos + 1;
17396             textEls[i].innerHTML = (intDay);
17397             d.setDate(d.getDate()+1);
17398             
17399             cells[i].className = ''; // "x-date-active";
17400             setCellClass(this, cells[i]);
17401         }
17402         var extraDays = 0;
17403         
17404         for(; i < 42; i++) {
17405             textEls[i].innerHTML = (++extraDays);
17406             d.setDate(d.getDate()+1);
17407             
17408             cells[i].className = "fc-future fc-other-month";
17409             setCellClass(this, cells[i]);
17410         }
17411         
17412         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17413         
17414         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17415         
17416         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17417         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17418         
17419         if(totalRows != 6){
17420             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17421             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17422         }
17423         
17424         this.fireEvent('monthchange', this, date);
17425         
17426         
17427         /*
17428         if(!this.internalRender){
17429             var main = this.el.dom.firstChild;
17430             var w = main.offsetWidth;
17431             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17432             Roo.fly(main).setWidth(w);
17433             this.internalRender = true;
17434             // opera does not respect the auto grow header center column
17435             // then, after it gets a width opera refuses to recalculate
17436             // without a second pass
17437             if(Roo.isOpera && !this.secondPass){
17438                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17439                 this.secondPass = true;
17440                 this.update.defer(10, this, [date]);
17441             }
17442         }
17443         */
17444         
17445     },
17446     
17447     findCell : function(dt) {
17448         dt = dt.clearTime().getTime();
17449         var ret = false;
17450         this.cells.each(function(c){
17451             //Roo.log("check " +c.dateValue + '?=' + dt);
17452             if(c.dateValue == dt){
17453                 ret = c;
17454                 return false;
17455             }
17456             return true;
17457         });
17458         
17459         return ret;
17460     },
17461     
17462     findCells : function(ev) {
17463         var s = ev.start.clone().clearTime().getTime();
17464        // Roo.log(s);
17465         var e= ev.end.clone().clearTime().getTime();
17466        // Roo.log(e);
17467         var ret = [];
17468         this.cells.each(function(c){
17469              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17470             
17471             if(c.dateValue > e){
17472                 return ;
17473             }
17474             if(c.dateValue < s){
17475                 return ;
17476             }
17477             ret.push(c);
17478         });
17479         
17480         return ret;    
17481     },
17482     
17483 //    findBestRow: function(cells)
17484 //    {
17485 //        var ret = 0;
17486 //        
17487 //        for (var i =0 ; i < cells.length;i++) {
17488 //            ret  = Math.max(cells[i].rows || 0,ret);
17489 //        }
17490 //        return ret;
17491 //        
17492 //    },
17493     
17494     
17495     addItem : function(ev)
17496     {
17497         // look for vertical location slot in
17498         var cells = this.findCells(ev);
17499         
17500 //        ev.row = this.findBestRow(cells);
17501         
17502         // work out the location.
17503         
17504         var crow = false;
17505         var rows = [];
17506         for(var i =0; i < cells.length; i++) {
17507             
17508             cells[i].row = cells[0].row;
17509             
17510             if(i == 0){
17511                 cells[i].row = cells[i].row + 1;
17512             }
17513             
17514             if (!crow) {
17515                 crow = {
17516                     start : cells[i],
17517                     end :  cells[i]
17518                 };
17519                 continue;
17520             }
17521             if (crow.start.getY() == cells[i].getY()) {
17522                 // on same row.
17523                 crow.end = cells[i];
17524                 continue;
17525             }
17526             // different row.
17527             rows.push(crow);
17528             crow = {
17529                 start: cells[i],
17530                 end : cells[i]
17531             };
17532             
17533         }
17534         
17535         rows.push(crow);
17536         ev.els = [];
17537         ev.rows = rows;
17538         ev.cells = cells;
17539         
17540         cells[0].events.push(ev);
17541         
17542         this.calevents.push(ev);
17543     },
17544     
17545     clearEvents: function() {
17546         
17547         if(!this.calevents){
17548             return;
17549         }
17550         
17551         Roo.each(this.cells.elements, function(c){
17552             c.row = 0;
17553             c.events = [];
17554             c.more = [];
17555         });
17556         
17557         Roo.each(this.calevents, function(e) {
17558             Roo.each(e.els, function(el) {
17559                 el.un('mouseenter' ,this.onEventEnter, this);
17560                 el.un('mouseleave' ,this.onEventLeave, this);
17561                 el.remove();
17562             },this);
17563         },this);
17564         
17565         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17566             e.remove();
17567         });
17568         
17569     },
17570     
17571     renderEvents: function()
17572     {   
17573         var _this = this;
17574         
17575         this.cells.each(function(c) {
17576             
17577             if(c.row < 5){
17578                 return;
17579             }
17580             
17581             var ev = c.events;
17582             
17583             var r = 4;
17584             if(c.row != c.events.length){
17585                 r = 4 - (4 - (c.row - c.events.length));
17586             }
17587             
17588             c.events = ev.slice(0, r);
17589             c.more = ev.slice(r);
17590             
17591             if(c.more.length && c.more.length == 1){
17592                 c.events.push(c.more.pop());
17593             }
17594             
17595             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17596             
17597         });
17598             
17599         this.cells.each(function(c) {
17600             
17601             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17602             
17603             
17604             for (var e = 0; e < c.events.length; e++){
17605                 var ev = c.events[e];
17606                 var rows = ev.rows;
17607                 
17608                 for(var i = 0; i < rows.length; i++) {
17609                 
17610                     // how many rows should it span..
17611
17612                     var  cfg = {
17613                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17614                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17615
17616                         unselectable : "on",
17617                         cn : [
17618                             {
17619                                 cls: 'fc-event-inner',
17620                                 cn : [
17621     //                                {
17622     //                                  tag:'span',
17623     //                                  cls: 'fc-event-time',
17624     //                                  html : cells.length > 1 ? '' : ev.time
17625     //                                },
17626                                     {
17627                                       tag:'span',
17628                                       cls: 'fc-event-title',
17629                                       html : String.format('{0}', ev.title)
17630                                     }
17631
17632
17633                                 ]
17634                             },
17635                             {
17636                                 cls: 'ui-resizable-handle ui-resizable-e',
17637                                 html : '&nbsp;&nbsp;&nbsp'
17638                             }
17639
17640                         ]
17641                     };
17642
17643                     if (i == 0) {
17644                         cfg.cls += ' fc-event-start';
17645                     }
17646                     if ((i+1) == rows.length) {
17647                         cfg.cls += ' fc-event-end';
17648                     }
17649
17650                     var ctr = _this.el.select('.fc-event-container',true).first();
17651                     var cg = ctr.createChild(cfg);
17652
17653                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17654                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17655
17656                     var r = (c.more.length) ? 1 : 0;
17657                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17658                     cg.setWidth(ebox.right - sbox.x -2);
17659
17660                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17661                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17662                     cg.on('click', _this.onEventClick, _this, ev);
17663
17664                     ev.els.push(cg);
17665                     
17666                 }
17667                 
17668             }
17669             
17670             
17671             if(c.more.length){
17672                 var  cfg = {
17673                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17674                     style : 'position: absolute',
17675                     unselectable : "on",
17676                     cn : [
17677                         {
17678                             cls: 'fc-event-inner',
17679                             cn : [
17680                                 {
17681                                   tag:'span',
17682                                   cls: 'fc-event-title',
17683                                   html : 'More'
17684                                 }
17685
17686
17687                             ]
17688                         },
17689                         {
17690                             cls: 'ui-resizable-handle ui-resizable-e',
17691                             html : '&nbsp;&nbsp;&nbsp'
17692                         }
17693
17694                     ]
17695                 };
17696
17697                 var ctr = _this.el.select('.fc-event-container',true).first();
17698                 var cg = ctr.createChild(cfg);
17699
17700                 var sbox = c.select('.fc-day-content',true).first().getBox();
17701                 var ebox = c.select('.fc-day-content',true).first().getBox();
17702                 //Roo.log(cg);
17703                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17704                 cg.setWidth(ebox.right - sbox.x -2);
17705
17706                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17707                 
17708             }
17709             
17710         });
17711         
17712         
17713         
17714     },
17715     
17716     onEventEnter: function (e, el,event,d) {
17717         this.fireEvent('evententer', this, el, event);
17718     },
17719     
17720     onEventLeave: function (e, el,event,d) {
17721         this.fireEvent('eventleave', this, el, event);
17722     },
17723     
17724     onEventClick: function (e, el,event,d) {
17725         this.fireEvent('eventclick', this, el, event);
17726     },
17727     
17728     onMonthChange: function () {
17729         this.store.load();
17730     },
17731     
17732     onMoreEventClick: function(e, el, more)
17733     {
17734         var _this = this;
17735         
17736         this.calpopover.placement = 'right';
17737         this.calpopover.setTitle('More');
17738         
17739         this.calpopover.setContent('');
17740         
17741         var ctr = this.calpopover.el.select('.popover-content', true).first();
17742         
17743         Roo.each(more, function(m){
17744             var cfg = {
17745                 cls : 'fc-event-hori fc-event-draggable',
17746                 html : m.title
17747             };
17748             var cg = ctr.createChild(cfg);
17749             
17750             cg.on('click', _this.onEventClick, _this, m);
17751         });
17752         
17753         this.calpopover.show(el);
17754         
17755         
17756     },
17757     
17758     onLoad: function () 
17759     {   
17760         this.calevents = [];
17761         var cal = this;
17762         
17763         if(this.store.getCount() > 0){
17764             this.store.data.each(function(d){
17765                cal.addItem({
17766                     id : d.data.id,
17767                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17768                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17769                     time : d.data.start_time,
17770                     title : d.data.title,
17771                     description : d.data.description,
17772                     venue : d.data.venue
17773                 });
17774             });
17775         }
17776         
17777         this.renderEvents();
17778         
17779         if(this.calevents.length && this.loadMask){
17780             this.maskEl.hide();
17781         }
17782     },
17783     
17784     onBeforeLoad: function()
17785     {
17786         this.clearEvents();
17787         if(this.loadMask){
17788             this.maskEl.show();
17789         }
17790     }
17791 });
17792
17793  
17794  /*
17795  * - LGPL
17796  *
17797  * element
17798  * 
17799  */
17800
17801 /**
17802  * @class Roo.bootstrap.Popover
17803  * @extends Roo.bootstrap.Component
17804  * Bootstrap Popover class
17805  * @cfg {String} html contents of the popover   (or false to use children..)
17806  * @cfg {String} title of popover (or false to hide)
17807  * @cfg {String} placement how it is placed
17808  * @cfg {String} trigger click || hover (or false to trigger manually)
17809  * @cfg {String} over what (parent or false to trigger manually.)
17810  * @cfg {Number} delay - delay before showing
17811  
17812  * @constructor
17813  * Create a new Popover
17814  * @param {Object} config The config object
17815  */
17816
17817 Roo.bootstrap.Popover = function(config){
17818     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17819     
17820     this.addEvents({
17821         // raw events
17822          /**
17823          * @event show
17824          * After the popover show
17825          * 
17826          * @param {Roo.bootstrap.Popover} this
17827          */
17828         "show" : true,
17829         /**
17830          * @event hide
17831          * After the popover hide
17832          * 
17833          * @param {Roo.bootstrap.Popover} this
17834          */
17835         "hide" : true
17836     });
17837 };
17838
17839 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17840     
17841     title: 'Fill in a title',
17842     html: false,
17843     
17844     placement : 'right',
17845     trigger : 'hover', // hover
17846     
17847     delay : 0,
17848     
17849     over: 'parent',
17850     
17851     can_build_overlaid : false,
17852     
17853     getChildContainer : function()
17854     {
17855         return this.el.select('.popover-content',true).first();
17856     },
17857     
17858     getAutoCreate : function(){
17859          
17860         var cfg = {
17861            cls : 'popover roo-dynamic',
17862            style: 'display:block',
17863            cn : [
17864                 {
17865                     cls : 'arrow'
17866                 },
17867                 {
17868                     cls : 'popover-inner',
17869                     cn : [
17870                         {
17871                             tag: 'h3',
17872                             cls: 'popover-title popover-header',
17873                             html : this.title
17874                         },
17875                         {
17876                             cls : 'popover-content popover-body',
17877                             html : this.html
17878                         }
17879                     ]
17880                     
17881                 }
17882            ]
17883         };
17884         
17885         return cfg;
17886     },
17887     setTitle: function(str)
17888     {
17889         this.title = str;
17890         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17891     },
17892     setContent: function(str)
17893     {
17894         this.html = str;
17895         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17896     },
17897     // as it get's added to the bottom of the page.
17898     onRender : function(ct, position)
17899     {
17900         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17901         if(!this.el){
17902             var cfg = Roo.apply({},  this.getAutoCreate());
17903             cfg.id = Roo.id();
17904             
17905             if (this.cls) {
17906                 cfg.cls += ' ' + this.cls;
17907             }
17908             if (this.style) {
17909                 cfg.style = this.style;
17910             }
17911             //Roo.log("adding to ");
17912             this.el = Roo.get(document.body).createChild(cfg, position);
17913 //            Roo.log(this.el);
17914         }
17915         this.initEvents();
17916     },
17917     
17918     initEvents : function()
17919     {
17920         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17921         this.el.enableDisplayMode('block');
17922         this.el.hide();
17923         if (this.over === false) {
17924             return; 
17925         }
17926         if (this.triggers === false) {
17927             return;
17928         }
17929         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17930         var triggers = this.trigger ? this.trigger.split(' ') : [];
17931         Roo.each(triggers, function(trigger) {
17932         
17933             if (trigger == 'click') {
17934                 on_el.on('click', this.toggle, this);
17935             } else if (trigger != 'manual') {
17936                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17937                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17938       
17939                 on_el.on(eventIn  ,this.enter, this);
17940                 on_el.on(eventOut, this.leave, this);
17941             }
17942         }, this);
17943         
17944     },
17945     
17946     
17947     // private
17948     timeout : null,
17949     hoverState : null,
17950     
17951     toggle : function () {
17952         this.hoverState == 'in' ? this.leave() : this.enter();
17953     },
17954     
17955     enter : function () {
17956         
17957         clearTimeout(this.timeout);
17958     
17959         this.hoverState = 'in';
17960     
17961         if (!this.delay || !this.delay.show) {
17962             this.show();
17963             return;
17964         }
17965         var _t = this;
17966         this.timeout = setTimeout(function () {
17967             if (_t.hoverState == 'in') {
17968                 _t.show();
17969             }
17970         }, this.delay.show)
17971     },
17972     
17973     leave : function() {
17974         clearTimeout(this.timeout);
17975     
17976         this.hoverState = 'out';
17977     
17978         if (!this.delay || !this.delay.hide) {
17979             this.hide();
17980             return;
17981         }
17982         var _t = this;
17983         this.timeout = setTimeout(function () {
17984             if (_t.hoverState == 'out') {
17985                 _t.hide();
17986             }
17987         }, this.delay.hide)
17988     },
17989     
17990     show : function (on_el)
17991     {
17992         if (!on_el) {
17993             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17994         }
17995         
17996         // set content.
17997         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17998         if (this.html !== false) {
17999             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
18000         }
18001         this.el.removeClass([
18002             'fade','top','bottom', 'left', 'right','in',
18003             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
18004         ]);
18005         if (!this.title.length) {
18006             this.el.select('.popover-title',true).hide();
18007         }
18008         
18009         var placement = typeof this.placement == 'function' ?
18010             this.placement.call(this, this.el, on_el) :
18011             this.placement;
18012             
18013         var autoToken = /\s?auto?\s?/i;
18014         var autoPlace = autoToken.test(placement);
18015         if (autoPlace) {
18016             placement = placement.replace(autoToken, '') || 'top';
18017         }
18018         
18019         //this.el.detach()
18020         //this.el.setXY([0,0]);
18021         this.el.show();
18022         this.el.dom.style.display='block';
18023         this.el.addClass(placement);
18024         
18025         //this.el.appendTo(on_el);
18026         
18027         var p = this.getPosition();
18028         var box = this.el.getBox();
18029         
18030         if (autoPlace) {
18031             // fixme..
18032         }
18033         var align = Roo.bootstrap.Popover.alignment[placement];
18034         
18035 //        Roo.log(align);
18036         this.el.alignTo(on_el, align[0],align[1]);
18037         //var arrow = this.el.select('.arrow',true).first();
18038         //arrow.set(align[2], 
18039         
18040         this.el.addClass('in');
18041         
18042         
18043         if (this.el.hasClass('fade')) {
18044             // fade it?
18045         }
18046         
18047         this.hoverState = 'in';
18048         
18049         this.fireEvent('show', this);
18050         
18051     },
18052     hide : function()
18053     {
18054         this.el.setXY([0,0]);
18055         this.el.removeClass('in');
18056         this.el.hide();
18057         this.hoverState = null;
18058         
18059         this.fireEvent('hide', this);
18060     }
18061     
18062 });
18063
18064 Roo.bootstrap.Popover.alignment = {
18065     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
18066     'right' : ['l-r', [10,0], 'left bs-popover-left'],
18067     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
18068     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
18069 };
18070
18071  /*
18072  * - LGPL
18073  *
18074  * Progress
18075  * 
18076  */
18077
18078 /**
18079  * @class Roo.bootstrap.Progress
18080  * @extends Roo.bootstrap.Component
18081  * Bootstrap Progress class
18082  * @cfg {Boolean} striped striped of the progress bar
18083  * @cfg {Boolean} active animated of the progress bar
18084  * 
18085  * 
18086  * @constructor
18087  * Create a new Progress
18088  * @param {Object} config The config object
18089  */
18090
18091 Roo.bootstrap.Progress = function(config){
18092     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
18093 };
18094
18095 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
18096     
18097     striped : false,
18098     active: false,
18099     
18100     getAutoCreate : function(){
18101         var cfg = {
18102             tag: 'div',
18103             cls: 'progress'
18104         };
18105         
18106         
18107         if(this.striped){
18108             cfg.cls += ' progress-striped';
18109         }
18110       
18111         if(this.active){
18112             cfg.cls += ' active';
18113         }
18114         
18115         
18116         return cfg;
18117     }
18118    
18119 });
18120
18121  
18122
18123  /*
18124  * - LGPL
18125  *
18126  * ProgressBar
18127  * 
18128  */
18129
18130 /**
18131  * @class Roo.bootstrap.ProgressBar
18132  * @extends Roo.bootstrap.Component
18133  * Bootstrap ProgressBar class
18134  * @cfg {Number} aria_valuenow aria-value now
18135  * @cfg {Number} aria_valuemin aria-value min
18136  * @cfg {Number} aria_valuemax aria-value max
18137  * @cfg {String} label label for the progress bar
18138  * @cfg {String} panel (success | info | warning | danger )
18139  * @cfg {String} role role of the progress bar
18140  * @cfg {String} sr_only text
18141  * 
18142  * 
18143  * @constructor
18144  * Create a new ProgressBar
18145  * @param {Object} config The config object
18146  */
18147
18148 Roo.bootstrap.ProgressBar = function(config){
18149     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
18150 };
18151
18152 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
18153     
18154     aria_valuenow : 0,
18155     aria_valuemin : 0,
18156     aria_valuemax : 100,
18157     label : false,
18158     panel : false,
18159     role : false,
18160     sr_only: false,
18161     
18162     getAutoCreate : function()
18163     {
18164         
18165         var cfg = {
18166             tag: 'div',
18167             cls: 'progress-bar',
18168             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18169         };
18170         
18171         if(this.sr_only){
18172             cfg.cn = {
18173                 tag: 'span',
18174                 cls: 'sr-only',
18175                 html: this.sr_only
18176             }
18177         }
18178         
18179         if(this.role){
18180             cfg.role = this.role;
18181         }
18182         
18183         if(this.aria_valuenow){
18184             cfg['aria-valuenow'] = this.aria_valuenow;
18185         }
18186         
18187         if(this.aria_valuemin){
18188             cfg['aria-valuemin'] = this.aria_valuemin;
18189         }
18190         
18191         if(this.aria_valuemax){
18192             cfg['aria-valuemax'] = this.aria_valuemax;
18193         }
18194         
18195         if(this.label && !this.sr_only){
18196             cfg.html = this.label;
18197         }
18198         
18199         if(this.panel){
18200             cfg.cls += ' progress-bar-' + this.panel;
18201         }
18202         
18203         return cfg;
18204     },
18205     
18206     update : function(aria_valuenow)
18207     {
18208         this.aria_valuenow = aria_valuenow;
18209         
18210         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18211     }
18212    
18213 });
18214
18215  
18216
18217  /*
18218  * - LGPL
18219  *
18220  * column
18221  * 
18222  */
18223
18224 /**
18225  * @class Roo.bootstrap.TabGroup
18226  * @extends Roo.bootstrap.Column
18227  * Bootstrap Column class
18228  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18229  * @cfg {Boolean} carousel true to make the group behave like a carousel
18230  * @cfg {Boolean} bullets show bullets for the panels
18231  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18232  * @cfg {Number} timer auto slide timer .. default 0 millisecond
18233  * @cfg {Boolean} showarrow (true|false) show arrow default true
18234  * 
18235  * @constructor
18236  * Create a new TabGroup
18237  * @param {Object} config The config object
18238  */
18239
18240 Roo.bootstrap.TabGroup = function(config){
18241     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18242     if (!this.navId) {
18243         this.navId = Roo.id();
18244     }
18245     this.tabs = [];
18246     Roo.bootstrap.TabGroup.register(this);
18247     
18248 };
18249
18250 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
18251     
18252     carousel : false,
18253     transition : false,
18254     bullets : 0,
18255     timer : 0,
18256     autoslide : false,
18257     slideFn : false,
18258     slideOnTouch : false,
18259     showarrow : true,
18260     
18261     getAutoCreate : function()
18262     {
18263         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18264         
18265         cfg.cls += ' tab-content';
18266         
18267         if (this.carousel) {
18268             cfg.cls += ' carousel slide';
18269             
18270             cfg.cn = [{
18271                cls : 'carousel-inner',
18272                cn : []
18273             }];
18274         
18275             if(this.bullets  && !Roo.isTouch){
18276                 
18277                 var bullets = {
18278                     cls : 'carousel-bullets',
18279                     cn : []
18280                 };
18281                
18282                 if(this.bullets_cls){
18283                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18284                 }
18285                 
18286                 bullets.cn.push({
18287                     cls : 'clear'
18288                 });
18289                 
18290                 cfg.cn[0].cn.push(bullets);
18291             }
18292             
18293             if(this.showarrow){
18294                 cfg.cn[0].cn.push({
18295                     tag : 'div',
18296                     class : 'carousel-arrow',
18297                     cn : [
18298                         {
18299                             tag : 'div',
18300                             class : 'carousel-prev',
18301                             cn : [
18302                                 {
18303                                     tag : 'i',
18304                                     class : 'fa fa-chevron-left'
18305                                 }
18306                             ]
18307                         },
18308                         {
18309                             tag : 'div',
18310                             class : 'carousel-next',
18311                             cn : [
18312                                 {
18313                                     tag : 'i',
18314                                     class : 'fa fa-chevron-right'
18315                                 }
18316                             ]
18317                         }
18318                     ]
18319                 });
18320             }
18321             
18322         }
18323         
18324         return cfg;
18325     },
18326     
18327     initEvents:  function()
18328     {
18329 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18330 //            this.el.on("touchstart", this.onTouchStart, this);
18331 //        }
18332         
18333         if(this.autoslide){
18334             var _this = this;
18335             
18336             this.slideFn = window.setInterval(function() {
18337                 _this.showPanelNext();
18338             }, this.timer);
18339         }
18340         
18341         if(this.showarrow){
18342             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18343             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18344         }
18345         
18346         
18347     },
18348     
18349 //    onTouchStart : function(e, el, o)
18350 //    {
18351 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18352 //            return;
18353 //        }
18354 //        
18355 //        this.showPanelNext();
18356 //    },
18357     
18358     
18359     getChildContainer : function()
18360     {
18361         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18362     },
18363     
18364     /**
18365     * register a Navigation item
18366     * @param {Roo.bootstrap.NavItem} the navitem to add
18367     */
18368     register : function(item)
18369     {
18370         this.tabs.push( item);
18371         item.navId = this.navId; // not really needed..
18372         this.addBullet();
18373     
18374     },
18375     
18376     getActivePanel : function()
18377     {
18378         var r = false;
18379         Roo.each(this.tabs, function(t) {
18380             if (t.active) {
18381                 r = t;
18382                 return false;
18383             }
18384             return null;
18385         });
18386         return r;
18387         
18388     },
18389     getPanelByName : function(n)
18390     {
18391         var r = false;
18392         Roo.each(this.tabs, function(t) {
18393             if (t.tabId == n) {
18394                 r = t;
18395                 return false;
18396             }
18397             return null;
18398         });
18399         return r;
18400     },
18401     indexOfPanel : function(p)
18402     {
18403         var r = false;
18404         Roo.each(this.tabs, function(t,i) {
18405             if (t.tabId == p.tabId) {
18406                 r = i;
18407                 return false;
18408             }
18409             return null;
18410         });
18411         return r;
18412     },
18413     /**
18414      * show a specific panel
18415      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18416      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18417      */
18418     showPanel : function (pan)
18419     {
18420         if(this.transition || typeof(pan) == 'undefined'){
18421             Roo.log("waiting for the transitionend");
18422             return false;
18423         }
18424         
18425         if (typeof(pan) == 'number') {
18426             pan = this.tabs[pan];
18427         }
18428         
18429         if (typeof(pan) == 'string') {
18430             pan = this.getPanelByName(pan);
18431         }
18432         
18433         var cur = this.getActivePanel();
18434         
18435         if(!pan || !cur){
18436             Roo.log('pan or acitve pan is undefined');
18437             return false;
18438         }
18439         
18440         if (pan.tabId == this.getActivePanel().tabId) {
18441             return true;
18442         }
18443         
18444         if (false === cur.fireEvent('beforedeactivate')) {
18445             return false;
18446         }
18447         
18448         if(this.bullets > 0 && !Roo.isTouch){
18449             this.setActiveBullet(this.indexOfPanel(pan));
18450         }
18451         
18452         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18453             
18454             //class="carousel-item carousel-item-next carousel-item-left"
18455             
18456             this.transition = true;
18457             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18458             var lr = dir == 'next' ? 'left' : 'right';
18459             pan.el.addClass(dir); // or prev
18460             pan.el.addClass('carousel-item-' + dir); // or prev
18461             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18462             cur.el.addClass(lr); // or right
18463             pan.el.addClass(lr);
18464             cur.el.addClass('carousel-item-' +lr); // or right
18465             pan.el.addClass('carousel-item-' +lr);
18466             
18467             
18468             var _this = this;
18469             cur.el.on('transitionend', function() {
18470                 Roo.log("trans end?");
18471                 
18472                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
18473                 pan.setActive(true);
18474                 
18475                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
18476                 cur.setActive(false);
18477                 
18478                 _this.transition = false;
18479                 
18480             }, this, { single:  true } );
18481             
18482             return true;
18483         }
18484         
18485         cur.setActive(false);
18486         pan.setActive(true);
18487         
18488         return true;
18489         
18490     },
18491     showPanelNext : function()
18492     {
18493         var i = this.indexOfPanel(this.getActivePanel());
18494         
18495         if (i >= this.tabs.length - 1 && !this.autoslide) {
18496             return;
18497         }
18498         
18499         if (i >= this.tabs.length - 1 && this.autoslide) {
18500             i = -1;
18501         }
18502         
18503         this.showPanel(this.tabs[i+1]);
18504     },
18505     
18506     showPanelPrev : function()
18507     {
18508         var i = this.indexOfPanel(this.getActivePanel());
18509         
18510         if (i  < 1 && !this.autoslide) {
18511             return;
18512         }
18513         
18514         if (i < 1 && this.autoslide) {
18515             i = this.tabs.length;
18516         }
18517         
18518         this.showPanel(this.tabs[i-1]);
18519     },
18520     
18521     
18522     addBullet: function()
18523     {
18524         if(!this.bullets || Roo.isTouch){
18525             return;
18526         }
18527         var ctr = this.el.select('.carousel-bullets',true).first();
18528         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18529         var bullet = ctr.createChild({
18530             cls : 'bullet bullet-' + i
18531         },ctr.dom.lastChild);
18532         
18533         
18534         var _this = this;
18535         
18536         bullet.on('click', (function(e, el, o, ii, t){
18537
18538             e.preventDefault();
18539
18540             this.showPanel(ii);
18541
18542             if(this.autoslide && this.slideFn){
18543                 clearInterval(this.slideFn);
18544                 this.slideFn = window.setInterval(function() {
18545                     _this.showPanelNext();
18546                 }, this.timer);
18547             }
18548
18549         }).createDelegate(this, [i, bullet], true));
18550                 
18551         
18552     },
18553      
18554     setActiveBullet : function(i)
18555     {
18556         if(Roo.isTouch){
18557             return;
18558         }
18559         
18560         Roo.each(this.el.select('.bullet', true).elements, function(el){
18561             el.removeClass('selected');
18562         });
18563
18564         var bullet = this.el.select('.bullet-' + i, true).first();
18565         
18566         if(!bullet){
18567             return;
18568         }
18569         
18570         bullet.addClass('selected');
18571     }
18572     
18573     
18574   
18575 });
18576
18577  
18578
18579  
18580  
18581 Roo.apply(Roo.bootstrap.TabGroup, {
18582     
18583     groups: {},
18584      /**
18585     * register a Navigation Group
18586     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18587     */
18588     register : function(navgrp)
18589     {
18590         this.groups[navgrp.navId] = navgrp;
18591         
18592     },
18593     /**
18594     * fetch a Navigation Group based on the navigation ID
18595     * if one does not exist , it will get created.
18596     * @param {string} the navgroup to add
18597     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18598     */
18599     get: function(navId) {
18600         if (typeof(this.groups[navId]) == 'undefined') {
18601             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18602         }
18603         return this.groups[navId] ;
18604     }
18605     
18606     
18607     
18608 });
18609
18610  /*
18611  * - LGPL
18612  *
18613  * TabPanel
18614  * 
18615  */
18616
18617 /**
18618  * @class Roo.bootstrap.TabPanel
18619  * @extends Roo.bootstrap.Component
18620  * Bootstrap TabPanel class
18621  * @cfg {Boolean} active panel active
18622  * @cfg {String} html panel content
18623  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18624  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18625  * @cfg {String} href click to link..
18626  * 
18627  * 
18628  * @constructor
18629  * Create a new TabPanel
18630  * @param {Object} config The config object
18631  */
18632
18633 Roo.bootstrap.TabPanel = function(config){
18634     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18635     this.addEvents({
18636         /**
18637              * @event changed
18638              * Fires when the active status changes
18639              * @param {Roo.bootstrap.TabPanel} this
18640              * @param {Boolean} state the new state
18641             
18642          */
18643         'changed': true,
18644         /**
18645              * @event beforedeactivate
18646              * Fires before a tab is de-activated - can be used to do validation on a form.
18647              * @param {Roo.bootstrap.TabPanel} this
18648              * @return {Boolean} false if there is an error
18649             
18650          */
18651         'beforedeactivate': true
18652      });
18653     
18654     this.tabId = this.tabId || Roo.id();
18655   
18656 };
18657
18658 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18659     
18660     active: false,
18661     html: false,
18662     tabId: false,
18663     navId : false,
18664     href : '',
18665     
18666     getAutoCreate : function(){
18667         
18668         
18669         var cfg = {
18670             tag: 'div',
18671             // item is needed for carousel - not sure if it has any effect otherwise
18672             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18673             html: this.html || ''
18674         };
18675         
18676         if(this.active){
18677             cfg.cls += ' active';
18678         }
18679         
18680         if(this.tabId){
18681             cfg.tabId = this.tabId;
18682         }
18683         
18684         
18685         
18686         return cfg;
18687     },
18688     
18689     initEvents:  function()
18690     {
18691         var p = this.parent();
18692         
18693         this.navId = this.navId || p.navId;
18694         
18695         if (typeof(this.navId) != 'undefined') {
18696             // not really needed.. but just in case.. parent should be a NavGroup.
18697             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18698             
18699             tg.register(this);
18700             
18701             var i = tg.tabs.length - 1;
18702             
18703             if(this.active && tg.bullets > 0 && i < tg.bullets){
18704                 tg.setActiveBullet(i);
18705             }
18706         }
18707         
18708         this.el.on('click', this.onClick, this);
18709         
18710         if(Roo.isTouch){
18711             this.el.on("touchstart", this.onTouchStart, this);
18712             this.el.on("touchmove", this.onTouchMove, this);
18713             this.el.on("touchend", this.onTouchEnd, this);
18714         }
18715         
18716     },
18717     
18718     onRender : function(ct, position)
18719     {
18720         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18721     },
18722     
18723     setActive : function(state)
18724     {
18725         Roo.log("panel - set active " + this.tabId + "=" + state);
18726         
18727         this.active = state;
18728         if (!state) {
18729             this.el.removeClass('active');
18730             
18731         } else  if (!this.el.hasClass('active')) {
18732             this.el.addClass('active');
18733         }
18734         
18735         this.fireEvent('changed', this, state);
18736     },
18737     
18738     onClick : function(e)
18739     {
18740         e.preventDefault();
18741         
18742         if(!this.href.length){
18743             return;
18744         }
18745         
18746         window.location.href = this.href;
18747     },
18748     
18749     startX : 0,
18750     startY : 0,
18751     endX : 0,
18752     endY : 0,
18753     swiping : false,
18754     
18755     onTouchStart : function(e)
18756     {
18757         this.swiping = false;
18758         
18759         this.startX = e.browserEvent.touches[0].clientX;
18760         this.startY = e.browserEvent.touches[0].clientY;
18761     },
18762     
18763     onTouchMove : function(e)
18764     {
18765         this.swiping = true;
18766         
18767         this.endX = e.browserEvent.touches[0].clientX;
18768         this.endY = e.browserEvent.touches[0].clientY;
18769     },
18770     
18771     onTouchEnd : function(e)
18772     {
18773         if(!this.swiping){
18774             this.onClick(e);
18775             return;
18776         }
18777         
18778         var tabGroup = this.parent();
18779         
18780         if(this.endX > this.startX){ // swiping right
18781             tabGroup.showPanelPrev();
18782             return;
18783         }
18784         
18785         if(this.startX > this.endX){ // swiping left
18786             tabGroup.showPanelNext();
18787             return;
18788         }
18789     }
18790     
18791     
18792 });
18793  
18794
18795  
18796
18797  /*
18798  * - LGPL
18799  *
18800  * DateField
18801  * 
18802  */
18803
18804 /**
18805  * @class Roo.bootstrap.DateField
18806  * @extends Roo.bootstrap.Input
18807  * Bootstrap DateField class
18808  * @cfg {Number} weekStart default 0
18809  * @cfg {String} viewMode default empty, (months|years)
18810  * @cfg {String} minViewMode default empty, (months|years)
18811  * @cfg {Number} startDate default -Infinity
18812  * @cfg {Number} endDate default Infinity
18813  * @cfg {Boolean} todayHighlight default false
18814  * @cfg {Boolean} todayBtn default false
18815  * @cfg {Boolean} calendarWeeks default false
18816  * @cfg {Object} daysOfWeekDisabled default empty
18817  * @cfg {Boolean} singleMode default false (true | false)
18818  * 
18819  * @cfg {Boolean} keyboardNavigation default true
18820  * @cfg {String} language default en
18821  * 
18822  * @constructor
18823  * Create a new DateField
18824  * @param {Object} config The config object
18825  */
18826
18827 Roo.bootstrap.DateField = function(config){
18828     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18829      this.addEvents({
18830             /**
18831              * @event show
18832              * Fires when this field show.
18833              * @param {Roo.bootstrap.DateField} this
18834              * @param {Mixed} date The date value
18835              */
18836             show : true,
18837             /**
18838              * @event show
18839              * Fires when this field hide.
18840              * @param {Roo.bootstrap.DateField} this
18841              * @param {Mixed} date The date value
18842              */
18843             hide : true,
18844             /**
18845              * @event select
18846              * Fires when select a date.
18847              * @param {Roo.bootstrap.DateField} this
18848              * @param {Mixed} date The date value
18849              */
18850             select : true,
18851             /**
18852              * @event beforeselect
18853              * Fires when before select a date.
18854              * @param {Roo.bootstrap.DateField} this
18855              * @param {Mixed} date The date value
18856              */
18857             beforeselect : true
18858         });
18859 };
18860
18861 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18862     
18863     /**
18864      * @cfg {String} format
18865      * The default date format string which can be overriden for localization support.  The format must be
18866      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18867      */
18868     format : "m/d/y",
18869     /**
18870      * @cfg {String} altFormats
18871      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18872      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18873      */
18874     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18875     
18876     weekStart : 0,
18877     
18878     viewMode : '',
18879     
18880     minViewMode : '',
18881     
18882     todayHighlight : false,
18883     
18884     todayBtn: false,
18885     
18886     language: 'en',
18887     
18888     keyboardNavigation: true,
18889     
18890     calendarWeeks: false,
18891     
18892     startDate: -Infinity,
18893     
18894     endDate: Infinity,
18895     
18896     daysOfWeekDisabled: [],
18897     
18898     _events: [],
18899     
18900     singleMode : false,
18901     
18902     UTCDate: function()
18903     {
18904         return new Date(Date.UTC.apply(Date, arguments));
18905     },
18906     
18907     UTCToday: function()
18908     {
18909         var today = new Date();
18910         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18911     },
18912     
18913     getDate: function() {
18914             var d = this.getUTCDate();
18915             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18916     },
18917     
18918     getUTCDate: function() {
18919             return this.date;
18920     },
18921     
18922     setDate: function(d) {
18923             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18924     },
18925     
18926     setUTCDate: function(d) {
18927             this.date = d;
18928             this.setValue(this.formatDate(this.date));
18929     },
18930         
18931     onRender: function(ct, position)
18932     {
18933         
18934         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18935         
18936         this.language = this.language || 'en';
18937         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18938         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18939         
18940         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18941         this.format = this.format || 'm/d/y';
18942         this.isInline = false;
18943         this.isInput = true;
18944         this.component = this.el.select('.add-on', true).first() || false;
18945         this.component = (this.component && this.component.length === 0) ? false : this.component;
18946         this.hasInput = this.component && this.inputEl().length;
18947         
18948         if (typeof(this.minViewMode === 'string')) {
18949             switch (this.minViewMode) {
18950                 case 'months':
18951                     this.minViewMode = 1;
18952                     break;
18953                 case 'years':
18954                     this.minViewMode = 2;
18955                     break;
18956                 default:
18957                     this.minViewMode = 0;
18958                     break;
18959             }
18960         }
18961         
18962         if (typeof(this.viewMode === 'string')) {
18963             switch (this.viewMode) {
18964                 case 'months':
18965                     this.viewMode = 1;
18966                     break;
18967                 case 'years':
18968                     this.viewMode = 2;
18969                     break;
18970                 default:
18971                     this.viewMode = 0;
18972                     break;
18973             }
18974         }
18975                 
18976         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18977         
18978 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18979         
18980         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18981         
18982         this.picker().on('mousedown', this.onMousedown, this);
18983         this.picker().on('click', this.onClick, this);
18984         
18985         this.picker().addClass('datepicker-dropdown');
18986         
18987         this.startViewMode = this.viewMode;
18988         
18989         if(this.singleMode){
18990             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18991                 v.setVisibilityMode(Roo.Element.DISPLAY);
18992                 v.hide();
18993             });
18994             
18995             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18996                 v.setStyle('width', '189px');
18997             });
18998         }
18999         
19000         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
19001             if(!this.calendarWeeks){
19002                 v.remove();
19003                 return;
19004             }
19005             
19006             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19007             v.attr('colspan', function(i, val){
19008                 return parseInt(val) + 1;
19009             });
19010         });
19011                         
19012         
19013         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
19014         
19015         this.setStartDate(this.startDate);
19016         this.setEndDate(this.endDate);
19017         
19018         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
19019         
19020         this.fillDow();
19021         this.fillMonths();
19022         this.update();
19023         this.showMode();
19024         
19025         if(this.isInline) {
19026             this.showPopup();
19027         }
19028     },
19029     
19030     picker : function()
19031     {
19032         return this.pickerEl;
19033 //        return this.el.select('.datepicker', true).first();
19034     },
19035     
19036     fillDow: function()
19037     {
19038         var dowCnt = this.weekStart;
19039         
19040         var dow = {
19041             tag: 'tr',
19042             cn: [
19043                 
19044             ]
19045         };
19046         
19047         if(this.calendarWeeks){
19048             dow.cn.push({
19049                 tag: 'th',
19050                 cls: 'cw',
19051                 html: '&nbsp;'
19052             })
19053         }
19054         
19055         while (dowCnt < this.weekStart + 7) {
19056             dow.cn.push({
19057                 tag: 'th',
19058                 cls: 'dow',
19059                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
19060             });
19061         }
19062         
19063         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
19064     },
19065     
19066     fillMonths: function()
19067     {    
19068         var i = 0;
19069         var months = this.picker().select('>.datepicker-months td', true).first();
19070         
19071         months.dom.innerHTML = '';
19072         
19073         while (i < 12) {
19074             var month = {
19075                 tag: 'span',
19076                 cls: 'month',
19077                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
19078             };
19079             
19080             months.createChild(month);
19081         }
19082         
19083     },
19084     
19085     update: function()
19086     {
19087         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;
19088         
19089         if (this.date < this.startDate) {
19090             this.viewDate = new Date(this.startDate);
19091         } else if (this.date > this.endDate) {
19092             this.viewDate = new Date(this.endDate);
19093         } else {
19094             this.viewDate = new Date(this.date);
19095         }
19096         
19097         this.fill();
19098     },
19099     
19100     fill: function() 
19101     {
19102         var d = new Date(this.viewDate),
19103                 year = d.getUTCFullYear(),
19104                 month = d.getUTCMonth(),
19105                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
19106                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
19107                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
19108                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
19109                 currentDate = this.date && this.date.valueOf(),
19110                 today = this.UTCToday();
19111         
19112         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
19113         
19114 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19115         
19116 //        this.picker.select('>tfoot th.today').
19117 //                                              .text(dates[this.language].today)
19118 //                                              .toggle(this.todayBtn !== false);
19119     
19120         this.updateNavArrows();
19121         this.fillMonths();
19122                                                 
19123         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
19124         
19125         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
19126          
19127         prevMonth.setUTCDate(day);
19128         
19129         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
19130         
19131         var nextMonth = new Date(prevMonth);
19132         
19133         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
19134         
19135         nextMonth = nextMonth.valueOf();
19136         
19137         var fillMonths = false;
19138         
19139         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
19140         
19141         while(prevMonth.valueOf() <= nextMonth) {
19142             var clsName = '';
19143             
19144             if (prevMonth.getUTCDay() === this.weekStart) {
19145                 if(fillMonths){
19146                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
19147                 }
19148                     
19149                 fillMonths = {
19150                     tag: 'tr',
19151                     cn: []
19152                 };
19153                 
19154                 if(this.calendarWeeks){
19155                     // ISO 8601: First week contains first thursday.
19156                     // ISO also states week starts on Monday, but we can be more abstract here.
19157                     var
19158                     // Start of current week: based on weekstart/current date
19159                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
19160                     // Thursday of this week
19161                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
19162                     // First Thursday of year, year from thursday
19163                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
19164                     // Calendar week: ms between thursdays, div ms per day, div 7 days
19165                     calWeek =  (th - yth) / 864e5 / 7 + 1;
19166                     
19167                     fillMonths.cn.push({
19168                         tag: 'td',
19169                         cls: 'cw',
19170                         html: calWeek
19171                     });
19172                 }
19173             }
19174             
19175             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
19176                 clsName += ' old';
19177             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19178                 clsName += ' new';
19179             }
19180             if (this.todayHighlight &&
19181                 prevMonth.getUTCFullYear() == today.getFullYear() &&
19182                 prevMonth.getUTCMonth() == today.getMonth() &&
19183                 prevMonth.getUTCDate() == today.getDate()) {
19184                 clsName += ' today';
19185             }
19186             
19187             if (currentDate && prevMonth.valueOf() === currentDate) {
19188                 clsName += ' active';
19189             }
19190             
19191             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19192                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19193                     clsName += ' disabled';
19194             }
19195             
19196             fillMonths.cn.push({
19197                 tag: 'td',
19198                 cls: 'day ' + clsName,
19199                 html: prevMonth.getDate()
19200             });
19201             
19202             prevMonth.setDate(prevMonth.getDate()+1);
19203         }
19204           
19205         var currentYear = this.date && this.date.getUTCFullYear();
19206         var currentMonth = this.date && this.date.getUTCMonth();
19207         
19208         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19209         
19210         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19211             v.removeClass('active');
19212             
19213             if(currentYear === year && k === currentMonth){
19214                 v.addClass('active');
19215             }
19216             
19217             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19218                 v.addClass('disabled');
19219             }
19220             
19221         });
19222         
19223         
19224         year = parseInt(year/10, 10) * 10;
19225         
19226         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19227         
19228         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19229         
19230         year -= 1;
19231         for (var i = -1; i < 11; i++) {
19232             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19233                 tag: 'span',
19234                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19235                 html: year
19236             });
19237             
19238             year += 1;
19239         }
19240     },
19241     
19242     showMode: function(dir) 
19243     {
19244         if (dir) {
19245             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19246         }
19247         
19248         Roo.each(this.picker().select('>div',true).elements, function(v){
19249             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19250             v.hide();
19251         });
19252         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19253     },
19254     
19255     place: function()
19256     {
19257         if(this.isInline) {
19258             return;
19259         }
19260         
19261         this.picker().removeClass(['bottom', 'top']);
19262         
19263         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19264             /*
19265              * place to the top of element!
19266              *
19267              */
19268             
19269             this.picker().addClass('top');
19270             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19271             
19272             return;
19273         }
19274         
19275         this.picker().addClass('bottom');
19276         
19277         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19278     },
19279     
19280     parseDate : function(value)
19281     {
19282         if(!value || value instanceof Date){
19283             return value;
19284         }
19285         var v = Date.parseDate(value, this.format);
19286         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19287             v = Date.parseDate(value, 'Y-m-d');
19288         }
19289         if(!v && this.altFormats){
19290             if(!this.altFormatsArray){
19291                 this.altFormatsArray = this.altFormats.split("|");
19292             }
19293             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19294                 v = Date.parseDate(value, this.altFormatsArray[i]);
19295             }
19296         }
19297         return v;
19298     },
19299     
19300     formatDate : function(date, fmt)
19301     {   
19302         return (!date || !(date instanceof Date)) ?
19303         date : date.dateFormat(fmt || this.format);
19304     },
19305     
19306     onFocus : function()
19307     {
19308         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19309         this.showPopup();
19310     },
19311     
19312     onBlur : function()
19313     {
19314         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19315         
19316         var d = this.inputEl().getValue();
19317         
19318         this.setValue(d);
19319                 
19320         this.hidePopup();
19321     },
19322     
19323     showPopup : function()
19324     {
19325         this.picker().show();
19326         this.update();
19327         this.place();
19328         
19329         this.fireEvent('showpopup', this, this.date);
19330     },
19331     
19332     hidePopup : function()
19333     {
19334         if(this.isInline) {
19335             return;
19336         }
19337         this.picker().hide();
19338         this.viewMode = this.startViewMode;
19339         this.showMode();
19340         
19341         this.fireEvent('hidepopup', this, this.date);
19342         
19343     },
19344     
19345     onMousedown: function(e)
19346     {
19347         e.stopPropagation();
19348         e.preventDefault();
19349     },
19350     
19351     keyup: function(e)
19352     {
19353         Roo.bootstrap.DateField.superclass.keyup.call(this);
19354         this.update();
19355     },
19356
19357     setValue: function(v)
19358     {
19359         if(this.fireEvent('beforeselect', this, v) !== false){
19360             var d = new Date(this.parseDate(v) ).clearTime();
19361         
19362             if(isNaN(d.getTime())){
19363                 this.date = this.viewDate = '';
19364                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19365                 return;
19366             }
19367
19368             v = this.formatDate(d);
19369
19370             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19371
19372             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19373
19374             this.update();
19375
19376             this.fireEvent('select', this, this.date);
19377         }
19378     },
19379     
19380     getValue: function()
19381     {
19382         return this.formatDate(this.date);
19383     },
19384     
19385     fireKey: function(e)
19386     {
19387         if (!this.picker().isVisible()){
19388             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19389                 this.showPopup();
19390             }
19391             return;
19392         }
19393         
19394         var dateChanged = false,
19395         dir, day, month,
19396         newDate, newViewDate;
19397         
19398         switch(e.keyCode){
19399             case 27: // escape
19400                 this.hidePopup();
19401                 e.preventDefault();
19402                 break;
19403             case 37: // left
19404             case 39: // right
19405                 if (!this.keyboardNavigation) {
19406                     break;
19407                 }
19408                 dir = e.keyCode == 37 ? -1 : 1;
19409                 
19410                 if (e.ctrlKey){
19411                     newDate = this.moveYear(this.date, dir);
19412                     newViewDate = this.moveYear(this.viewDate, dir);
19413                 } else if (e.shiftKey){
19414                     newDate = this.moveMonth(this.date, dir);
19415                     newViewDate = this.moveMonth(this.viewDate, dir);
19416                 } else {
19417                     newDate = new Date(this.date);
19418                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19419                     newViewDate = new Date(this.viewDate);
19420                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19421                 }
19422                 if (this.dateWithinRange(newDate)){
19423                     this.date = newDate;
19424                     this.viewDate = newViewDate;
19425                     this.setValue(this.formatDate(this.date));
19426 //                    this.update();
19427                     e.preventDefault();
19428                     dateChanged = true;
19429                 }
19430                 break;
19431             case 38: // up
19432             case 40: // down
19433                 if (!this.keyboardNavigation) {
19434                     break;
19435                 }
19436                 dir = e.keyCode == 38 ? -1 : 1;
19437                 if (e.ctrlKey){
19438                     newDate = this.moveYear(this.date, dir);
19439                     newViewDate = this.moveYear(this.viewDate, dir);
19440                 } else if (e.shiftKey){
19441                     newDate = this.moveMonth(this.date, dir);
19442                     newViewDate = this.moveMonth(this.viewDate, dir);
19443                 } else {
19444                     newDate = new Date(this.date);
19445                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19446                     newViewDate = new Date(this.viewDate);
19447                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19448                 }
19449                 if (this.dateWithinRange(newDate)){
19450                     this.date = newDate;
19451                     this.viewDate = newViewDate;
19452                     this.setValue(this.formatDate(this.date));
19453 //                    this.update();
19454                     e.preventDefault();
19455                     dateChanged = true;
19456                 }
19457                 break;
19458             case 13: // enter
19459                 this.setValue(this.formatDate(this.date));
19460                 this.hidePopup();
19461                 e.preventDefault();
19462                 break;
19463             case 9: // tab
19464                 this.setValue(this.formatDate(this.date));
19465                 this.hidePopup();
19466                 break;
19467             case 16: // shift
19468             case 17: // ctrl
19469             case 18: // alt
19470                 break;
19471             default :
19472                 this.hidePopup();
19473                 
19474         }
19475     },
19476     
19477     
19478     onClick: function(e) 
19479     {
19480         e.stopPropagation();
19481         e.preventDefault();
19482         
19483         var target = e.getTarget();
19484         
19485         if(target.nodeName.toLowerCase() === 'i'){
19486             target = Roo.get(target).dom.parentNode;
19487         }
19488         
19489         var nodeName = target.nodeName;
19490         var className = target.className;
19491         var html = target.innerHTML;
19492         //Roo.log(nodeName);
19493         
19494         switch(nodeName.toLowerCase()) {
19495             case 'th':
19496                 switch(className) {
19497                     case 'switch':
19498                         this.showMode(1);
19499                         break;
19500                     case 'prev':
19501                     case 'next':
19502                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19503                         switch(this.viewMode){
19504                                 case 0:
19505                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19506                                         break;
19507                                 case 1:
19508                                 case 2:
19509                                         this.viewDate = this.moveYear(this.viewDate, dir);
19510                                         break;
19511                         }
19512                         this.fill();
19513                         break;
19514                     case 'today':
19515                         var date = new Date();
19516                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19517 //                        this.fill()
19518                         this.setValue(this.formatDate(this.date));
19519                         
19520                         this.hidePopup();
19521                         break;
19522                 }
19523                 break;
19524             case 'span':
19525                 if (className.indexOf('disabled') < 0) {
19526                     this.viewDate.setUTCDate(1);
19527                     if (className.indexOf('month') > -1) {
19528                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19529                     } else {
19530                         var year = parseInt(html, 10) || 0;
19531                         this.viewDate.setUTCFullYear(year);
19532                         
19533                     }
19534                     
19535                     if(this.singleMode){
19536                         this.setValue(this.formatDate(this.viewDate));
19537                         this.hidePopup();
19538                         return;
19539                     }
19540                     
19541                     this.showMode(-1);
19542                     this.fill();
19543                 }
19544                 break;
19545                 
19546             case 'td':
19547                 //Roo.log(className);
19548                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19549                     var day = parseInt(html, 10) || 1;
19550                     var year = this.viewDate.getUTCFullYear(),
19551                         month = this.viewDate.getUTCMonth();
19552
19553                     if (className.indexOf('old') > -1) {
19554                         if(month === 0 ){
19555                             month = 11;
19556                             year -= 1;
19557                         }else{
19558                             month -= 1;
19559                         }
19560                     } else if (className.indexOf('new') > -1) {
19561                         if (month == 11) {
19562                             month = 0;
19563                             year += 1;
19564                         } else {
19565                             month += 1;
19566                         }
19567                     }
19568                     //Roo.log([year,month,day]);
19569                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19570                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19571 //                    this.fill();
19572                     //Roo.log(this.formatDate(this.date));
19573                     this.setValue(this.formatDate(this.date));
19574                     this.hidePopup();
19575                 }
19576                 break;
19577         }
19578     },
19579     
19580     setStartDate: function(startDate)
19581     {
19582         this.startDate = startDate || -Infinity;
19583         if (this.startDate !== -Infinity) {
19584             this.startDate = this.parseDate(this.startDate);
19585         }
19586         this.update();
19587         this.updateNavArrows();
19588     },
19589
19590     setEndDate: function(endDate)
19591     {
19592         this.endDate = endDate || Infinity;
19593         if (this.endDate !== Infinity) {
19594             this.endDate = this.parseDate(this.endDate);
19595         }
19596         this.update();
19597         this.updateNavArrows();
19598     },
19599     
19600     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19601     {
19602         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19603         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19604             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19605         }
19606         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19607             return parseInt(d, 10);
19608         });
19609         this.update();
19610         this.updateNavArrows();
19611     },
19612     
19613     updateNavArrows: function() 
19614     {
19615         if(this.singleMode){
19616             return;
19617         }
19618         
19619         var d = new Date(this.viewDate),
19620         year = d.getUTCFullYear(),
19621         month = d.getUTCMonth();
19622         
19623         Roo.each(this.picker().select('.prev', true).elements, function(v){
19624             v.show();
19625             switch (this.viewMode) {
19626                 case 0:
19627
19628                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19629                         v.hide();
19630                     }
19631                     break;
19632                 case 1:
19633                 case 2:
19634                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19635                         v.hide();
19636                     }
19637                     break;
19638             }
19639         });
19640         
19641         Roo.each(this.picker().select('.next', true).elements, function(v){
19642             v.show();
19643             switch (this.viewMode) {
19644                 case 0:
19645
19646                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19647                         v.hide();
19648                     }
19649                     break;
19650                 case 1:
19651                 case 2:
19652                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19653                         v.hide();
19654                     }
19655                     break;
19656             }
19657         })
19658     },
19659     
19660     moveMonth: function(date, dir)
19661     {
19662         if (!dir) {
19663             return date;
19664         }
19665         var new_date = new Date(date.valueOf()),
19666         day = new_date.getUTCDate(),
19667         month = new_date.getUTCMonth(),
19668         mag = Math.abs(dir),
19669         new_month, test;
19670         dir = dir > 0 ? 1 : -1;
19671         if (mag == 1){
19672             test = dir == -1
19673             // If going back one month, make sure month is not current month
19674             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19675             ? function(){
19676                 return new_date.getUTCMonth() == month;
19677             }
19678             // If going forward one month, make sure month is as expected
19679             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19680             : function(){
19681                 return new_date.getUTCMonth() != new_month;
19682             };
19683             new_month = month + dir;
19684             new_date.setUTCMonth(new_month);
19685             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19686             if (new_month < 0 || new_month > 11) {
19687                 new_month = (new_month + 12) % 12;
19688             }
19689         } else {
19690             // For magnitudes >1, move one month at a time...
19691             for (var i=0; i<mag; i++) {
19692                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19693                 new_date = this.moveMonth(new_date, dir);
19694             }
19695             // ...then reset the day, keeping it in the new month
19696             new_month = new_date.getUTCMonth();
19697             new_date.setUTCDate(day);
19698             test = function(){
19699                 return new_month != new_date.getUTCMonth();
19700             };
19701         }
19702         // Common date-resetting loop -- if date is beyond end of month, make it
19703         // end of month
19704         while (test()){
19705             new_date.setUTCDate(--day);
19706             new_date.setUTCMonth(new_month);
19707         }
19708         return new_date;
19709     },
19710
19711     moveYear: function(date, dir)
19712     {
19713         return this.moveMonth(date, dir*12);
19714     },
19715
19716     dateWithinRange: function(date)
19717     {
19718         return date >= this.startDate && date <= this.endDate;
19719     },
19720
19721     
19722     remove: function() 
19723     {
19724         this.picker().remove();
19725     },
19726     
19727     validateValue : function(value)
19728     {
19729         if(this.getVisibilityEl().hasClass('hidden')){
19730             return true;
19731         }
19732         
19733         if(value.length < 1)  {
19734             if(this.allowBlank){
19735                 return true;
19736             }
19737             return false;
19738         }
19739         
19740         if(value.length < this.minLength){
19741             return false;
19742         }
19743         if(value.length > this.maxLength){
19744             return false;
19745         }
19746         if(this.vtype){
19747             var vt = Roo.form.VTypes;
19748             if(!vt[this.vtype](value, this)){
19749                 return false;
19750             }
19751         }
19752         if(typeof this.validator == "function"){
19753             var msg = this.validator(value);
19754             if(msg !== true){
19755                 return false;
19756             }
19757         }
19758         
19759         if(this.regex && !this.regex.test(value)){
19760             return false;
19761         }
19762         
19763         if(typeof(this.parseDate(value)) == 'undefined'){
19764             return false;
19765         }
19766         
19767         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19768             return false;
19769         }      
19770         
19771         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19772             return false;
19773         } 
19774         
19775         
19776         return true;
19777     },
19778     
19779     reset : function()
19780     {
19781         this.date = this.viewDate = '';
19782         
19783         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19784     }
19785    
19786 });
19787
19788 Roo.apply(Roo.bootstrap.DateField,  {
19789     
19790     head : {
19791         tag: 'thead',
19792         cn: [
19793         {
19794             tag: 'tr',
19795             cn: [
19796             {
19797                 tag: 'th',
19798                 cls: 'prev',
19799                 html: '<i class="fa fa-arrow-left"/>'
19800             },
19801             {
19802                 tag: 'th',
19803                 cls: 'switch',
19804                 colspan: '5'
19805             },
19806             {
19807                 tag: 'th',
19808                 cls: 'next',
19809                 html: '<i class="fa fa-arrow-right"/>'
19810             }
19811
19812             ]
19813         }
19814         ]
19815     },
19816     
19817     content : {
19818         tag: 'tbody',
19819         cn: [
19820         {
19821             tag: 'tr',
19822             cn: [
19823             {
19824                 tag: 'td',
19825                 colspan: '7'
19826             }
19827             ]
19828         }
19829         ]
19830     },
19831     
19832     footer : {
19833         tag: 'tfoot',
19834         cn: [
19835         {
19836             tag: 'tr',
19837             cn: [
19838             {
19839                 tag: 'th',
19840                 colspan: '7',
19841                 cls: 'today'
19842             }
19843                     
19844             ]
19845         }
19846         ]
19847     },
19848     
19849     dates:{
19850         en: {
19851             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19852             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19853             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19854             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19855             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19856             today: "Today"
19857         }
19858     },
19859     
19860     modes: [
19861     {
19862         clsName: 'days',
19863         navFnc: 'Month',
19864         navStep: 1
19865     },
19866     {
19867         clsName: 'months',
19868         navFnc: 'FullYear',
19869         navStep: 1
19870     },
19871     {
19872         clsName: 'years',
19873         navFnc: 'FullYear',
19874         navStep: 10
19875     }]
19876 });
19877
19878 Roo.apply(Roo.bootstrap.DateField,  {
19879   
19880     template : {
19881         tag: 'div',
19882         cls: 'datepicker dropdown-menu roo-dynamic',
19883         cn: [
19884         {
19885             tag: 'div',
19886             cls: 'datepicker-days',
19887             cn: [
19888             {
19889                 tag: 'table',
19890                 cls: 'table-condensed',
19891                 cn:[
19892                 Roo.bootstrap.DateField.head,
19893                 {
19894                     tag: 'tbody'
19895                 },
19896                 Roo.bootstrap.DateField.footer
19897                 ]
19898             }
19899             ]
19900         },
19901         {
19902             tag: 'div',
19903             cls: 'datepicker-months',
19904             cn: [
19905             {
19906                 tag: 'table',
19907                 cls: 'table-condensed',
19908                 cn:[
19909                 Roo.bootstrap.DateField.head,
19910                 Roo.bootstrap.DateField.content,
19911                 Roo.bootstrap.DateField.footer
19912                 ]
19913             }
19914             ]
19915         },
19916         {
19917             tag: 'div',
19918             cls: 'datepicker-years',
19919             cn: [
19920             {
19921                 tag: 'table',
19922                 cls: 'table-condensed',
19923                 cn:[
19924                 Roo.bootstrap.DateField.head,
19925                 Roo.bootstrap.DateField.content,
19926                 Roo.bootstrap.DateField.footer
19927                 ]
19928             }
19929             ]
19930         }
19931         ]
19932     }
19933 });
19934
19935  
19936
19937  /*
19938  * - LGPL
19939  *
19940  * TimeField
19941  * 
19942  */
19943
19944 /**
19945  * @class Roo.bootstrap.TimeField
19946  * @extends Roo.bootstrap.Input
19947  * Bootstrap DateField class
19948  * 
19949  * 
19950  * @constructor
19951  * Create a new TimeField
19952  * @param {Object} config The config object
19953  */
19954
19955 Roo.bootstrap.TimeField = function(config){
19956     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19957     this.addEvents({
19958             /**
19959              * @event show
19960              * Fires when this field show.
19961              * @param {Roo.bootstrap.DateField} thisthis
19962              * @param {Mixed} date The date value
19963              */
19964             show : true,
19965             /**
19966              * @event show
19967              * Fires when this field hide.
19968              * @param {Roo.bootstrap.DateField} this
19969              * @param {Mixed} date The date value
19970              */
19971             hide : true,
19972             /**
19973              * @event select
19974              * Fires when select a date.
19975              * @param {Roo.bootstrap.DateField} this
19976              * @param {Mixed} date The date value
19977              */
19978             select : true
19979         });
19980 };
19981
19982 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19983     
19984     /**
19985      * @cfg {String} format
19986      * The default time format string which can be overriden for localization support.  The format must be
19987      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19988      */
19989     format : "H:i",
19990        
19991     onRender: function(ct, position)
19992     {
19993         
19994         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19995                 
19996         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19997         
19998         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19999         
20000         this.pop = this.picker().select('>.datepicker-time',true).first();
20001         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20002         
20003         this.picker().on('mousedown', this.onMousedown, this);
20004         this.picker().on('click', this.onClick, this);
20005         
20006         this.picker().addClass('datepicker-dropdown');
20007     
20008         this.fillTime();
20009         this.update();
20010             
20011         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
20012         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
20013         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
20014         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
20015         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
20016         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
20017
20018     },
20019     
20020     fireKey: function(e){
20021         if (!this.picker().isVisible()){
20022             if (e.keyCode == 27) { // allow escape to hide and re-show picker
20023                 this.show();
20024             }
20025             return;
20026         }
20027
20028         e.preventDefault();
20029         
20030         switch(e.keyCode){
20031             case 27: // escape
20032                 this.hide();
20033                 break;
20034             case 37: // left
20035             case 39: // right
20036                 this.onTogglePeriod();
20037                 break;
20038             case 38: // up
20039                 this.onIncrementMinutes();
20040                 break;
20041             case 40: // down
20042                 this.onDecrementMinutes();
20043                 break;
20044             case 13: // enter
20045             case 9: // tab
20046                 this.setTime();
20047                 break;
20048         }
20049     },
20050     
20051     onClick: function(e) {
20052         e.stopPropagation();
20053         e.preventDefault();
20054     },
20055     
20056     picker : function()
20057     {
20058         return this.el.select('.datepicker', true).first();
20059     },
20060     
20061     fillTime: function()
20062     {    
20063         var time = this.pop.select('tbody', true).first();
20064         
20065         time.dom.innerHTML = '';
20066         
20067         time.createChild({
20068             tag: 'tr',
20069             cn: [
20070                 {
20071                     tag: 'td',
20072                     cn: [
20073                         {
20074                             tag: 'a',
20075                             href: '#',
20076                             cls: 'btn',
20077                             cn: [
20078                                 {
20079                                     tag: 'span',
20080                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
20081                                 }
20082                             ]
20083                         } 
20084                     ]
20085                 },
20086                 {
20087                     tag: 'td',
20088                     cls: 'separator'
20089                 },
20090                 {
20091                     tag: 'td',
20092                     cn: [
20093                         {
20094                             tag: 'a',
20095                             href: '#',
20096                             cls: 'btn',
20097                             cn: [
20098                                 {
20099                                     tag: 'span',
20100                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
20101                                 }
20102                             ]
20103                         }
20104                     ]
20105                 },
20106                 {
20107                     tag: 'td',
20108                     cls: 'separator'
20109                 }
20110             ]
20111         });
20112         
20113         time.createChild({
20114             tag: 'tr',
20115             cn: [
20116                 {
20117                     tag: 'td',
20118                     cn: [
20119                         {
20120                             tag: 'span',
20121                             cls: 'timepicker-hour',
20122                             html: '00'
20123                         }  
20124                     ]
20125                 },
20126                 {
20127                     tag: 'td',
20128                     cls: 'separator',
20129                     html: ':'
20130                 },
20131                 {
20132                     tag: 'td',
20133                     cn: [
20134                         {
20135                             tag: 'span',
20136                             cls: 'timepicker-minute',
20137                             html: '00'
20138                         }  
20139                     ]
20140                 },
20141                 {
20142                     tag: 'td',
20143                     cls: 'separator'
20144                 },
20145                 {
20146                     tag: 'td',
20147                     cn: [
20148                         {
20149                             tag: 'button',
20150                             type: 'button',
20151                             cls: 'btn btn-primary period',
20152                             html: 'AM'
20153                             
20154                         }
20155                     ]
20156                 }
20157             ]
20158         });
20159         
20160         time.createChild({
20161             tag: 'tr',
20162             cn: [
20163                 {
20164                     tag: 'td',
20165                     cn: [
20166                         {
20167                             tag: 'a',
20168                             href: '#',
20169                             cls: 'btn',
20170                             cn: [
20171                                 {
20172                                     tag: 'span',
20173                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
20174                                 }
20175                             ]
20176                         }
20177                     ]
20178                 },
20179                 {
20180                     tag: 'td',
20181                     cls: 'separator'
20182                 },
20183                 {
20184                     tag: 'td',
20185                     cn: [
20186                         {
20187                             tag: 'a',
20188                             href: '#',
20189                             cls: 'btn',
20190                             cn: [
20191                                 {
20192                                     tag: 'span',
20193                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
20194                                 }
20195                             ]
20196                         }
20197                     ]
20198                 },
20199                 {
20200                     tag: 'td',
20201                     cls: 'separator'
20202                 }
20203             ]
20204         });
20205         
20206     },
20207     
20208     update: function()
20209     {
20210         
20211         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20212         
20213         this.fill();
20214     },
20215     
20216     fill: function() 
20217     {
20218         var hours = this.time.getHours();
20219         var minutes = this.time.getMinutes();
20220         var period = 'AM';
20221         
20222         if(hours > 11){
20223             period = 'PM';
20224         }
20225         
20226         if(hours == 0){
20227             hours = 12;
20228         }
20229         
20230         
20231         if(hours > 12){
20232             hours = hours - 12;
20233         }
20234         
20235         if(hours < 10){
20236             hours = '0' + hours;
20237         }
20238         
20239         if(minutes < 10){
20240             minutes = '0' + minutes;
20241         }
20242         
20243         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20244         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20245         this.pop.select('button', true).first().dom.innerHTML = period;
20246         
20247     },
20248     
20249     place: function()
20250     {   
20251         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20252         
20253         var cls = ['bottom'];
20254         
20255         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20256             cls.pop();
20257             cls.push('top');
20258         }
20259         
20260         cls.push('right');
20261         
20262         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20263             cls.pop();
20264             cls.push('left');
20265         }
20266         
20267         this.picker().addClass(cls.join('-'));
20268         
20269         var _this = this;
20270         
20271         Roo.each(cls, function(c){
20272             if(c == 'bottom'){
20273                 _this.picker().setTop(_this.inputEl().getHeight());
20274                 return;
20275             }
20276             if(c == 'top'){
20277                 _this.picker().setTop(0 - _this.picker().getHeight());
20278                 return;
20279             }
20280             
20281             if(c == 'left'){
20282                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20283                 return;
20284             }
20285             if(c == 'right'){
20286                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20287                 return;
20288             }
20289         });
20290         
20291     },
20292   
20293     onFocus : function()
20294     {
20295         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20296         this.show();
20297     },
20298     
20299     onBlur : function()
20300     {
20301         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20302         this.hide();
20303     },
20304     
20305     show : function()
20306     {
20307         this.picker().show();
20308         this.pop.show();
20309         this.update();
20310         this.place();
20311         
20312         this.fireEvent('show', this, this.date);
20313     },
20314     
20315     hide : function()
20316     {
20317         this.picker().hide();
20318         this.pop.hide();
20319         
20320         this.fireEvent('hide', this, this.date);
20321     },
20322     
20323     setTime : function()
20324     {
20325         this.hide();
20326         this.setValue(this.time.format(this.format));
20327         
20328         this.fireEvent('select', this, this.date);
20329         
20330         
20331     },
20332     
20333     onMousedown: function(e){
20334         e.stopPropagation();
20335         e.preventDefault();
20336     },
20337     
20338     onIncrementHours: function()
20339     {
20340         Roo.log('onIncrementHours');
20341         this.time = this.time.add(Date.HOUR, 1);
20342         this.update();
20343         
20344     },
20345     
20346     onDecrementHours: function()
20347     {
20348         Roo.log('onDecrementHours');
20349         this.time = this.time.add(Date.HOUR, -1);
20350         this.update();
20351     },
20352     
20353     onIncrementMinutes: function()
20354     {
20355         Roo.log('onIncrementMinutes');
20356         this.time = this.time.add(Date.MINUTE, 1);
20357         this.update();
20358     },
20359     
20360     onDecrementMinutes: function()
20361     {
20362         Roo.log('onDecrementMinutes');
20363         this.time = this.time.add(Date.MINUTE, -1);
20364         this.update();
20365     },
20366     
20367     onTogglePeriod: function()
20368     {
20369         Roo.log('onTogglePeriod');
20370         this.time = this.time.add(Date.HOUR, 12);
20371         this.update();
20372     }
20373     
20374    
20375 });
20376
20377 Roo.apply(Roo.bootstrap.TimeField,  {
20378     
20379     content : {
20380         tag: 'tbody',
20381         cn: [
20382             {
20383                 tag: 'tr',
20384                 cn: [
20385                 {
20386                     tag: 'td',
20387                     colspan: '7'
20388                 }
20389                 ]
20390             }
20391         ]
20392     },
20393     
20394     footer : {
20395         tag: 'tfoot',
20396         cn: [
20397             {
20398                 tag: 'tr',
20399                 cn: [
20400                 {
20401                     tag: 'th',
20402                     colspan: '7',
20403                     cls: '',
20404                     cn: [
20405                         {
20406                             tag: 'button',
20407                             cls: 'btn btn-info ok',
20408                             html: 'OK'
20409                         }
20410                     ]
20411                 }
20412
20413                 ]
20414             }
20415         ]
20416     }
20417 });
20418
20419 Roo.apply(Roo.bootstrap.TimeField,  {
20420   
20421     template : {
20422         tag: 'div',
20423         cls: 'datepicker dropdown-menu',
20424         cn: [
20425             {
20426                 tag: 'div',
20427                 cls: 'datepicker-time',
20428                 cn: [
20429                 {
20430                     tag: 'table',
20431                     cls: 'table-condensed',
20432                     cn:[
20433                     Roo.bootstrap.TimeField.content,
20434                     Roo.bootstrap.TimeField.footer
20435                     ]
20436                 }
20437                 ]
20438             }
20439         ]
20440     }
20441 });
20442
20443  
20444
20445  /*
20446  * - LGPL
20447  *
20448  * MonthField
20449  * 
20450  */
20451
20452 /**
20453  * @class Roo.bootstrap.MonthField
20454  * @extends Roo.bootstrap.Input
20455  * Bootstrap MonthField class
20456  * 
20457  * @cfg {String} language default en
20458  * 
20459  * @constructor
20460  * Create a new MonthField
20461  * @param {Object} config The config object
20462  */
20463
20464 Roo.bootstrap.MonthField = function(config){
20465     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20466     
20467     this.addEvents({
20468         /**
20469          * @event show
20470          * Fires when this field show.
20471          * @param {Roo.bootstrap.MonthField} this
20472          * @param {Mixed} date The date value
20473          */
20474         show : true,
20475         /**
20476          * @event show
20477          * Fires when this field hide.
20478          * @param {Roo.bootstrap.MonthField} this
20479          * @param {Mixed} date The date value
20480          */
20481         hide : true,
20482         /**
20483          * @event select
20484          * Fires when select a date.
20485          * @param {Roo.bootstrap.MonthField} this
20486          * @param {String} oldvalue The old value
20487          * @param {String} newvalue The new value
20488          */
20489         select : true
20490     });
20491 };
20492
20493 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20494     
20495     onRender: function(ct, position)
20496     {
20497         
20498         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20499         
20500         this.language = this.language || 'en';
20501         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20502         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20503         
20504         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20505         this.isInline = false;
20506         this.isInput = true;
20507         this.component = this.el.select('.add-on', true).first() || false;
20508         this.component = (this.component && this.component.length === 0) ? false : this.component;
20509         this.hasInput = this.component && this.inputEL().length;
20510         
20511         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20512         
20513         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20514         
20515         this.picker().on('mousedown', this.onMousedown, this);
20516         this.picker().on('click', this.onClick, this);
20517         
20518         this.picker().addClass('datepicker-dropdown');
20519         
20520         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20521             v.setStyle('width', '189px');
20522         });
20523         
20524         this.fillMonths();
20525         
20526         this.update();
20527         
20528         if(this.isInline) {
20529             this.show();
20530         }
20531         
20532     },
20533     
20534     setValue: function(v, suppressEvent)
20535     {   
20536         var o = this.getValue();
20537         
20538         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20539         
20540         this.update();
20541
20542         if(suppressEvent !== true){
20543             this.fireEvent('select', this, o, v);
20544         }
20545         
20546     },
20547     
20548     getValue: function()
20549     {
20550         return this.value;
20551     },
20552     
20553     onClick: function(e) 
20554     {
20555         e.stopPropagation();
20556         e.preventDefault();
20557         
20558         var target = e.getTarget();
20559         
20560         if(target.nodeName.toLowerCase() === 'i'){
20561             target = Roo.get(target).dom.parentNode;
20562         }
20563         
20564         var nodeName = target.nodeName;
20565         var className = target.className;
20566         var html = target.innerHTML;
20567         
20568         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20569             return;
20570         }
20571         
20572         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20573         
20574         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20575         
20576         this.hide();
20577                         
20578     },
20579     
20580     picker : function()
20581     {
20582         return this.pickerEl;
20583     },
20584     
20585     fillMonths: function()
20586     {    
20587         var i = 0;
20588         var months = this.picker().select('>.datepicker-months td', true).first();
20589         
20590         months.dom.innerHTML = '';
20591         
20592         while (i < 12) {
20593             var month = {
20594                 tag: 'span',
20595                 cls: 'month',
20596                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20597             };
20598             
20599             months.createChild(month);
20600         }
20601         
20602     },
20603     
20604     update: function()
20605     {
20606         var _this = this;
20607         
20608         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20609             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20610         }
20611         
20612         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20613             e.removeClass('active');
20614             
20615             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20616                 e.addClass('active');
20617             }
20618         })
20619     },
20620     
20621     place: function()
20622     {
20623         if(this.isInline) {
20624             return;
20625         }
20626         
20627         this.picker().removeClass(['bottom', 'top']);
20628         
20629         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20630             /*
20631              * place to the top of element!
20632              *
20633              */
20634             
20635             this.picker().addClass('top');
20636             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20637             
20638             return;
20639         }
20640         
20641         this.picker().addClass('bottom');
20642         
20643         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20644     },
20645     
20646     onFocus : function()
20647     {
20648         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20649         this.show();
20650     },
20651     
20652     onBlur : function()
20653     {
20654         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20655         
20656         var d = this.inputEl().getValue();
20657         
20658         this.setValue(d);
20659                 
20660         this.hide();
20661     },
20662     
20663     show : function()
20664     {
20665         this.picker().show();
20666         this.picker().select('>.datepicker-months', true).first().show();
20667         this.update();
20668         this.place();
20669         
20670         this.fireEvent('show', this, this.date);
20671     },
20672     
20673     hide : function()
20674     {
20675         if(this.isInline) {
20676             return;
20677         }
20678         this.picker().hide();
20679         this.fireEvent('hide', this, this.date);
20680         
20681     },
20682     
20683     onMousedown: function(e)
20684     {
20685         e.stopPropagation();
20686         e.preventDefault();
20687     },
20688     
20689     keyup: function(e)
20690     {
20691         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20692         this.update();
20693     },
20694
20695     fireKey: function(e)
20696     {
20697         if (!this.picker().isVisible()){
20698             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20699                 this.show();
20700             }
20701             return;
20702         }
20703         
20704         var dir;
20705         
20706         switch(e.keyCode){
20707             case 27: // escape
20708                 this.hide();
20709                 e.preventDefault();
20710                 break;
20711             case 37: // left
20712             case 39: // right
20713                 dir = e.keyCode == 37 ? -1 : 1;
20714                 
20715                 this.vIndex = this.vIndex + dir;
20716                 
20717                 if(this.vIndex < 0){
20718                     this.vIndex = 0;
20719                 }
20720                 
20721                 if(this.vIndex > 11){
20722                     this.vIndex = 11;
20723                 }
20724                 
20725                 if(isNaN(this.vIndex)){
20726                     this.vIndex = 0;
20727                 }
20728                 
20729                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20730                 
20731                 break;
20732             case 38: // up
20733             case 40: // down
20734                 
20735                 dir = e.keyCode == 38 ? -1 : 1;
20736                 
20737                 this.vIndex = this.vIndex + dir * 4;
20738                 
20739                 if(this.vIndex < 0){
20740                     this.vIndex = 0;
20741                 }
20742                 
20743                 if(this.vIndex > 11){
20744                     this.vIndex = 11;
20745                 }
20746                 
20747                 if(isNaN(this.vIndex)){
20748                     this.vIndex = 0;
20749                 }
20750                 
20751                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20752                 break;
20753                 
20754             case 13: // enter
20755                 
20756                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20757                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20758                 }
20759                 
20760                 this.hide();
20761                 e.preventDefault();
20762                 break;
20763             case 9: // tab
20764                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20765                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20766                 }
20767                 this.hide();
20768                 break;
20769             case 16: // shift
20770             case 17: // ctrl
20771             case 18: // alt
20772                 break;
20773             default :
20774                 this.hide();
20775                 
20776         }
20777     },
20778     
20779     remove: function() 
20780     {
20781         this.picker().remove();
20782     }
20783    
20784 });
20785
20786 Roo.apply(Roo.bootstrap.MonthField,  {
20787     
20788     content : {
20789         tag: 'tbody',
20790         cn: [
20791         {
20792             tag: 'tr',
20793             cn: [
20794             {
20795                 tag: 'td',
20796                 colspan: '7'
20797             }
20798             ]
20799         }
20800         ]
20801     },
20802     
20803     dates:{
20804         en: {
20805             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20806             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20807         }
20808     }
20809 });
20810
20811 Roo.apply(Roo.bootstrap.MonthField,  {
20812   
20813     template : {
20814         tag: 'div',
20815         cls: 'datepicker dropdown-menu roo-dynamic',
20816         cn: [
20817             {
20818                 tag: 'div',
20819                 cls: 'datepicker-months',
20820                 cn: [
20821                 {
20822                     tag: 'table',
20823                     cls: 'table-condensed',
20824                     cn:[
20825                         Roo.bootstrap.DateField.content
20826                     ]
20827                 }
20828                 ]
20829             }
20830         ]
20831     }
20832 });
20833
20834  
20835
20836  
20837  /*
20838  * - LGPL
20839  *
20840  * CheckBox
20841  * 
20842  */
20843
20844 /**
20845  * @class Roo.bootstrap.CheckBox
20846  * @extends Roo.bootstrap.Input
20847  * Bootstrap CheckBox class
20848  * 
20849  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20850  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20851  * @cfg {String} boxLabel The text that appears beside the checkbox
20852  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20853  * @cfg {Boolean} checked initnal the element
20854  * @cfg {Boolean} inline inline the element (default false)
20855  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20856  * @cfg {String} tooltip label tooltip
20857  * 
20858  * @constructor
20859  * Create a new CheckBox
20860  * @param {Object} config The config object
20861  */
20862
20863 Roo.bootstrap.CheckBox = function(config){
20864     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20865    
20866     this.addEvents({
20867         /**
20868         * @event check
20869         * Fires when the element is checked or unchecked.
20870         * @param {Roo.bootstrap.CheckBox} this This input
20871         * @param {Boolean} checked The new checked value
20872         */
20873        check : true,
20874        /**
20875         * @event click
20876         * Fires when the element is click.
20877         * @param {Roo.bootstrap.CheckBox} this This input
20878         */
20879        click : true
20880     });
20881     
20882 };
20883
20884 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20885   
20886     inputType: 'checkbox',
20887     inputValue: 1,
20888     valueOff: 0,
20889     boxLabel: false,
20890     checked: false,
20891     weight : false,
20892     inline: false,
20893     tooltip : '',
20894     
20895     getAutoCreate : function()
20896     {
20897         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20898         
20899         var id = Roo.id();
20900         
20901         var cfg = {};
20902         
20903         cfg.cls = 'form-group ' + this.inputType; //input-group
20904         
20905         if(this.inline){
20906             cfg.cls += ' ' + this.inputType + '-inline';
20907         }
20908         
20909         var input =  {
20910             tag: 'input',
20911             id : id,
20912             type : this.inputType,
20913             value : this.inputValue,
20914             cls : 'roo-' + this.inputType, //'form-box',
20915             placeholder : this.placeholder || ''
20916             
20917         };
20918         
20919         if(this.inputType != 'radio'){
20920             var hidden =  {
20921                 tag: 'input',
20922                 type : 'hidden',
20923                 cls : 'roo-hidden-value',
20924                 value : this.checked ? this.inputValue : this.valueOff
20925             };
20926         }
20927         
20928             
20929         if (this.weight) { // Validity check?
20930             cfg.cls += " " + this.inputType + "-" + this.weight;
20931         }
20932         
20933         if (this.disabled) {
20934             input.disabled=true;
20935         }
20936         
20937         if(this.checked){
20938             input.checked = this.checked;
20939         }
20940         
20941         if (this.name) {
20942             
20943             input.name = this.name;
20944             
20945             if(this.inputType != 'radio'){
20946                 hidden.name = this.name;
20947                 input.name = '_hidden_' + this.name;
20948             }
20949         }
20950         
20951         if (this.size) {
20952             input.cls += ' input-' + this.size;
20953         }
20954         
20955         var settings=this;
20956         
20957         ['xs','sm','md','lg'].map(function(size){
20958             if (settings[size]) {
20959                 cfg.cls += ' col-' + size + '-' + settings[size];
20960             }
20961         });
20962         
20963         var inputblock = input;
20964          
20965         if (this.before || this.after) {
20966             
20967             inputblock = {
20968                 cls : 'input-group',
20969                 cn :  [] 
20970             };
20971             
20972             if (this.before) {
20973                 inputblock.cn.push({
20974                     tag :'span',
20975                     cls : 'input-group-addon',
20976                     html : this.before
20977                 });
20978             }
20979             
20980             inputblock.cn.push(input);
20981             
20982             if(this.inputType != 'radio'){
20983                 inputblock.cn.push(hidden);
20984             }
20985             
20986             if (this.after) {
20987                 inputblock.cn.push({
20988                     tag :'span',
20989                     cls : 'input-group-addon',
20990                     html : this.after
20991                 });
20992             }
20993             
20994         }
20995         
20996         if (align ==='left' && this.fieldLabel.length) {
20997 //                Roo.log("left and has label");
20998             cfg.cn = [
20999                 {
21000                     tag: 'label',
21001                     'for' :  id,
21002                     cls : 'control-label',
21003                     html : this.fieldLabel
21004                 },
21005                 {
21006                     cls : "", 
21007                     cn: [
21008                         inputblock
21009                     ]
21010                 }
21011             ];
21012             
21013             if(this.labelWidth > 12){
21014                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
21015             }
21016             
21017             if(this.labelWidth < 13 && this.labelmd == 0){
21018                 this.labelmd = this.labelWidth;
21019             }
21020             
21021             if(this.labellg > 0){
21022                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
21023                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
21024             }
21025             
21026             if(this.labelmd > 0){
21027                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
21028                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
21029             }
21030             
21031             if(this.labelsm > 0){
21032                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
21033                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
21034             }
21035             
21036             if(this.labelxs > 0){
21037                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
21038                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
21039             }
21040             
21041         } else if ( this.fieldLabel.length) {
21042 //                Roo.log(" label");
21043                 cfg.cn = [
21044                    
21045                     {
21046                         tag: this.boxLabel ? 'span' : 'label',
21047                         'for': id,
21048                         cls: 'control-label box-input-label',
21049                         //cls : 'input-group-addon',
21050                         html : this.fieldLabel
21051                     },
21052                     
21053                     inputblock
21054                     
21055                 ];
21056
21057         } else {
21058             
21059 //                Roo.log(" no label && no align");
21060                 cfg.cn = [  inputblock ] ;
21061                 
21062                 
21063         }
21064         
21065         if(this.boxLabel){
21066              var boxLabelCfg = {
21067                 tag: 'label',
21068                 //'for': id, // box label is handled by onclick - so no for...
21069                 cls: 'box-label',
21070                 html: this.boxLabel
21071             };
21072             
21073             if(this.tooltip){
21074                 boxLabelCfg.tooltip = this.tooltip;
21075             }
21076              
21077             cfg.cn.push(boxLabelCfg);
21078         }
21079         
21080         if(this.inputType != 'radio'){
21081             cfg.cn.push(hidden);
21082         }
21083         
21084         return cfg;
21085         
21086     },
21087     
21088     /**
21089      * return the real input element.
21090      */
21091     inputEl: function ()
21092     {
21093         return this.el.select('input.roo-' + this.inputType,true).first();
21094     },
21095     hiddenEl: function ()
21096     {
21097         return this.el.select('input.roo-hidden-value',true).first();
21098     },
21099     
21100     labelEl: function()
21101     {
21102         return this.el.select('label.control-label',true).first();
21103     },
21104     /* depricated... */
21105     
21106     label: function()
21107     {
21108         return this.labelEl();
21109     },
21110     
21111     boxLabelEl: function()
21112     {
21113         return this.el.select('label.box-label',true).first();
21114     },
21115     
21116     initEvents : function()
21117     {
21118 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
21119         
21120         this.inputEl().on('click', this.onClick,  this);
21121         
21122         if (this.boxLabel) { 
21123             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
21124         }
21125         
21126         this.startValue = this.getValue();
21127         
21128         if(this.groupId){
21129             Roo.bootstrap.CheckBox.register(this);
21130         }
21131     },
21132     
21133     onClick : function(e)
21134     {   
21135         if(this.fireEvent('click', this, e) !== false){
21136             this.setChecked(!this.checked);
21137         }
21138         
21139     },
21140     
21141     setChecked : function(state,suppressEvent)
21142     {
21143         this.startValue = this.getValue();
21144
21145         if(this.inputType == 'radio'){
21146             
21147             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21148                 e.dom.checked = false;
21149             });
21150             
21151             this.inputEl().dom.checked = true;
21152             
21153             this.inputEl().dom.value = this.inputValue;
21154             
21155             if(suppressEvent !== true){
21156                 this.fireEvent('check', this, true);
21157             }
21158             
21159             this.validate();
21160             
21161             return;
21162         }
21163         
21164         this.checked = state;
21165         
21166         this.inputEl().dom.checked = state;
21167         
21168         
21169         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
21170         
21171         if(suppressEvent !== true){
21172             this.fireEvent('check', this, state);
21173         }
21174         
21175         this.validate();
21176     },
21177     
21178     getValue : function()
21179     {
21180         if(this.inputType == 'radio'){
21181             return this.getGroupValue();
21182         }
21183         
21184         return this.hiddenEl().dom.value;
21185         
21186     },
21187     
21188     getGroupValue : function()
21189     {
21190         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21191             return '';
21192         }
21193         
21194         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21195     },
21196     
21197     setValue : function(v,suppressEvent)
21198     {
21199         if(this.inputType == 'radio'){
21200             this.setGroupValue(v, suppressEvent);
21201             return;
21202         }
21203         
21204         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21205         
21206         this.validate();
21207     },
21208     
21209     setGroupValue : function(v, suppressEvent)
21210     {
21211         this.startValue = this.getValue();
21212         
21213         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21214             e.dom.checked = false;
21215             
21216             if(e.dom.value == v){
21217                 e.dom.checked = true;
21218             }
21219         });
21220         
21221         if(suppressEvent !== true){
21222             this.fireEvent('check', this, true);
21223         }
21224
21225         this.validate();
21226         
21227         return;
21228     },
21229     
21230     validate : function()
21231     {
21232         if(this.getVisibilityEl().hasClass('hidden')){
21233             return true;
21234         }
21235         
21236         if(
21237                 this.disabled || 
21238                 (this.inputType == 'radio' && this.validateRadio()) ||
21239                 (this.inputType == 'checkbox' && this.validateCheckbox())
21240         ){
21241             this.markValid();
21242             return true;
21243         }
21244         
21245         this.markInvalid();
21246         return false;
21247     },
21248     
21249     validateRadio : function()
21250     {
21251         if(this.getVisibilityEl().hasClass('hidden')){
21252             return true;
21253         }
21254         
21255         if(this.allowBlank){
21256             return true;
21257         }
21258         
21259         var valid = false;
21260         
21261         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21262             if(!e.dom.checked){
21263                 return;
21264             }
21265             
21266             valid = true;
21267             
21268             return false;
21269         });
21270         
21271         return valid;
21272     },
21273     
21274     validateCheckbox : function()
21275     {
21276         if(!this.groupId){
21277             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21278             //return (this.getValue() == this.inputValue) ? true : false;
21279         }
21280         
21281         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21282         
21283         if(!group){
21284             return false;
21285         }
21286         
21287         var r = false;
21288         
21289         for(var i in group){
21290             if(group[i].el.isVisible(true)){
21291                 r = false;
21292                 break;
21293             }
21294             
21295             r = true;
21296         }
21297         
21298         for(var i in group){
21299             if(r){
21300                 break;
21301             }
21302             
21303             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21304         }
21305         
21306         return r;
21307     },
21308     
21309     /**
21310      * Mark this field as valid
21311      */
21312     markValid : function()
21313     {
21314         var _this = this;
21315         
21316         this.fireEvent('valid', this);
21317         
21318         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21319         
21320         if(this.groupId){
21321             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21322         }
21323         
21324         if(label){
21325             label.markValid();
21326         }
21327
21328         if(this.inputType == 'radio'){
21329             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21330                 var fg = e.findParent('.form-group', false, true);
21331                 if (Roo.bootstrap.version == 3) {
21332                     fg.removeClass([_this.invalidClass, _this.validClass]);
21333                     fg.addClass(_this.validClass);
21334                 } else {
21335                     fg.removeClass(['is-valid', 'is-invalid']);
21336                     fg.addClass('is-valid');
21337                 }
21338             });
21339             
21340             return;
21341         }
21342
21343         if(!this.groupId){
21344             var fg = this.el.findParent('.form-group', false, true);
21345             if (Roo.bootstrap.version == 3) {
21346                 fg.removeClass([this.invalidClass, this.validClass]);
21347                 fg.addClass(this.validClass);
21348             } else {
21349                 fg.removeClass(['is-valid', 'is-invalid']);
21350                 fg.addClass('is-valid');
21351             }
21352             return;
21353         }
21354         
21355         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21356         
21357         if(!group){
21358             return;
21359         }
21360         
21361         for(var i in group){
21362             var fg = group[i].el.findParent('.form-group', false, true);
21363             if (Roo.bootstrap.version == 3) {
21364                 fg.removeClass([this.invalidClass, this.validClass]);
21365                 fg.addClass(this.validClass);
21366             } else {
21367                 fg.removeClass(['is-valid', 'is-invalid']);
21368                 fg.addClass('is-valid');
21369             }
21370         }
21371     },
21372     
21373      /**
21374      * Mark this field as invalid
21375      * @param {String} msg The validation message
21376      */
21377     markInvalid : function(msg)
21378     {
21379         if(this.allowBlank){
21380             return;
21381         }
21382         
21383         var _this = this;
21384         
21385         this.fireEvent('invalid', this, msg);
21386         
21387         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21388         
21389         if(this.groupId){
21390             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21391         }
21392         
21393         if(label){
21394             label.markInvalid();
21395         }
21396             
21397         if(this.inputType == 'radio'){
21398             
21399             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21400                 var fg = e.findParent('.form-group', false, true);
21401                 if (Roo.bootstrap.version == 3) {
21402                     fg.removeClass([_this.invalidClass, _this.validClass]);
21403                     fg.addClass(_this.invalidClass);
21404                 } else {
21405                     fg.removeClass(['is-invalid', 'is-valid']);
21406                     fg.addClass('is-invalid');
21407                 }
21408             });
21409             
21410             return;
21411         }
21412         
21413         if(!this.groupId){
21414             var fg = this.el.findParent('.form-group', false, true);
21415             if (Roo.bootstrap.version == 3) {
21416                 fg.removeClass([_this.invalidClass, _this.validClass]);
21417                 fg.addClass(_this.invalidClass);
21418             } else {
21419                 fg.removeClass(['is-invalid', 'is-valid']);
21420                 fg.addClass('is-invalid');
21421             }
21422             return;
21423         }
21424         
21425         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21426         
21427         if(!group){
21428             return;
21429         }
21430         
21431         for(var i in group){
21432             var fg = group[i].el.findParent('.form-group', false, true);
21433             if (Roo.bootstrap.version == 3) {
21434                 fg.removeClass([_this.invalidClass, _this.validClass]);
21435                 fg.addClass(_this.invalidClass);
21436             } else {
21437                 fg.removeClass(['is-invalid', 'is-valid']);
21438                 fg.addClass('is-invalid');
21439             }
21440         }
21441         
21442     },
21443     
21444     clearInvalid : function()
21445     {
21446         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21447         
21448         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21449         
21450         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21451         
21452         if (label && label.iconEl) {
21453             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
21454             label.iconEl.removeClass(['is-invalid', 'is-valid']);
21455         }
21456     },
21457     
21458     disable : function()
21459     {
21460         if(this.inputType != 'radio'){
21461             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21462             return;
21463         }
21464         
21465         var _this = this;
21466         
21467         if(this.rendered){
21468             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21469                 _this.getActionEl().addClass(this.disabledClass);
21470                 e.dom.disabled = true;
21471             });
21472         }
21473         
21474         this.disabled = true;
21475         this.fireEvent("disable", this);
21476         return this;
21477     },
21478
21479     enable : function()
21480     {
21481         if(this.inputType != 'radio'){
21482             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21483             return;
21484         }
21485         
21486         var _this = this;
21487         
21488         if(this.rendered){
21489             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21490                 _this.getActionEl().removeClass(this.disabledClass);
21491                 e.dom.disabled = false;
21492             });
21493         }
21494         
21495         this.disabled = false;
21496         this.fireEvent("enable", this);
21497         return this;
21498     },
21499     
21500     setBoxLabel : function(v)
21501     {
21502         this.boxLabel = v;
21503         
21504         if(this.rendered){
21505             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21506         }
21507     }
21508
21509 });
21510
21511 Roo.apply(Roo.bootstrap.CheckBox, {
21512     
21513     groups: {},
21514     
21515      /**
21516     * register a CheckBox Group
21517     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21518     */
21519     register : function(checkbox)
21520     {
21521         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21522             this.groups[checkbox.groupId] = {};
21523         }
21524         
21525         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21526             return;
21527         }
21528         
21529         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21530         
21531     },
21532     /**
21533     * fetch a CheckBox Group based on the group ID
21534     * @param {string} the group ID
21535     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21536     */
21537     get: function(groupId) {
21538         if (typeof(this.groups[groupId]) == 'undefined') {
21539             return false;
21540         }
21541         
21542         return this.groups[groupId] ;
21543     }
21544     
21545     
21546 });
21547 /*
21548  * - LGPL
21549  *
21550  * RadioItem
21551  * 
21552  */
21553
21554 /**
21555  * @class Roo.bootstrap.Radio
21556  * @extends Roo.bootstrap.Component
21557  * Bootstrap Radio class
21558  * @cfg {String} boxLabel - the label associated
21559  * @cfg {String} value - the value of radio
21560  * 
21561  * @constructor
21562  * Create a new Radio
21563  * @param {Object} config The config object
21564  */
21565 Roo.bootstrap.Radio = function(config){
21566     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21567     
21568 };
21569
21570 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21571     
21572     boxLabel : '',
21573     
21574     value : '',
21575     
21576     getAutoCreate : function()
21577     {
21578         var cfg = {
21579             tag : 'div',
21580             cls : 'form-group radio',
21581             cn : [
21582                 {
21583                     tag : 'label',
21584                     cls : 'box-label',
21585                     html : this.boxLabel
21586                 }
21587             ]
21588         };
21589         
21590         return cfg;
21591     },
21592     
21593     initEvents : function() 
21594     {
21595         this.parent().register(this);
21596         
21597         this.el.on('click', this.onClick, this);
21598         
21599     },
21600     
21601     onClick : function(e)
21602     {
21603         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21604             this.setChecked(true);
21605         }
21606     },
21607     
21608     setChecked : function(state, suppressEvent)
21609     {
21610         this.parent().setValue(this.value, suppressEvent);
21611         
21612     },
21613     
21614     setBoxLabel : function(v)
21615     {
21616         this.boxLabel = v;
21617         
21618         if(this.rendered){
21619             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21620         }
21621     }
21622     
21623 });
21624  
21625
21626  /*
21627  * - LGPL
21628  *
21629  * Input
21630  * 
21631  */
21632
21633 /**
21634  * @class Roo.bootstrap.SecurePass
21635  * @extends Roo.bootstrap.Input
21636  * Bootstrap SecurePass class
21637  *
21638  * 
21639  * @constructor
21640  * Create a new SecurePass
21641  * @param {Object} config The config object
21642  */
21643  
21644 Roo.bootstrap.SecurePass = function (config) {
21645     // these go here, so the translation tool can replace them..
21646     this.errors = {
21647         PwdEmpty: "Please type a password, and then retype it to confirm.",
21648         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21649         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21650         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21651         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21652         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21653         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21654         TooWeak: "Your password is Too Weak."
21655     },
21656     this.meterLabel = "Password strength:";
21657     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21658     this.meterClass = [
21659         "roo-password-meter-tooweak", 
21660         "roo-password-meter-weak", 
21661         "roo-password-meter-medium", 
21662         "roo-password-meter-strong", 
21663         "roo-password-meter-grey"
21664     ];
21665     
21666     this.errors = {};
21667     
21668     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21669 }
21670
21671 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21672     /**
21673      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21674      * {
21675      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21676      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21677      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21678      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21679      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21680      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21681      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21682      * })
21683      */
21684     // private
21685     
21686     meterWidth: 300,
21687     errorMsg :'',    
21688     errors: false,
21689     imageRoot: '/',
21690     /**
21691      * @cfg {String/Object} Label for the strength meter (defaults to
21692      * 'Password strength:')
21693      */
21694     // private
21695     meterLabel: '',
21696     /**
21697      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21698      * ['Weak', 'Medium', 'Strong'])
21699      */
21700     // private    
21701     pwdStrengths: false,    
21702     // private
21703     strength: 0,
21704     // private
21705     _lastPwd: null,
21706     // private
21707     kCapitalLetter: 0,
21708     kSmallLetter: 1,
21709     kDigit: 2,
21710     kPunctuation: 3,
21711     
21712     insecure: false,
21713     // private
21714     initEvents: function ()
21715     {
21716         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21717
21718         if (this.el.is('input[type=password]') && Roo.isSafari) {
21719             this.el.on('keydown', this.SafariOnKeyDown, this);
21720         }
21721
21722         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21723     },
21724     // private
21725     onRender: function (ct, position)
21726     {
21727         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21728         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21729         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21730
21731         this.trigger.createChild({
21732                    cn: [
21733                     {
21734                     //id: 'PwdMeter',
21735                     tag: 'div',
21736                     cls: 'roo-password-meter-grey col-xs-12',
21737                     style: {
21738                         //width: 0,
21739                         //width: this.meterWidth + 'px'                                                
21740                         }
21741                     },
21742                     {                            
21743                          cls: 'roo-password-meter-text'                          
21744                     }
21745                 ]            
21746         });
21747
21748          
21749         if (this.hideTrigger) {
21750             this.trigger.setDisplayed(false);
21751         }
21752         this.setSize(this.width || '', this.height || '');
21753     },
21754     // private
21755     onDestroy: function ()
21756     {
21757         if (this.trigger) {
21758             this.trigger.removeAllListeners();
21759             this.trigger.remove();
21760         }
21761         if (this.wrap) {
21762             this.wrap.remove();
21763         }
21764         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21765     },
21766     // private
21767     checkStrength: function ()
21768     {
21769         var pwd = this.inputEl().getValue();
21770         if (pwd == this._lastPwd) {
21771             return;
21772         }
21773
21774         var strength;
21775         if (this.ClientSideStrongPassword(pwd)) {
21776             strength = 3;
21777         } else if (this.ClientSideMediumPassword(pwd)) {
21778             strength = 2;
21779         } else if (this.ClientSideWeakPassword(pwd)) {
21780             strength = 1;
21781         } else {
21782             strength = 0;
21783         }
21784         
21785         Roo.log('strength1: ' + strength);
21786         
21787         //var pm = this.trigger.child('div/div/div').dom;
21788         var pm = this.trigger.child('div/div');
21789         pm.removeClass(this.meterClass);
21790         pm.addClass(this.meterClass[strength]);
21791                 
21792         
21793         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21794                 
21795         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21796         
21797         this._lastPwd = pwd;
21798     },
21799     reset: function ()
21800     {
21801         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21802         
21803         this._lastPwd = '';
21804         
21805         var pm = this.trigger.child('div/div');
21806         pm.removeClass(this.meterClass);
21807         pm.addClass('roo-password-meter-grey');        
21808         
21809         
21810         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21811         
21812         pt.innerHTML = '';
21813         this.inputEl().dom.type='password';
21814     },
21815     // private
21816     validateValue: function (value)
21817     {
21818         
21819         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21820             return false;
21821         }
21822         if (value.length == 0) {
21823             if (this.allowBlank) {
21824                 this.clearInvalid();
21825                 return true;
21826             }
21827
21828             this.markInvalid(this.errors.PwdEmpty);
21829             this.errorMsg = this.errors.PwdEmpty;
21830             return false;
21831         }
21832         
21833         if(this.insecure){
21834             return true;
21835         }
21836         
21837         if ('[\x21-\x7e]*'.match(value)) {
21838             this.markInvalid(this.errors.PwdBadChar);
21839             this.errorMsg = this.errors.PwdBadChar;
21840             return false;
21841         }
21842         if (value.length < 6) {
21843             this.markInvalid(this.errors.PwdShort);
21844             this.errorMsg = this.errors.PwdShort;
21845             return false;
21846         }
21847         if (value.length > 16) {
21848             this.markInvalid(this.errors.PwdLong);
21849             this.errorMsg = this.errors.PwdLong;
21850             return false;
21851         }
21852         var strength;
21853         if (this.ClientSideStrongPassword(value)) {
21854             strength = 3;
21855         } else if (this.ClientSideMediumPassword(value)) {
21856             strength = 2;
21857         } else if (this.ClientSideWeakPassword(value)) {
21858             strength = 1;
21859         } else {
21860             strength = 0;
21861         }
21862
21863         
21864         if (strength < 2) {
21865             //this.markInvalid(this.errors.TooWeak);
21866             this.errorMsg = this.errors.TooWeak;
21867             //return false;
21868         }
21869         
21870         
21871         console.log('strength2: ' + strength);
21872         
21873         //var pm = this.trigger.child('div/div/div').dom;
21874         
21875         var pm = this.trigger.child('div/div');
21876         pm.removeClass(this.meterClass);
21877         pm.addClass(this.meterClass[strength]);
21878                 
21879         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21880                 
21881         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21882         
21883         this.errorMsg = ''; 
21884         return true;
21885     },
21886     // private
21887     CharacterSetChecks: function (type)
21888     {
21889         this.type = type;
21890         this.fResult = false;
21891     },
21892     // private
21893     isctype: function (character, type)
21894     {
21895         switch (type) {  
21896             case this.kCapitalLetter:
21897                 if (character >= 'A' && character <= 'Z') {
21898                     return true;
21899                 }
21900                 break;
21901             
21902             case this.kSmallLetter:
21903                 if (character >= 'a' && character <= 'z') {
21904                     return true;
21905                 }
21906                 break;
21907             
21908             case this.kDigit:
21909                 if (character >= '0' && character <= '9') {
21910                     return true;
21911                 }
21912                 break;
21913             
21914             case this.kPunctuation:
21915                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21916                     return true;
21917                 }
21918                 break;
21919             
21920             default:
21921                 return false;
21922         }
21923
21924     },
21925     // private
21926     IsLongEnough: function (pwd, size)
21927     {
21928         return !(pwd == null || isNaN(size) || pwd.length < size);
21929     },
21930     // private
21931     SpansEnoughCharacterSets: function (word, nb)
21932     {
21933         if (!this.IsLongEnough(word, nb))
21934         {
21935             return false;
21936         }
21937
21938         var characterSetChecks = new Array(
21939             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21940             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21941         );
21942         
21943         for (var index = 0; index < word.length; ++index) {
21944             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21945                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21946                     characterSetChecks[nCharSet].fResult = true;
21947                     break;
21948                 }
21949             }
21950         }
21951
21952         var nCharSets = 0;
21953         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21954             if (characterSetChecks[nCharSet].fResult) {
21955                 ++nCharSets;
21956             }
21957         }
21958
21959         if (nCharSets < nb) {
21960             return false;
21961         }
21962         return true;
21963     },
21964     // private
21965     ClientSideStrongPassword: function (pwd)
21966     {
21967         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21968     },
21969     // private
21970     ClientSideMediumPassword: function (pwd)
21971     {
21972         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21973     },
21974     // private
21975     ClientSideWeakPassword: function (pwd)
21976     {
21977         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21978     }
21979           
21980 })//<script type="text/javascript">
21981
21982 /*
21983  * Based  Ext JS Library 1.1.1
21984  * Copyright(c) 2006-2007, Ext JS, LLC.
21985  * LGPL
21986  *
21987  */
21988  
21989 /**
21990  * @class Roo.HtmlEditorCore
21991  * @extends Roo.Component
21992  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21993  *
21994  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21995  */
21996
21997 Roo.HtmlEditorCore = function(config){
21998     
21999     
22000     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
22001     
22002     
22003     this.addEvents({
22004         /**
22005          * @event initialize
22006          * Fires when the editor is fully initialized (including the iframe)
22007          * @param {Roo.HtmlEditorCore} this
22008          */
22009         initialize: true,
22010         /**
22011          * @event activate
22012          * Fires when the editor is first receives the focus. Any insertion must wait
22013          * until after this event.
22014          * @param {Roo.HtmlEditorCore} this
22015          */
22016         activate: true,
22017          /**
22018          * @event beforesync
22019          * Fires before the textarea is updated with content from the editor iframe. Return false
22020          * to cancel the sync.
22021          * @param {Roo.HtmlEditorCore} this
22022          * @param {String} html
22023          */
22024         beforesync: true,
22025          /**
22026          * @event beforepush
22027          * Fires before the iframe editor is updated with content from the textarea. Return false
22028          * to cancel the push.
22029          * @param {Roo.HtmlEditorCore} this
22030          * @param {String} html
22031          */
22032         beforepush: true,
22033          /**
22034          * @event sync
22035          * Fires when the textarea is updated with content from the editor iframe.
22036          * @param {Roo.HtmlEditorCore} this
22037          * @param {String} html
22038          */
22039         sync: true,
22040          /**
22041          * @event push
22042          * Fires when the iframe editor is updated with content from the textarea.
22043          * @param {Roo.HtmlEditorCore} this
22044          * @param {String} html
22045          */
22046         push: true,
22047         
22048         /**
22049          * @event editorevent
22050          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22051          * @param {Roo.HtmlEditorCore} this
22052          */
22053         editorevent: true
22054         
22055     });
22056     
22057     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
22058     
22059     // defaults : white / black...
22060     this.applyBlacklists();
22061     
22062     
22063     
22064 };
22065
22066
22067 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
22068
22069
22070      /**
22071      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
22072      */
22073     
22074     owner : false,
22075     
22076      /**
22077      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22078      *                        Roo.resizable.
22079      */
22080     resizable : false,
22081      /**
22082      * @cfg {Number} height (in pixels)
22083      */   
22084     height: 300,
22085    /**
22086      * @cfg {Number} width (in pixels)
22087      */   
22088     width: 500,
22089     
22090     /**
22091      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22092      * 
22093      */
22094     stylesheets: false,
22095     
22096     // id of frame..
22097     frameId: false,
22098     
22099     // private properties
22100     validationEvent : false,
22101     deferHeight: true,
22102     initialized : false,
22103     activated : false,
22104     sourceEditMode : false,
22105     onFocus : Roo.emptyFn,
22106     iframePad:3,
22107     hideMode:'offsets',
22108     
22109     clearUp: true,
22110     
22111     // blacklist + whitelisted elements..
22112     black: false,
22113     white: false,
22114      
22115     bodyCls : '',
22116
22117     /**
22118      * Protected method that will not generally be called directly. It
22119      * is called when the editor initializes the iframe with HTML contents. Override this method if you
22120      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
22121      */
22122     getDocMarkup : function(){
22123         // body styles..
22124         var st = '';
22125         
22126         // inherit styels from page...?? 
22127         if (this.stylesheets === false) {
22128             
22129             Roo.get(document.head).select('style').each(function(node) {
22130                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22131             });
22132             
22133             Roo.get(document.head).select('link').each(function(node) { 
22134                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22135             });
22136             
22137         } else if (!this.stylesheets.length) {
22138                 // simple..
22139                 st = '<style type="text/css">' +
22140                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22141                    '</style>';
22142         } else { 
22143             st = '<style type="text/css">' +
22144                     this.stylesheets +
22145                 '</style>';
22146         }
22147         
22148         st +=  '<style type="text/css">' +
22149             'IMG { cursor: pointer } ' +
22150         '</style>';
22151
22152         var cls = 'roo-htmleditor-body';
22153         
22154         if(this.bodyCls.length){
22155             cls += ' ' + this.bodyCls;
22156         }
22157         
22158         return '<html><head>' + st  +
22159             //<style type="text/css">' +
22160             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22161             //'</style>' +
22162             ' </head><body class="' +  cls + '"></body></html>';
22163     },
22164
22165     // private
22166     onRender : function(ct, position)
22167     {
22168         var _t = this;
22169         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
22170         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
22171         
22172         
22173         this.el.dom.style.border = '0 none';
22174         this.el.dom.setAttribute('tabIndex', -1);
22175         this.el.addClass('x-hidden hide');
22176         
22177         
22178         
22179         if(Roo.isIE){ // fix IE 1px bogus margin
22180             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
22181         }
22182        
22183         
22184         this.frameId = Roo.id();
22185         
22186          
22187         
22188         var iframe = this.owner.wrap.createChild({
22189             tag: 'iframe',
22190             cls: 'form-control', // bootstrap..
22191             id: this.frameId,
22192             name: this.frameId,
22193             frameBorder : 'no',
22194             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
22195         }, this.el
22196         );
22197         
22198         
22199         this.iframe = iframe.dom;
22200
22201          this.assignDocWin();
22202         
22203         this.doc.designMode = 'on';
22204        
22205         this.doc.open();
22206         this.doc.write(this.getDocMarkup());
22207         this.doc.close();
22208
22209         
22210         var task = { // must defer to wait for browser to be ready
22211             run : function(){
22212                 //console.log("run task?" + this.doc.readyState);
22213                 this.assignDocWin();
22214                 if(this.doc.body || this.doc.readyState == 'complete'){
22215                     try {
22216                         this.doc.designMode="on";
22217                     } catch (e) {
22218                         return;
22219                     }
22220                     Roo.TaskMgr.stop(task);
22221                     this.initEditor.defer(10, this);
22222                 }
22223             },
22224             interval : 10,
22225             duration: 10000,
22226             scope: this
22227         };
22228         Roo.TaskMgr.start(task);
22229
22230     },
22231
22232     // private
22233     onResize : function(w, h)
22234     {
22235          Roo.log('resize: ' +w + ',' + h );
22236         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22237         if(!this.iframe){
22238             return;
22239         }
22240         if(typeof w == 'number'){
22241             
22242             this.iframe.style.width = w + 'px';
22243         }
22244         if(typeof h == 'number'){
22245             
22246             this.iframe.style.height = h + 'px';
22247             if(this.doc){
22248                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22249             }
22250         }
22251         
22252     },
22253
22254     /**
22255      * Toggles the editor between standard and source edit mode.
22256      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22257      */
22258     toggleSourceEdit : function(sourceEditMode){
22259         
22260         this.sourceEditMode = sourceEditMode === true;
22261         
22262         if(this.sourceEditMode){
22263  
22264             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
22265             
22266         }else{
22267             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22268             //this.iframe.className = '';
22269             this.deferFocus();
22270         }
22271         //this.setSize(this.owner.wrap.getSize());
22272         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22273     },
22274
22275     
22276   
22277
22278     /**
22279      * Protected method that will not generally be called directly. If you need/want
22280      * custom HTML cleanup, this is the method you should override.
22281      * @param {String} html The HTML to be cleaned
22282      * return {String} The cleaned HTML
22283      */
22284     cleanHtml : function(html){
22285         html = String(html);
22286         if(html.length > 5){
22287             if(Roo.isSafari){ // strip safari nonsense
22288                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22289             }
22290         }
22291         if(html == '&nbsp;'){
22292             html = '';
22293         }
22294         return html;
22295     },
22296
22297     /**
22298      * HTML Editor -> Textarea
22299      * Protected method that will not generally be called directly. Syncs the contents
22300      * of the editor iframe with the textarea.
22301      */
22302     syncValue : function(){
22303         if(this.initialized){
22304             var bd = (this.doc.body || this.doc.documentElement);
22305             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22306             var html = bd.innerHTML;
22307             if(Roo.isSafari){
22308                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22309                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22310                 if(m && m[1]){
22311                     html = '<div style="'+m[0]+'">' + html + '</div>';
22312                 }
22313             }
22314             html = this.cleanHtml(html);
22315             // fix up the special chars.. normaly like back quotes in word...
22316             // however we do not want to do this with chinese..
22317             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22318                 var cc = b.charCodeAt();
22319                 if (
22320                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22321                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22322                     (cc >= 0xf900 && cc < 0xfb00 )
22323                 ) {
22324                         return b;
22325                 }
22326                 return "&#"+cc+";" 
22327             });
22328             if(this.owner.fireEvent('beforesync', this, html) !== false){
22329                 this.el.dom.value = html;
22330                 this.owner.fireEvent('sync', this, html);
22331             }
22332         }
22333     },
22334
22335     /**
22336      * Protected method that will not generally be called directly. Pushes the value of the textarea
22337      * into the iframe editor.
22338      */
22339     pushValue : function(){
22340         if(this.initialized){
22341             var v = this.el.dom.value.trim();
22342             
22343 //            if(v.length < 1){
22344 //                v = '&#160;';
22345 //            }
22346             
22347             if(this.owner.fireEvent('beforepush', this, v) !== false){
22348                 var d = (this.doc.body || this.doc.documentElement);
22349                 d.innerHTML = v;
22350                 this.cleanUpPaste();
22351                 this.el.dom.value = d.innerHTML;
22352                 this.owner.fireEvent('push', this, v);
22353             }
22354         }
22355     },
22356
22357     // private
22358     deferFocus : function(){
22359         this.focus.defer(10, this);
22360     },
22361
22362     // doc'ed in Field
22363     focus : function(){
22364         if(this.win && !this.sourceEditMode){
22365             this.win.focus();
22366         }else{
22367             this.el.focus();
22368         }
22369     },
22370     
22371     assignDocWin: function()
22372     {
22373         var iframe = this.iframe;
22374         
22375          if(Roo.isIE){
22376             this.doc = iframe.contentWindow.document;
22377             this.win = iframe.contentWindow;
22378         } else {
22379 //            if (!Roo.get(this.frameId)) {
22380 //                return;
22381 //            }
22382 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22383 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22384             
22385             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22386                 return;
22387             }
22388             
22389             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22390             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22391         }
22392     },
22393     
22394     // private
22395     initEditor : function(){
22396         //console.log("INIT EDITOR");
22397         this.assignDocWin();
22398         
22399         
22400         
22401         this.doc.designMode="on";
22402         this.doc.open();
22403         this.doc.write(this.getDocMarkup());
22404         this.doc.close();
22405         
22406         var dbody = (this.doc.body || this.doc.documentElement);
22407         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22408         // this copies styles from the containing element into thsi one..
22409         // not sure why we need all of this..
22410         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22411         
22412         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22413         //ss['background-attachment'] = 'fixed'; // w3c
22414         dbody.bgProperties = 'fixed'; // ie
22415         //Roo.DomHelper.applyStyles(dbody, ss);
22416         Roo.EventManager.on(this.doc, {
22417             //'mousedown': this.onEditorEvent,
22418             'mouseup': this.onEditorEvent,
22419             'dblclick': this.onEditorEvent,
22420             'click': this.onEditorEvent,
22421             'keyup': this.onEditorEvent,
22422             buffer:100,
22423             scope: this
22424         });
22425         if(Roo.isGecko){
22426             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22427         }
22428         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22429             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22430         }
22431         this.initialized = true;
22432
22433         this.owner.fireEvent('initialize', this);
22434         this.pushValue();
22435     },
22436
22437     // private
22438     onDestroy : function(){
22439         
22440         
22441         
22442         if(this.rendered){
22443             
22444             //for (var i =0; i < this.toolbars.length;i++) {
22445             //    // fixme - ask toolbars for heights?
22446             //    this.toolbars[i].onDestroy();
22447            // }
22448             
22449             //this.wrap.dom.innerHTML = '';
22450             //this.wrap.remove();
22451         }
22452     },
22453
22454     // private
22455     onFirstFocus : function(){
22456         
22457         this.assignDocWin();
22458         
22459         
22460         this.activated = true;
22461          
22462     
22463         if(Roo.isGecko){ // prevent silly gecko errors
22464             this.win.focus();
22465             var s = this.win.getSelection();
22466             if(!s.focusNode || s.focusNode.nodeType != 3){
22467                 var r = s.getRangeAt(0);
22468                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22469                 r.collapse(true);
22470                 this.deferFocus();
22471             }
22472             try{
22473                 this.execCmd('useCSS', true);
22474                 this.execCmd('styleWithCSS', false);
22475             }catch(e){}
22476         }
22477         this.owner.fireEvent('activate', this);
22478     },
22479
22480     // private
22481     adjustFont: function(btn){
22482         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22483         //if(Roo.isSafari){ // safari
22484         //    adjust *= 2;
22485        // }
22486         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22487         if(Roo.isSafari){ // safari
22488             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22489             v =  (v < 10) ? 10 : v;
22490             v =  (v > 48) ? 48 : v;
22491             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22492             
22493         }
22494         
22495         
22496         v = Math.max(1, v+adjust);
22497         
22498         this.execCmd('FontSize', v  );
22499     },
22500
22501     onEditorEvent : function(e)
22502     {
22503         this.owner.fireEvent('editorevent', this, e);
22504       //  this.updateToolbar();
22505         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22506     },
22507
22508     insertTag : function(tg)
22509     {
22510         // could be a bit smarter... -> wrap the current selected tRoo..
22511         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22512             
22513             range = this.createRange(this.getSelection());
22514             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22515             wrappingNode.appendChild(range.extractContents());
22516             range.insertNode(wrappingNode);
22517
22518             return;
22519             
22520             
22521             
22522         }
22523         this.execCmd("formatblock",   tg);
22524         
22525     },
22526     
22527     insertText : function(txt)
22528     {
22529         
22530         
22531         var range = this.createRange();
22532         range.deleteContents();
22533                //alert(Sender.getAttribute('label'));
22534                
22535         range.insertNode(this.doc.createTextNode(txt));
22536     } ,
22537     
22538      
22539
22540     /**
22541      * Executes a Midas editor command on the editor document and performs necessary focus and
22542      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22543      * @param {String} cmd The Midas command
22544      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22545      */
22546     relayCmd : function(cmd, value){
22547         this.win.focus();
22548         this.execCmd(cmd, value);
22549         this.owner.fireEvent('editorevent', this);
22550         //this.updateToolbar();
22551         this.owner.deferFocus();
22552     },
22553
22554     /**
22555      * Executes a Midas editor command directly on the editor document.
22556      * For visual commands, you should use {@link #relayCmd} instead.
22557      * <b>This should only be called after the editor is initialized.</b>
22558      * @param {String} cmd The Midas command
22559      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22560      */
22561     execCmd : function(cmd, value){
22562         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22563         this.syncValue();
22564     },
22565  
22566  
22567    
22568     /**
22569      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22570      * to insert tRoo.
22571      * @param {String} text | dom node.. 
22572      */
22573     insertAtCursor : function(text)
22574     {
22575         
22576         if(!this.activated){
22577             return;
22578         }
22579         /*
22580         if(Roo.isIE){
22581             this.win.focus();
22582             var r = this.doc.selection.createRange();
22583             if(r){
22584                 r.collapse(true);
22585                 r.pasteHTML(text);
22586                 this.syncValue();
22587                 this.deferFocus();
22588             
22589             }
22590             return;
22591         }
22592         */
22593         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22594             this.win.focus();
22595             
22596             
22597             // from jquery ui (MIT licenced)
22598             var range, node;
22599             var win = this.win;
22600             
22601             if (win.getSelection && win.getSelection().getRangeAt) {
22602                 range = win.getSelection().getRangeAt(0);
22603                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22604                 range.insertNode(node);
22605             } else if (win.document.selection && win.document.selection.createRange) {
22606                 // no firefox support
22607                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22608                 win.document.selection.createRange().pasteHTML(txt);
22609             } else {
22610                 // no firefox support
22611                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22612                 this.execCmd('InsertHTML', txt);
22613             } 
22614             
22615             this.syncValue();
22616             
22617             this.deferFocus();
22618         }
22619     },
22620  // private
22621     mozKeyPress : function(e){
22622         if(e.ctrlKey){
22623             var c = e.getCharCode(), cmd;
22624           
22625             if(c > 0){
22626                 c = String.fromCharCode(c).toLowerCase();
22627                 switch(c){
22628                     case 'b':
22629                         cmd = 'bold';
22630                         break;
22631                     case 'i':
22632                         cmd = 'italic';
22633                         break;
22634                     
22635                     case 'u':
22636                         cmd = 'underline';
22637                         break;
22638                     
22639                     case 'v':
22640                         this.cleanUpPaste.defer(100, this);
22641                         return;
22642                         
22643                 }
22644                 if(cmd){
22645                     this.win.focus();
22646                     this.execCmd(cmd);
22647                     this.deferFocus();
22648                     e.preventDefault();
22649                 }
22650                 
22651             }
22652         }
22653     },
22654
22655     // private
22656     fixKeys : function(){ // load time branching for fastest keydown performance
22657         if(Roo.isIE){
22658             return function(e){
22659                 var k = e.getKey(), r;
22660                 if(k == e.TAB){
22661                     e.stopEvent();
22662                     r = this.doc.selection.createRange();
22663                     if(r){
22664                         r.collapse(true);
22665                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22666                         this.deferFocus();
22667                     }
22668                     return;
22669                 }
22670                 
22671                 if(k == e.ENTER){
22672                     r = this.doc.selection.createRange();
22673                     if(r){
22674                         var target = r.parentElement();
22675                         if(!target || target.tagName.toLowerCase() != 'li'){
22676                             e.stopEvent();
22677                             r.pasteHTML('<br />');
22678                             r.collapse(false);
22679                             r.select();
22680                         }
22681                     }
22682                 }
22683                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22684                     this.cleanUpPaste.defer(100, this);
22685                     return;
22686                 }
22687                 
22688                 
22689             };
22690         }else if(Roo.isOpera){
22691             return function(e){
22692                 var k = e.getKey();
22693                 if(k == e.TAB){
22694                     e.stopEvent();
22695                     this.win.focus();
22696                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22697                     this.deferFocus();
22698                 }
22699                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22700                     this.cleanUpPaste.defer(100, this);
22701                     return;
22702                 }
22703                 
22704             };
22705         }else if(Roo.isSafari){
22706             return function(e){
22707                 var k = e.getKey();
22708                 
22709                 if(k == e.TAB){
22710                     e.stopEvent();
22711                     this.execCmd('InsertText','\t');
22712                     this.deferFocus();
22713                     return;
22714                 }
22715                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22716                     this.cleanUpPaste.defer(100, this);
22717                     return;
22718                 }
22719                 
22720              };
22721         }
22722     }(),
22723     
22724     getAllAncestors: function()
22725     {
22726         var p = this.getSelectedNode();
22727         var a = [];
22728         if (!p) {
22729             a.push(p); // push blank onto stack..
22730             p = this.getParentElement();
22731         }
22732         
22733         
22734         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22735             a.push(p);
22736             p = p.parentNode;
22737         }
22738         a.push(this.doc.body);
22739         return a;
22740     },
22741     lastSel : false,
22742     lastSelNode : false,
22743     
22744     
22745     getSelection : function() 
22746     {
22747         this.assignDocWin();
22748         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22749     },
22750     
22751     getSelectedNode: function() 
22752     {
22753         // this may only work on Gecko!!!
22754         
22755         // should we cache this!!!!
22756         
22757         
22758         
22759          
22760         var range = this.createRange(this.getSelection()).cloneRange();
22761         
22762         if (Roo.isIE) {
22763             var parent = range.parentElement();
22764             while (true) {
22765                 var testRange = range.duplicate();
22766                 testRange.moveToElementText(parent);
22767                 if (testRange.inRange(range)) {
22768                     break;
22769                 }
22770                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22771                     break;
22772                 }
22773                 parent = parent.parentElement;
22774             }
22775             return parent;
22776         }
22777         
22778         // is ancestor a text element.
22779         var ac =  range.commonAncestorContainer;
22780         if (ac.nodeType == 3) {
22781             ac = ac.parentNode;
22782         }
22783         
22784         var ar = ac.childNodes;
22785          
22786         var nodes = [];
22787         var other_nodes = [];
22788         var has_other_nodes = false;
22789         for (var i=0;i<ar.length;i++) {
22790             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22791                 continue;
22792             }
22793             // fullly contained node.
22794             
22795             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22796                 nodes.push(ar[i]);
22797                 continue;
22798             }
22799             
22800             // probably selected..
22801             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22802                 other_nodes.push(ar[i]);
22803                 continue;
22804             }
22805             // outer..
22806             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22807                 continue;
22808             }
22809             
22810             
22811             has_other_nodes = true;
22812         }
22813         if (!nodes.length && other_nodes.length) {
22814             nodes= other_nodes;
22815         }
22816         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22817             return false;
22818         }
22819         
22820         return nodes[0];
22821     },
22822     createRange: function(sel)
22823     {
22824         // this has strange effects when using with 
22825         // top toolbar - not sure if it's a great idea.
22826         //this.editor.contentWindow.focus();
22827         if (typeof sel != "undefined") {
22828             try {
22829                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22830             } catch(e) {
22831                 return this.doc.createRange();
22832             }
22833         } else {
22834             return this.doc.createRange();
22835         }
22836     },
22837     getParentElement: function()
22838     {
22839         
22840         this.assignDocWin();
22841         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22842         
22843         var range = this.createRange(sel);
22844          
22845         try {
22846             var p = range.commonAncestorContainer;
22847             while (p.nodeType == 3) { // text node
22848                 p = p.parentNode;
22849             }
22850             return p;
22851         } catch (e) {
22852             return null;
22853         }
22854     
22855     },
22856     /***
22857      *
22858      * Range intersection.. the hard stuff...
22859      *  '-1' = before
22860      *  '0' = hits..
22861      *  '1' = after.
22862      *         [ -- selected range --- ]
22863      *   [fail]                        [fail]
22864      *
22865      *    basically..
22866      *      if end is before start or  hits it. fail.
22867      *      if start is after end or hits it fail.
22868      *
22869      *   if either hits (but other is outside. - then it's not 
22870      *   
22871      *    
22872      **/
22873     
22874     
22875     // @see http://www.thismuchiknow.co.uk/?p=64.
22876     rangeIntersectsNode : function(range, node)
22877     {
22878         var nodeRange = node.ownerDocument.createRange();
22879         try {
22880             nodeRange.selectNode(node);
22881         } catch (e) {
22882             nodeRange.selectNodeContents(node);
22883         }
22884     
22885         var rangeStartRange = range.cloneRange();
22886         rangeStartRange.collapse(true);
22887     
22888         var rangeEndRange = range.cloneRange();
22889         rangeEndRange.collapse(false);
22890     
22891         var nodeStartRange = nodeRange.cloneRange();
22892         nodeStartRange.collapse(true);
22893     
22894         var nodeEndRange = nodeRange.cloneRange();
22895         nodeEndRange.collapse(false);
22896     
22897         return rangeStartRange.compareBoundaryPoints(
22898                  Range.START_TO_START, nodeEndRange) == -1 &&
22899                rangeEndRange.compareBoundaryPoints(
22900                  Range.START_TO_START, nodeStartRange) == 1;
22901         
22902          
22903     },
22904     rangeCompareNode : function(range, node)
22905     {
22906         var nodeRange = node.ownerDocument.createRange();
22907         try {
22908             nodeRange.selectNode(node);
22909         } catch (e) {
22910             nodeRange.selectNodeContents(node);
22911         }
22912         
22913         
22914         range.collapse(true);
22915     
22916         nodeRange.collapse(true);
22917      
22918         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22919         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22920          
22921         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22922         
22923         var nodeIsBefore   =  ss == 1;
22924         var nodeIsAfter    = ee == -1;
22925         
22926         if (nodeIsBefore && nodeIsAfter) {
22927             return 0; // outer
22928         }
22929         if (!nodeIsBefore && nodeIsAfter) {
22930             return 1; //right trailed.
22931         }
22932         
22933         if (nodeIsBefore && !nodeIsAfter) {
22934             return 2;  // left trailed.
22935         }
22936         // fully contined.
22937         return 3;
22938     },
22939
22940     // private? - in a new class?
22941     cleanUpPaste :  function()
22942     {
22943         // cleans up the whole document..
22944         Roo.log('cleanuppaste');
22945         
22946         this.cleanUpChildren(this.doc.body);
22947         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22948         if (clean != this.doc.body.innerHTML) {
22949             this.doc.body.innerHTML = clean;
22950         }
22951         
22952     },
22953     
22954     cleanWordChars : function(input) {// change the chars to hex code
22955         var he = Roo.HtmlEditorCore;
22956         
22957         var output = input;
22958         Roo.each(he.swapCodes, function(sw) { 
22959             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22960             
22961             output = output.replace(swapper, sw[1]);
22962         });
22963         
22964         return output;
22965     },
22966     
22967     
22968     cleanUpChildren : function (n)
22969     {
22970         if (!n.childNodes.length) {
22971             return;
22972         }
22973         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22974            this.cleanUpChild(n.childNodes[i]);
22975         }
22976     },
22977     
22978     
22979         
22980     
22981     cleanUpChild : function (node)
22982     {
22983         var ed = this;
22984         //console.log(node);
22985         if (node.nodeName == "#text") {
22986             // clean up silly Windows -- stuff?
22987             return; 
22988         }
22989         if (node.nodeName == "#comment") {
22990             node.parentNode.removeChild(node);
22991             // clean up silly Windows -- stuff?
22992             return; 
22993         }
22994         var lcname = node.tagName.toLowerCase();
22995         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22996         // whitelist of tags..
22997         
22998         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22999             // remove node.
23000             node.parentNode.removeChild(node);
23001             return;
23002             
23003         }
23004         
23005         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
23006         
23007         // remove <a name=....> as rendering on yahoo mailer is borked with this.
23008         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
23009         
23010         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
23011         //    remove_keep_children = true;
23012         //}
23013         
23014         if (remove_keep_children) {
23015             this.cleanUpChildren(node);
23016             // inserts everything just before this node...
23017             while (node.childNodes.length) {
23018                 var cn = node.childNodes[0];
23019                 node.removeChild(cn);
23020                 node.parentNode.insertBefore(cn, node);
23021             }
23022             node.parentNode.removeChild(node);
23023             return;
23024         }
23025         
23026         if (!node.attributes || !node.attributes.length) {
23027             this.cleanUpChildren(node);
23028             return;
23029         }
23030         
23031         function cleanAttr(n,v)
23032         {
23033             
23034             if (v.match(/^\./) || v.match(/^\//)) {
23035                 return;
23036             }
23037             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
23038                 return;
23039             }
23040             if (v.match(/^#/)) {
23041                 return;
23042             }
23043 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
23044             node.removeAttribute(n);
23045             
23046         }
23047         
23048         var cwhite = this.cwhite;
23049         var cblack = this.cblack;
23050             
23051         function cleanStyle(n,v)
23052         {
23053             if (v.match(/expression/)) { //XSS?? should we even bother..
23054                 node.removeAttribute(n);
23055                 return;
23056             }
23057             
23058             var parts = v.split(/;/);
23059             var clean = [];
23060             
23061             Roo.each(parts, function(p) {
23062                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
23063                 if (!p.length) {
23064                     return true;
23065                 }
23066                 var l = p.split(':').shift().replace(/\s+/g,'');
23067                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
23068                 
23069                 if ( cwhite.length && cblack.indexOf(l) > -1) {
23070 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23071                     //node.removeAttribute(n);
23072                     return true;
23073                 }
23074                 //Roo.log()
23075                 // only allow 'c whitelisted system attributes'
23076                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
23077 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23078                     //node.removeAttribute(n);
23079                     return true;
23080                 }
23081                 
23082                 
23083                  
23084                 
23085                 clean.push(p);
23086                 return true;
23087             });
23088             if (clean.length) { 
23089                 node.setAttribute(n, clean.join(';'));
23090             } else {
23091                 node.removeAttribute(n);
23092             }
23093             
23094         }
23095         
23096         
23097         for (var i = node.attributes.length-1; i > -1 ; i--) {
23098             var a = node.attributes[i];
23099             //console.log(a);
23100             
23101             if (a.name.toLowerCase().substr(0,2)=='on')  {
23102                 node.removeAttribute(a.name);
23103                 continue;
23104             }
23105             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
23106                 node.removeAttribute(a.name);
23107                 continue;
23108             }
23109             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
23110                 cleanAttr(a.name,a.value); // fixme..
23111                 continue;
23112             }
23113             if (a.name == 'style') {
23114                 cleanStyle(a.name,a.value);
23115                 continue;
23116             }
23117             /// clean up MS crap..
23118             // tecnically this should be a list of valid class'es..
23119             
23120             
23121             if (a.name == 'class') {
23122                 if (a.value.match(/^Mso/)) {
23123                     node.className = '';
23124                 }
23125                 
23126                 if (a.value.match(/^body$/)) {
23127                     node.className = '';
23128                 }
23129                 continue;
23130             }
23131             
23132             // style cleanup!?
23133             // class cleanup?
23134             
23135         }
23136         
23137         
23138         this.cleanUpChildren(node);
23139         
23140         
23141     },
23142     
23143     /**
23144      * Clean up MS wordisms...
23145      */
23146     cleanWord : function(node)
23147     {
23148         
23149         
23150         if (!node) {
23151             this.cleanWord(this.doc.body);
23152             return;
23153         }
23154         if (node.nodeName == "#text") {
23155             // clean up silly Windows -- stuff?
23156             return; 
23157         }
23158         if (node.nodeName == "#comment") {
23159             node.parentNode.removeChild(node);
23160             // clean up silly Windows -- stuff?
23161             return; 
23162         }
23163         
23164         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
23165             node.parentNode.removeChild(node);
23166             return;
23167         }
23168         
23169         // remove - but keep children..
23170         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
23171             while (node.childNodes.length) {
23172                 var cn = node.childNodes[0];
23173                 node.removeChild(cn);
23174                 node.parentNode.insertBefore(cn, node);
23175             }
23176             node.parentNode.removeChild(node);
23177             this.iterateChildren(node, this.cleanWord);
23178             return;
23179         }
23180         // clean styles
23181         if (node.className.length) {
23182             
23183             var cn = node.className.split(/\W+/);
23184             var cna = [];
23185             Roo.each(cn, function(cls) {
23186                 if (cls.match(/Mso[a-zA-Z]+/)) {
23187                     return;
23188                 }
23189                 cna.push(cls);
23190             });
23191             node.className = cna.length ? cna.join(' ') : '';
23192             if (!cna.length) {
23193                 node.removeAttribute("class");
23194             }
23195         }
23196         
23197         if (node.hasAttribute("lang")) {
23198             node.removeAttribute("lang");
23199         }
23200         
23201         if (node.hasAttribute("style")) {
23202             
23203             var styles = node.getAttribute("style").split(";");
23204             var nstyle = [];
23205             Roo.each(styles, function(s) {
23206                 if (!s.match(/:/)) {
23207                     return;
23208                 }
23209                 var kv = s.split(":");
23210                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
23211                     return;
23212                 }
23213                 // what ever is left... we allow.
23214                 nstyle.push(s);
23215             });
23216             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23217             if (!nstyle.length) {
23218                 node.removeAttribute('style');
23219             }
23220         }
23221         this.iterateChildren(node, this.cleanWord);
23222         
23223         
23224         
23225     },
23226     /**
23227      * iterateChildren of a Node, calling fn each time, using this as the scole..
23228      * @param {DomNode} node node to iterate children of.
23229      * @param {Function} fn method of this class to call on each item.
23230      */
23231     iterateChildren : function(node, fn)
23232     {
23233         if (!node.childNodes.length) {
23234                 return;
23235         }
23236         for (var i = node.childNodes.length-1; i > -1 ; i--) {
23237            fn.call(this, node.childNodes[i])
23238         }
23239     },
23240     
23241     
23242     /**
23243      * cleanTableWidths.
23244      *
23245      * Quite often pasting from word etc.. results in tables with column and widths.
23246      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23247      *
23248      */
23249     cleanTableWidths : function(node)
23250     {
23251          
23252          
23253         if (!node) {
23254             this.cleanTableWidths(this.doc.body);
23255             return;
23256         }
23257         
23258         // ignore list...
23259         if (node.nodeName == "#text" || node.nodeName == "#comment") {
23260             return; 
23261         }
23262         Roo.log(node.tagName);
23263         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23264             this.iterateChildren(node, this.cleanTableWidths);
23265             return;
23266         }
23267         if (node.hasAttribute('width')) {
23268             node.removeAttribute('width');
23269         }
23270         
23271          
23272         if (node.hasAttribute("style")) {
23273             // pretty basic...
23274             
23275             var styles = node.getAttribute("style").split(";");
23276             var nstyle = [];
23277             Roo.each(styles, function(s) {
23278                 if (!s.match(/:/)) {
23279                     return;
23280                 }
23281                 var kv = s.split(":");
23282                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23283                     return;
23284                 }
23285                 // what ever is left... we allow.
23286                 nstyle.push(s);
23287             });
23288             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23289             if (!nstyle.length) {
23290                 node.removeAttribute('style');
23291             }
23292         }
23293         
23294         this.iterateChildren(node, this.cleanTableWidths);
23295         
23296         
23297     },
23298     
23299     
23300     
23301     
23302     domToHTML : function(currentElement, depth, nopadtext) {
23303         
23304         depth = depth || 0;
23305         nopadtext = nopadtext || false;
23306     
23307         if (!currentElement) {
23308             return this.domToHTML(this.doc.body);
23309         }
23310         
23311         //Roo.log(currentElement);
23312         var j;
23313         var allText = false;
23314         var nodeName = currentElement.nodeName;
23315         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23316         
23317         if  (nodeName == '#text') {
23318             
23319             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23320         }
23321         
23322         
23323         var ret = '';
23324         if (nodeName != 'BODY') {
23325              
23326             var i = 0;
23327             // Prints the node tagName, such as <A>, <IMG>, etc
23328             if (tagName) {
23329                 var attr = [];
23330                 for(i = 0; i < currentElement.attributes.length;i++) {
23331                     // quoting?
23332                     var aname = currentElement.attributes.item(i).name;
23333                     if (!currentElement.attributes.item(i).value.length) {
23334                         continue;
23335                     }
23336                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23337                 }
23338                 
23339                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23340             } 
23341             else {
23342                 
23343                 // eack
23344             }
23345         } else {
23346             tagName = false;
23347         }
23348         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23349             return ret;
23350         }
23351         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23352             nopadtext = true;
23353         }
23354         
23355         
23356         // Traverse the tree
23357         i = 0;
23358         var currentElementChild = currentElement.childNodes.item(i);
23359         var allText = true;
23360         var innerHTML  = '';
23361         lastnode = '';
23362         while (currentElementChild) {
23363             // Formatting code (indent the tree so it looks nice on the screen)
23364             var nopad = nopadtext;
23365             if (lastnode == 'SPAN') {
23366                 nopad  = true;
23367             }
23368             // text
23369             if  (currentElementChild.nodeName == '#text') {
23370                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23371                 toadd = nopadtext ? toadd : toadd.trim();
23372                 if (!nopad && toadd.length > 80) {
23373                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23374                 }
23375                 innerHTML  += toadd;
23376                 
23377                 i++;
23378                 currentElementChild = currentElement.childNodes.item(i);
23379                 lastNode = '';
23380                 continue;
23381             }
23382             allText = false;
23383             
23384             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23385                 
23386             // Recursively traverse the tree structure of the child node
23387             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23388             lastnode = currentElementChild.nodeName;
23389             i++;
23390             currentElementChild=currentElement.childNodes.item(i);
23391         }
23392         
23393         ret += innerHTML;
23394         
23395         if (!allText) {
23396                 // The remaining code is mostly for formatting the tree
23397             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23398         }
23399         
23400         
23401         if (tagName) {
23402             ret+= "</"+tagName+">";
23403         }
23404         return ret;
23405         
23406     },
23407         
23408     applyBlacklists : function()
23409     {
23410         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23411         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23412         
23413         this.white = [];
23414         this.black = [];
23415         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23416             if (b.indexOf(tag) > -1) {
23417                 return;
23418             }
23419             this.white.push(tag);
23420             
23421         }, this);
23422         
23423         Roo.each(w, function(tag) {
23424             if (b.indexOf(tag) > -1) {
23425                 return;
23426             }
23427             if (this.white.indexOf(tag) > -1) {
23428                 return;
23429             }
23430             this.white.push(tag);
23431             
23432         }, this);
23433         
23434         
23435         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23436             if (w.indexOf(tag) > -1) {
23437                 return;
23438             }
23439             this.black.push(tag);
23440             
23441         }, this);
23442         
23443         Roo.each(b, function(tag) {
23444             if (w.indexOf(tag) > -1) {
23445                 return;
23446             }
23447             if (this.black.indexOf(tag) > -1) {
23448                 return;
23449             }
23450             this.black.push(tag);
23451             
23452         }, this);
23453         
23454         
23455         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23456         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23457         
23458         this.cwhite = [];
23459         this.cblack = [];
23460         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23461             if (b.indexOf(tag) > -1) {
23462                 return;
23463             }
23464             this.cwhite.push(tag);
23465             
23466         }, this);
23467         
23468         Roo.each(w, function(tag) {
23469             if (b.indexOf(tag) > -1) {
23470                 return;
23471             }
23472             if (this.cwhite.indexOf(tag) > -1) {
23473                 return;
23474             }
23475             this.cwhite.push(tag);
23476             
23477         }, this);
23478         
23479         
23480         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23481             if (w.indexOf(tag) > -1) {
23482                 return;
23483             }
23484             this.cblack.push(tag);
23485             
23486         }, this);
23487         
23488         Roo.each(b, function(tag) {
23489             if (w.indexOf(tag) > -1) {
23490                 return;
23491             }
23492             if (this.cblack.indexOf(tag) > -1) {
23493                 return;
23494             }
23495             this.cblack.push(tag);
23496             
23497         }, this);
23498     },
23499     
23500     setStylesheets : function(stylesheets)
23501     {
23502         if(typeof(stylesheets) == 'string'){
23503             Roo.get(this.iframe.contentDocument.head).createChild({
23504                 tag : 'link',
23505                 rel : 'stylesheet',
23506                 type : 'text/css',
23507                 href : stylesheets
23508             });
23509             
23510             return;
23511         }
23512         var _this = this;
23513      
23514         Roo.each(stylesheets, function(s) {
23515             if(!s.length){
23516                 return;
23517             }
23518             
23519             Roo.get(_this.iframe.contentDocument.head).createChild({
23520                 tag : 'link',
23521                 rel : 'stylesheet',
23522                 type : 'text/css',
23523                 href : s
23524             });
23525         });
23526
23527         
23528     },
23529     
23530     removeStylesheets : function()
23531     {
23532         var _this = this;
23533         
23534         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23535             s.remove();
23536         });
23537     },
23538     
23539     setStyle : function(style)
23540     {
23541         Roo.get(this.iframe.contentDocument.head).createChild({
23542             tag : 'style',
23543             type : 'text/css',
23544             html : style
23545         });
23546
23547         return;
23548     }
23549     
23550     // hide stuff that is not compatible
23551     /**
23552      * @event blur
23553      * @hide
23554      */
23555     /**
23556      * @event change
23557      * @hide
23558      */
23559     /**
23560      * @event focus
23561      * @hide
23562      */
23563     /**
23564      * @event specialkey
23565      * @hide
23566      */
23567     /**
23568      * @cfg {String} fieldClass @hide
23569      */
23570     /**
23571      * @cfg {String} focusClass @hide
23572      */
23573     /**
23574      * @cfg {String} autoCreate @hide
23575      */
23576     /**
23577      * @cfg {String} inputType @hide
23578      */
23579     /**
23580      * @cfg {String} invalidClass @hide
23581      */
23582     /**
23583      * @cfg {String} invalidText @hide
23584      */
23585     /**
23586      * @cfg {String} msgFx @hide
23587      */
23588     /**
23589      * @cfg {String} validateOnBlur @hide
23590      */
23591 });
23592
23593 Roo.HtmlEditorCore.white = [
23594         'area', 'br', 'img', 'input', 'hr', 'wbr',
23595         
23596        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23597        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23598        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23599        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23600        'table',   'ul',         'xmp', 
23601        
23602        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23603       'thead',   'tr', 
23604      
23605       'dir', 'menu', 'ol', 'ul', 'dl',
23606        
23607       'embed',  'object'
23608 ];
23609
23610
23611 Roo.HtmlEditorCore.black = [
23612     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23613         'applet', // 
23614         'base',   'basefont', 'bgsound', 'blink',  'body', 
23615         'frame',  'frameset', 'head',    'html',   'ilayer', 
23616         'iframe', 'layer',  'link',     'meta',    'object',   
23617         'script', 'style' ,'title',  'xml' // clean later..
23618 ];
23619 Roo.HtmlEditorCore.clean = [
23620     'script', 'style', 'title', 'xml'
23621 ];
23622 Roo.HtmlEditorCore.remove = [
23623     'font'
23624 ];
23625 // attributes..
23626
23627 Roo.HtmlEditorCore.ablack = [
23628     'on'
23629 ];
23630     
23631 Roo.HtmlEditorCore.aclean = [ 
23632     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23633 ];
23634
23635 // protocols..
23636 Roo.HtmlEditorCore.pwhite= [
23637         'http',  'https',  'mailto'
23638 ];
23639
23640 // white listed style attributes.
23641 Roo.HtmlEditorCore.cwhite= [
23642       //  'text-align', /// default is to allow most things..
23643       
23644          
23645 //        'font-size'//??
23646 ];
23647
23648 // black listed style attributes.
23649 Roo.HtmlEditorCore.cblack= [
23650       //  'font-size' -- this can be set by the project 
23651 ];
23652
23653
23654 Roo.HtmlEditorCore.swapCodes   =[ 
23655     [    8211, "--" ], 
23656     [    8212, "--" ], 
23657     [    8216,  "'" ],  
23658     [    8217, "'" ],  
23659     [    8220, '"' ],  
23660     [    8221, '"' ],  
23661     [    8226, "*" ],  
23662     [    8230, "..." ]
23663 ]; 
23664
23665     /*
23666  * - LGPL
23667  *
23668  * HtmlEditor
23669  * 
23670  */
23671
23672 /**
23673  * @class Roo.bootstrap.HtmlEditor
23674  * @extends Roo.bootstrap.TextArea
23675  * Bootstrap HtmlEditor class
23676
23677  * @constructor
23678  * Create a new HtmlEditor
23679  * @param {Object} config The config object
23680  */
23681
23682 Roo.bootstrap.HtmlEditor = function(config){
23683     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23684     if (!this.toolbars) {
23685         this.toolbars = [];
23686     }
23687     
23688     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23689     this.addEvents({
23690             /**
23691              * @event initialize
23692              * Fires when the editor is fully initialized (including the iframe)
23693              * @param {HtmlEditor} this
23694              */
23695             initialize: true,
23696             /**
23697              * @event activate
23698              * Fires when the editor is first receives the focus. Any insertion must wait
23699              * until after this event.
23700              * @param {HtmlEditor} this
23701              */
23702             activate: true,
23703              /**
23704              * @event beforesync
23705              * Fires before the textarea is updated with content from the editor iframe. Return false
23706              * to cancel the sync.
23707              * @param {HtmlEditor} this
23708              * @param {String} html
23709              */
23710             beforesync: true,
23711              /**
23712              * @event beforepush
23713              * Fires before the iframe editor is updated with content from the textarea. Return false
23714              * to cancel the push.
23715              * @param {HtmlEditor} this
23716              * @param {String} html
23717              */
23718             beforepush: true,
23719              /**
23720              * @event sync
23721              * Fires when the textarea is updated with content from the editor iframe.
23722              * @param {HtmlEditor} this
23723              * @param {String} html
23724              */
23725             sync: true,
23726              /**
23727              * @event push
23728              * Fires when the iframe editor is updated with content from the textarea.
23729              * @param {HtmlEditor} this
23730              * @param {String} html
23731              */
23732             push: true,
23733              /**
23734              * @event editmodechange
23735              * Fires when the editor switches edit modes
23736              * @param {HtmlEditor} this
23737              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23738              */
23739             editmodechange: true,
23740             /**
23741              * @event editorevent
23742              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23743              * @param {HtmlEditor} this
23744              */
23745             editorevent: true,
23746             /**
23747              * @event firstfocus
23748              * Fires when on first focus - needed by toolbars..
23749              * @param {HtmlEditor} this
23750              */
23751             firstfocus: true,
23752             /**
23753              * @event autosave
23754              * Auto save the htmlEditor value as a file into Events
23755              * @param {HtmlEditor} this
23756              */
23757             autosave: true,
23758             /**
23759              * @event savedpreview
23760              * preview the saved version of htmlEditor
23761              * @param {HtmlEditor} this
23762              */
23763             savedpreview: true
23764         });
23765 };
23766
23767
23768 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23769     
23770     
23771       /**
23772      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23773      */
23774     toolbars : false,
23775     
23776      /**
23777     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23778     */
23779     btns : [],
23780    
23781      /**
23782      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23783      *                        Roo.resizable.
23784      */
23785     resizable : false,
23786      /**
23787      * @cfg {Number} height (in pixels)
23788      */   
23789     height: 300,
23790    /**
23791      * @cfg {Number} width (in pixels)
23792      */   
23793     width: false,
23794     
23795     /**
23796      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23797      * 
23798      */
23799     stylesheets: false,
23800     
23801     // id of frame..
23802     frameId: false,
23803     
23804     // private properties
23805     validationEvent : false,
23806     deferHeight: true,
23807     initialized : false,
23808     activated : false,
23809     
23810     onFocus : Roo.emptyFn,
23811     iframePad:3,
23812     hideMode:'offsets',
23813     
23814     tbContainer : false,
23815     
23816     bodyCls : '',
23817     
23818     toolbarContainer :function() {
23819         return this.wrap.select('.x-html-editor-tb',true).first();
23820     },
23821
23822     /**
23823      * Protected method that will not generally be called directly. It
23824      * is called when the editor creates its toolbar. Override this method if you need to
23825      * add custom toolbar buttons.
23826      * @param {HtmlEditor} editor
23827      */
23828     createToolbar : function(){
23829         Roo.log('renewing');
23830         Roo.log("create toolbars");
23831         
23832         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23833         this.toolbars[0].render(this.toolbarContainer());
23834         
23835         return;
23836         
23837 //        if (!editor.toolbars || !editor.toolbars.length) {
23838 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23839 //        }
23840 //        
23841 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23842 //            editor.toolbars[i] = Roo.factory(
23843 //                    typeof(editor.toolbars[i]) == 'string' ?
23844 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23845 //                Roo.bootstrap.HtmlEditor);
23846 //            editor.toolbars[i].init(editor);
23847 //        }
23848     },
23849
23850      
23851     // private
23852     onRender : function(ct, position)
23853     {
23854        // Roo.log("Call onRender: " + this.xtype);
23855         var _t = this;
23856         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23857       
23858         this.wrap = this.inputEl().wrap({
23859             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23860         });
23861         
23862         this.editorcore.onRender(ct, position);
23863          
23864         if (this.resizable) {
23865             this.resizeEl = new Roo.Resizable(this.wrap, {
23866                 pinned : true,
23867                 wrap: true,
23868                 dynamic : true,
23869                 minHeight : this.height,
23870                 height: this.height,
23871                 handles : this.resizable,
23872                 width: this.width,
23873                 listeners : {
23874                     resize : function(r, w, h) {
23875                         _t.onResize(w,h); // -something
23876                     }
23877                 }
23878             });
23879             
23880         }
23881         this.createToolbar(this);
23882        
23883         
23884         if(!this.width && this.resizable){
23885             this.setSize(this.wrap.getSize());
23886         }
23887         if (this.resizeEl) {
23888             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23889             // should trigger onReize..
23890         }
23891         
23892     },
23893
23894     // private
23895     onResize : function(w, h)
23896     {
23897         Roo.log('resize: ' +w + ',' + h );
23898         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23899         var ew = false;
23900         var eh = false;
23901         
23902         if(this.inputEl() ){
23903             if(typeof w == 'number'){
23904                 var aw = w - this.wrap.getFrameWidth('lr');
23905                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23906                 ew = aw;
23907             }
23908             if(typeof h == 'number'){
23909                  var tbh = -11;  // fixme it needs to tool bar size!
23910                 for (var i =0; i < this.toolbars.length;i++) {
23911                     // fixme - ask toolbars for heights?
23912                     tbh += this.toolbars[i].el.getHeight();
23913                     //if (this.toolbars[i].footer) {
23914                     //    tbh += this.toolbars[i].footer.el.getHeight();
23915                     //}
23916                 }
23917               
23918                 
23919                 
23920                 
23921                 
23922                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23923                 ah -= 5; // knock a few pixes off for look..
23924                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23925                 var eh = ah;
23926             }
23927         }
23928         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23929         this.editorcore.onResize(ew,eh);
23930         
23931     },
23932
23933     /**
23934      * Toggles the editor between standard and source edit mode.
23935      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23936      */
23937     toggleSourceEdit : function(sourceEditMode)
23938     {
23939         this.editorcore.toggleSourceEdit(sourceEditMode);
23940         
23941         if(this.editorcore.sourceEditMode){
23942             Roo.log('editor - showing textarea');
23943             
23944 //            Roo.log('in');
23945 //            Roo.log(this.syncValue());
23946             this.syncValue();
23947             this.inputEl().removeClass(['hide', 'x-hidden']);
23948             this.inputEl().dom.removeAttribute('tabIndex');
23949             this.inputEl().focus();
23950         }else{
23951             Roo.log('editor - hiding textarea');
23952 //            Roo.log('out')
23953 //            Roo.log(this.pushValue()); 
23954             this.pushValue();
23955             
23956             this.inputEl().addClass(['hide', 'x-hidden']);
23957             this.inputEl().dom.setAttribute('tabIndex', -1);
23958             //this.deferFocus();
23959         }
23960          
23961         if(this.resizable){
23962             this.setSize(this.wrap.getSize());
23963         }
23964         
23965         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23966     },
23967  
23968     // private (for BoxComponent)
23969     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23970
23971     // private (for BoxComponent)
23972     getResizeEl : function(){
23973         return this.wrap;
23974     },
23975
23976     // private (for BoxComponent)
23977     getPositionEl : function(){
23978         return this.wrap;
23979     },
23980
23981     // private
23982     initEvents : function(){
23983         this.originalValue = this.getValue();
23984     },
23985
23986 //    /**
23987 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23988 //     * @method
23989 //     */
23990 //    markInvalid : Roo.emptyFn,
23991 //    /**
23992 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23993 //     * @method
23994 //     */
23995 //    clearInvalid : Roo.emptyFn,
23996
23997     setValue : function(v){
23998         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23999         this.editorcore.pushValue();
24000     },
24001
24002      
24003     // private
24004     deferFocus : function(){
24005         this.focus.defer(10, this);
24006     },
24007
24008     // doc'ed in Field
24009     focus : function(){
24010         this.editorcore.focus();
24011         
24012     },
24013       
24014
24015     // private
24016     onDestroy : function(){
24017         
24018         
24019         
24020         if(this.rendered){
24021             
24022             for (var i =0; i < this.toolbars.length;i++) {
24023                 // fixme - ask toolbars for heights?
24024                 this.toolbars[i].onDestroy();
24025             }
24026             
24027             this.wrap.dom.innerHTML = '';
24028             this.wrap.remove();
24029         }
24030     },
24031
24032     // private
24033     onFirstFocus : function(){
24034         //Roo.log("onFirstFocus");
24035         this.editorcore.onFirstFocus();
24036          for (var i =0; i < this.toolbars.length;i++) {
24037             this.toolbars[i].onFirstFocus();
24038         }
24039         
24040     },
24041     
24042     // private
24043     syncValue : function()
24044     {   
24045         this.editorcore.syncValue();
24046     },
24047     
24048     pushValue : function()
24049     {   
24050         this.editorcore.pushValue();
24051     }
24052      
24053     
24054     // hide stuff that is not compatible
24055     /**
24056      * @event blur
24057      * @hide
24058      */
24059     /**
24060      * @event change
24061      * @hide
24062      */
24063     /**
24064      * @event focus
24065      * @hide
24066      */
24067     /**
24068      * @event specialkey
24069      * @hide
24070      */
24071     /**
24072      * @cfg {String} fieldClass @hide
24073      */
24074     /**
24075      * @cfg {String} focusClass @hide
24076      */
24077     /**
24078      * @cfg {String} autoCreate @hide
24079      */
24080     /**
24081      * @cfg {String} inputType @hide
24082      */
24083      
24084     /**
24085      * @cfg {String} invalidText @hide
24086      */
24087     /**
24088      * @cfg {String} msgFx @hide
24089      */
24090     /**
24091      * @cfg {String} validateOnBlur @hide
24092      */
24093 });
24094  
24095     
24096    
24097    
24098    
24099       
24100 Roo.namespace('Roo.bootstrap.htmleditor');
24101 /**
24102  * @class Roo.bootstrap.HtmlEditorToolbar1
24103  * Basic Toolbar
24104  * 
24105  * @example
24106  * Usage:
24107  *
24108  new Roo.bootstrap.HtmlEditor({
24109     ....
24110     toolbars : [
24111         new Roo.bootstrap.HtmlEditorToolbar1({
24112             disable : { fonts: 1 , format: 1, ..., ... , ...],
24113             btns : [ .... ]
24114         })
24115     }
24116      
24117  * 
24118  * @cfg {Object} disable List of elements to disable..
24119  * @cfg {Array} btns List of additional buttons.
24120  * 
24121  * 
24122  * NEEDS Extra CSS? 
24123  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24124  */
24125  
24126 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
24127 {
24128     
24129     Roo.apply(this, config);
24130     
24131     // default disabled, based on 'good practice'..
24132     this.disable = this.disable || {};
24133     Roo.applyIf(this.disable, {
24134         fontSize : true,
24135         colors : true,
24136         specialElements : true
24137     });
24138     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
24139     
24140     this.editor = config.editor;
24141     this.editorcore = config.editor.editorcore;
24142     
24143     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
24144     
24145     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24146     // dont call parent... till later.
24147 }
24148 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
24149      
24150     bar : true,
24151     
24152     editor : false,
24153     editorcore : false,
24154     
24155     
24156     formats : [
24157         "p" ,  
24158         "h1","h2","h3","h4","h5","h6", 
24159         "pre", "code", 
24160         "abbr", "acronym", "address", "cite", "samp", "var",
24161         'div','span'
24162     ],
24163     
24164     onRender : function(ct, position)
24165     {
24166        // Roo.log("Call onRender: " + this.xtype);
24167         
24168        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
24169        Roo.log(this.el);
24170        this.el.dom.style.marginBottom = '0';
24171        var _this = this;
24172        var editorcore = this.editorcore;
24173        var editor= this.editor;
24174        
24175        var children = [];
24176        var btn = function(id,cmd , toggle, handler, html){
24177        
24178             var  event = toggle ? 'toggle' : 'click';
24179        
24180             var a = {
24181                 size : 'sm',
24182                 xtype: 'Button',
24183                 xns: Roo.bootstrap,
24184                 //glyphicon : id,
24185                 fa: id,
24186                 cmd : id || cmd,
24187                 enableToggle:toggle !== false,
24188                 html : html || '',
24189                 pressed : toggle ? false : null,
24190                 listeners : {}
24191             };
24192             a.listeners[toggle ? 'toggle' : 'click'] = function() {
24193                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
24194             };
24195             children.push(a);
24196             return a;
24197        }
24198        
24199     //    var cb_box = function...
24200         
24201         var style = {
24202                 xtype: 'Button',
24203                 size : 'sm',
24204                 xns: Roo.bootstrap,
24205                 fa : 'font',
24206                 //html : 'submit'
24207                 menu : {
24208                     xtype: 'Menu',
24209                     xns: Roo.bootstrap,
24210                     items:  []
24211                 }
24212         };
24213         Roo.each(this.formats, function(f) {
24214             style.menu.items.push({
24215                 xtype :'MenuItem',
24216                 xns: Roo.bootstrap,
24217                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24218                 tagname : f,
24219                 listeners : {
24220                     click : function()
24221                     {
24222                         editorcore.insertTag(this.tagname);
24223                         editor.focus();
24224                     }
24225                 }
24226                 
24227             });
24228         });
24229         children.push(style);   
24230         
24231         btn('bold',false,true);
24232         btn('italic',false,true);
24233         btn('align-left', 'justifyleft',true);
24234         btn('align-center', 'justifycenter',true);
24235         btn('align-right' , 'justifyright',true);
24236         btn('link', false, false, function(btn) {
24237             //Roo.log("create link?");
24238             var url = prompt(this.createLinkText, this.defaultLinkValue);
24239             if(url && url != 'http:/'+'/'){
24240                 this.editorcore.relayCmd('createlink', url);
24241             }
24242         }),
24243         btn('list','insertunorderedlist',true);
24244         btn('pencil', false,true, function(btn){
24245                 Roo.log(this);
24246                 this.toggleSourceEdit(btn.pressed);
24247         });
24248         
24249         if (this.editor.btns.length > 0) {
24250             for (var i = 0; i<this.editor.btns.length; i++) {
24251                 children.push(this.editor.btns[i]);
24252             }
24253         }
24254         
24255         /*
24256         var cog = {
24257                 xtype: 'Button',
24258                 size : 'sm',
24259                 xns: Roo.bootstrap,
24260                 glyphicon : 'cog',
24261                 //html : 'submit'
24262                 menu : {
24263                     xtype: 'Menu',
24264                     xns: Roo.bootstrap,
24265                     items:  []
24266                 }
24267         };
24268         
24269         cog.menu.items.push({
24270             xtype :'MenuItem',
24271             xns: Roo.bootstrap,
24272             html : Clean styles,
24273             tagname : f,
24274             listeners : {
24275                 click : function()
24276                 {
24277                     editorcore.insertTag(this.tagname);
24278                     editor.focus();
24279                 }
24280             }
24281             
24282         });
24283        */
24284         
24285          
24286        this.xtype = 'NavSimplebar';
24287         
24288         for(var i=0;i< children.length;i++) {
24289             
24290             this.buttons.add(this.addxtypeChild(children[i]));
24291             
24292         }
24293         
24294         editor.on('editorevent', this.updateToolbar, this);
24295     },
24296     onBtnClick : function(id)
24297     {
24298        this.editorcore.relayCmd(id);
24299        this.editorcore.focus();
24300     },
24301     
24302     /**
24303      * Protected method that will not generally be called directly. It triggers
24304      * a toolbar update by reading the markup state of the current selection in the editor.
24305      */
24306     updateToolbar: function(){
24307
24308         if(!this.editorcore.activated){
24309             this.editor.onFirstFocus(); // is this neeed?
24310             return;
24311         }
24312
24313         var btns = this.buttons; 
24314         var doc = this.editorcore.doc;
24315         btns.get('bold').setActive(doc.queryCommandState('bold'));
24316         btns.get('italic').setActive(doc.queryCommandState('italic'));
24317         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24318         
24319         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24320         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24321         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24322         
24323         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24324         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24325          /*
24326         
24327         var ans = this.editorcore.getAllAncestors();
24328         if (this.formatCombo) {
24329             
24330             
24331             var store = this.formatCombo.store;
24332             this.formatCombo.setValue("");
24333             for (var i =0; i < ans.length;i++) {
24334                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24335                     // select it..
24336                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24337                     break;
24338                 }
24339             }
24340         }
24341         
24342         
24343         
24344         // hides menus... - so this cant be on a menu...
24345         Roo.bootstrap.MenuMgr.hideAll();
24346         */
24347         Roo.bootstrap.MenuMgr.hideAll();
24348         //this.editorsyncValue();
24349     },
24350     onFirstFocus: function() {
24351         this.buttons.each(function(item){
24352            item.enable();
24353         });
24354     },
24355     toggleSourceEdit : function(sourceEditMode){
24356         
24357           
24358         if(sourceEditMode){
24359             Roo.log("disabling buttons");
24360            this.buttons.each( function(item){
24361                 if(item.cmd != 'pencil'){
24362                     item.disable();
24363                 }
24364             });
24365           
24366         }else{
24367             Roo.log("enabling buttons");
24368             if(this.editorcore.initialized){
24369                 this.buttons.each( function(item){
24370                     item.enable();
24371                 });
24372             }
24373             
24374         }
24375         Roo.log("calling toggole on editor");
24376         // tell the editor that it's been pressed..
24377         this.editor.toggleSourceEdit(sourceEditMode);
24378        
24379     }
24380 });
24381
24382
24383
24384
24385
24386 /**
24387  * @class Roo.bootstrap.Table.AbstractSelectionModel
24388  * @extends Roo.util.Observable
24389  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24390  * implemented by descendant classes.  This class should not be directly instantiated.
24391  * @constructor
24392  */
24393 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24394     this.locked = false;
24395     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24396 };
24397
24398
24399 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24400     /** @ignore Called by the grid automatically. Do not call directly. */
24401     init : function(grid){
24402         this.grid = grid;
24403         this.initEvents();
24404     },
24405
24406     /**
24407      * Locks the selections.
24408      */
24409     lock : function(){
24410         this.locked = true;
24411     },
24412
24413     /**
24414      * Unlocks the selections.
24415      */
24416     unlock : function(){
24417         this.locked = false;
24418     },
24419
24420     /**
24421      * Returns true if the selections are locked.
24422      * @return {Boolean}
24423      */
24424     isLocked : function(){
24425         return this.locked;
24426     }
24427 });
24428 /**
24429  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24430  * @class Roo.bootstrap.Table.RowSelectionModel
24431  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24432  * It supports multiple selections and keyboard selection/navigation. 
24433  * @constructor
24434  * @param {Object} config
24435  */
24436
24437 Roo.bootstrap.Table.RowSelectionModel = function(config){
24438     Roo.apply(this, config);
24439     this.selections = new Roo.util.MixedCollection(false, function(o){
24440         return o.id;
24441     });
24442
24443     this.last = false;
24444     this.lastActive = false;
24445
24446     this.addEvents({
24447         /**
24448              * @event selectionchange
24449              * Fires when the selection changes
24450              * @param {SelectionModel} this
24451              */
24452             "selectionchange" : true,
24453         /**
24454              * @event afterselectionchange
24455              * Fires after the selection changes (eg. by key press or clicking)
24456              * @param {SelectionModel} this
24457              */
24458             "afterselectionchange" : true,
24459         /**
24460              * @event beforerowselect
24461              * Fires when a row is selected being selected, return false to cancel.
24462              * @param {SelectionModel} this
24463              * @param {Number} rowIndex The selected index
24464              * @param {Boolean} keepExisting False if other selections will be cleared
24465              */
24466             "beforerowselect" : true,
24467         /**
24468              * @event rowselect
24469              * Fires when a row is selected.
24470              * @param {SelectionModel} this
24471              * @param {Number} rowIndex The selected index
24472              * @param {Roo.data.Record} r The record
24473              */
24474             "rowselect" : true,
24475         /**
24476              * @event rowdeselect
24477              * Fires when a row is deselected.
24478              * @param {SelectionModel} this
24479              * @param {Number} rowIndex The selected index
24480              */
24481         "rowdeselect" : true
24482     });
24483     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24484     this.locked = false;
24485  };
24486
24487 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24488     /**
24489      * @cfg {Boolean} singleSelect
24490      * True to allow selection of only one row at a time (defaults to false)
24491      */
24492     singleSelect : false,
24493
24494     // private
24495     initEvents : function()
24496     {
24497
24498         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24499         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24500         //}else{ // allow click to work like normal
24501          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24502         //}
24503         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24504         this.grid.on("rowclick", this.handleMouseDown, this);
24505         
24506         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24507             "up" : function(e){
24508                 if(!e.shiftKey){
24509                     this.selectPrevious(e.shiftKey);
24510                 }else if(this.last !== false && this.lastActive !== false){
24511                     var last = this.last;
24512                     this.selectRange(this.last,  this.lastActive-1);
24513                     this.grid.getView().focusRow(this.lastActive);
24514                     if(last !== false){
24515                         this.last = last;
24516                     }
24517                 }else{
24518                     this.selectFirstRow();
24519                 }
24520                 this.fireEvent("afterselectionchange", this);
24521             },
24522             "down" : function(e){
24523                 if(!e.shiftKey){
24524                     this.selectNext(e.shiftKey);
24525                 }else if(this.last !== false && this.lastActive !== false){
24526                     var last = this.last;
24527                     this.selectRange(this.last,  this.lastActive+1);
24528                     this.grid.getView().focusRow(this.lastActive);
24529                     if(last !== false){
24530                         this.last = last;
24531                     }
24532                 }else{
24533                     this.selectFirstRow();
24534                 }
24535                 this.fireEvent("afterselectionchange", this);
24536             },
24537             scope: this
24538         });
24539         this.grid.store.on('load', function(){
24540             this.selections.clear();
24541         },this);
24542         /*
24543         var view = this.grid.view;
24544         view.on("refresh", this.onRefresh, this);
24545         view.on("rowupdated", this.onRowUpdated, this);
24546         view.on("rowremoved", this.onRemove, this);
24547         */
24548     },
24549
24550     // private
24551     onRefresh : function()
24552     {
24553         var ds = this.grid.store, i, v = this.grid.view;
24554         var s = this.selections;
24555         s.each(function(r){
24556             if((i = ds.indexOfId(r.id)) != -1){
24557                 v.onRowSelect(i);
24558             }else{
24559                 s.remove(r);
24560             }
24561         });
24562     },
24563
24564     // private
24565     onRemove : function(v, index, r){
24566         this.selections.remove(r);
24567     },
24568
24569     // private
24570     onRowUpdated : function(v, index, r){
24571         if(this.isSelected(r)){
24572             v.onRowSelect(index);
24573         }
24574     },
24575
24576     /**
24577      * Select records.
24578      * @param {Array} records The records to select
24579      * @param {Boolean} keepExisting (optional) True to keep existing selections
24580      */
24581     selectRecords : function(records, keepExisting)
24582     {
24583         if(!keepExisting){
24584             this.clearSelections();
24585         }
24586             var ds = this.grid.store;
24587         for(var i = 0, len = records.length; i < len; i++){
24588             this.selectRow(ds.indexOf(records[i]), true);
24589         }
24590     },
24591
24592     /**
24593      * Gets the number of selected rows.
24594      * @return {Number}
24595      */
24596     getCount : function(){
24597         return this.selections.length;
24598     },
24599
24600     /**
24601      * Selects the first row in the grid.
24602      */
24603     selectFirstRow : function(){
24604         this.selectRow(0);
24605     },
24606
24607     /**
24608      * Select the last row.
24609      * @param {Boolean} keepExisting (optional) True to keep existing selections
24610      */
24611     selectLastRow : function(keepExisting){
24612         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24613         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24614     },
24615
24616     /**
24617      * Selects the row immediately following the last selected row.
24618      * @param {Boolean} keepExisting (optional) True to keep existing selections
24619      */
24620     selectNext : function(keepExisting)
24621     {
24622             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24623             this.selectRow(this.last+1, keepExisting);
24624             this.grid.getView().focusRow(this.last);
24625         }
24626     },
24627
24628     /**
24629      * Selects the row that precedes the last selected row.
24630      * @param {Boolean} keepExisting (optional) True to keep existing selections
24631      */
24632     selectPrevious : function(keepExisting){
24633         if(this.last){
24634             this.selectRow(this.last-1, keepExisting);
24635             this.grid.getView().focusRow(this.last);
24636         }
24637     },
24638
24639     /**
24640      * Returns the selected records
24641      * @return {Array} Array of selected records
24642      */
24643     getSelections : function(){
24644         return [].concat(this.selections.items);
24645     },
24646
24647     /**
24648      * Returns the first selected record.
24649      * @return {Record}
24650      */
24651     getSelected : function(){
24652         return this.selections.itemAt(0);
24653     },
24654
24655
24656     /**
24657      * Clears all selections.
24658      */
24659     clearSelections : function(fast)
24660     {
24661         if(this.locked) {
24662             return;
24663         }
24664         if(fast !== true){
24665                 var ds = this.grid.store;
24666             var s = this.selections;
24667             s.each(function(r){
24668                 this.deselectRow(ds.indexOfId(r.id));
24669             }, this);
24670             s.clear();
24671         }else{
24672             this.selections.clear();
24673         }
24674         this.last = false;
24675     },
24676
24677
24678     /**
24679      * Selects all rows.
24680      */
24681     selectAll : function(){
24682         if(this.locked) {
24683             return;
24684         }
24685         this.selections.clear();
24686         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24687             this.selectRow(i, true);
24688         }
24689     },
24690
24691     /**
24692      * Returns True if there is a selection.
24693      * @return {Boolean}
24694      */
24695     hasSelection : function(){
24696         return this.selections.length > 0;
24697     },
24698
24699     /**
24700      * Returns True if the specified row is selected.
24701      * @param {Number/Record} record The record or index of the record to check
24702      * @return {Boolean}
24703      */
24704     isSelected : function(index){
24705             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24706         return (r && this.selections.key(r.id) ? true : false);
24707     },
24708
24709     /**
24710      * Returns True if the specified record id is selected.
24711      * @param {String} id The id of record to check
24712      * @return {Boolean}
24713      */
24714     isIdSelected : function(id){
24715         return (this.selections.key(id) ? true : false);
24716     },
24717
24718
24719     // private
24720     handleMouseDBClick : function(e, t){
24721         
24722     },
24723     // private
24724     handleMouseDown : function(e, t)
24725     {
24726             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24727         if(this.isLocked() || rowIndex < 0 ){
24728             return;
24729         };
24730         if(e.shiftKey && this.last !== false){
24731             var last = this.last;
24732             this.selectRange(last, rowIndex, e.ctrlKey);
24733             this.last = last; // reset the last
24734             t.focus();
24735     
24736         }else{
24737             var isSelected = this.isSelected(rowIndex);
24738             //Roo.log("select row:" + rowIndex);
24739             if(isSelected){
24740                 this.deselectRow(rowIndex);
24741             } else {
24742                         this.selectRow(rowIndex, true);
24743             }
24744     
24745             /*
24746                 if(e.button !== 0 && isSelected){
24747                 alert('rowIndex 2: ' + rowIndex);
24748                     view.focusRow(rowIndex);
24749                 }else if(e.ctrlKey && isSelected){
24750                     this.deselectRow(rowIndex);
24751                 }else if(!isSelected){
24752                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24753                     view.focusRow(rowIndex);
24754                 }
24755             */
24756         }
24757         this.fireEvent("afterselectionchange", this);
24758     },
24759     // private
24760     handleDragableRowClick :  function(grid, rowIndex, e) 
24761     {
24762         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24763             this.selectRow(rowIndex, false);
24764             grid.view.focusRow(rowIndex);
24765              this.fireEvent("afterselectionchange", this);
24766         }
24767     },
24768     
24769     /**
24770      * Selects multiple rows.
24771      * @param {Array} rows Array of the indexes of the row to select
24772      * @param {Boolean} keepExisting (optional) True to keep existing selections
24773      */
24774     selectRows : function(rows, keepExisting){
24775         if(!keepExisting){
24776             this.clearSelections();
24777         }
24778         for(var i = 0, len = rows.length; i < len; i++){
24779             this.selectRow(rows[i], true);
24780         }
24781     },
24782
24783     /**
24784      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24785      * @param {Number} startRow The index of the first row in the range
24786      * @param {Number} endRow The index of the last row in the range
24787      * @param {Boolean} keepExisting (optional) True to retain existing selections
24788      */
24789     selectRange : function(startRow, endRow, keepExisting){
24790         if(this.locked) {
24791             return;
24792         }
24793         if(!keepExisting){
24794             this.clearSelections();
24795         }
24796         if(startRow <= endRow){
24797             for(var i = startRow; i <= endRow; i++){
24798                 this.selectRow(i, true);
24799             }
24800         }else{
24801             for(var i = startRow; i >= endRow; i--){
24802                 this.selectRow(i, true);
24803             }
24804         }
24805     },
24806
24807     /**
24808      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24809      * @param {Number} startRow The index of the first row in the range
24810      * @param {Number} endRow The index of the last row in the range
24811      */
24812     deselectRange : function(startRow, endRow, preventViewNotify){
24813         if(this.locked) {
24814             return;
24815         }
24816         for(var i = startRow; i <= endRow; i++){
24817             this.deselectRow(i, preventViewNotify);
24818         }
24819     },
24820
24821     /**
24822      * Selects a row.
24823      * @param {Number} row The index of the row to select
24824      * @param {Boolean} keepExisting (optional) True to keep existing selections
24825      */
24826     selectRow : function(index, keepExisting, preventViewNotify)
24827     {
24828             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24829             return;
24830         }
24831         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24832             if(!keepExisting || this.singleSelect){
24833                 this.clearSelections();
24834             }
24835             
24836             var r = this.grid.store.getAt(index);
24837             //console.log('selectRow - record id :' + r.id);
24838             
24839             this.selections.add(r);
24840             this.last = this.lastActive = index;
24841             if(!preventViewNotify){
24842                 var proxy = new Roo.Element(
24843                                 this.grid.getRowDom(index)
24844                 );
24845                 proxy.addClass('bg-info info');
24846             }
24847             this.fireEvent("rowselect", this, index, r);
24848             this.fireEvent("selectionchange", this);
24849         }
24850     },
24851
24852     /**
24853      * Deselects a row.
24854      * @param {Number} row The index of the row to deselect
24855      */
24856     deselectRow : function(index, preventViewNotify)
24857     {
24858         if(this.locked) {
24859             return;
24860         }
24861         if(this.last == index){
24862             this.last = false;
24863         }
24864         if(this.lastActive == index){
24865             this.lastActive = false;
24866         }
24867         
24868         var r = this.grid.store.getAt(index);
24869         if (!r) {
24870             return;
24871         }
24872         
24873         this.selections.remove(r);
24874         //.console.log('deselectRow - record id :' + r.id);
24875         if(!preventViewNotify){
24876         
24877             var proxy = new Roo.Element(
24878                 this.grid.getRowDom(index)
24879             );
24880             proxy.removeClass('bg-info info');
24881         }
24882         this.fireEvent("rowdeselect", this, index);
24883         this.fireEvent("selectionchange", this);
24884     },
24885
24886     // private
24887     restoreLast : function(){
24888         if(this._last){
24889             this.last = this._last;
24890         }
24891     },
24892
24893     // private
24894     acceptsNav : function(row, col, cm){
24895         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24896     },
24897
24898     // private
24899     onEditorKey : function(field, e){
24900         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24901         if(k == e.TAB){
24902             e.stopEvent();
24903             ed.completeEdit();
24904             if(e.shiftKey){
24905                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24906             }else{
24907                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24908             }
24909         }else if(k == e.ENTER && !e.ctrlKey){
24910             e.stopEvent();
24911             ed.completeEdit();
24912             if(e.shiftKey){
24913                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24914             }else{
24915                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24916             }
24917         }else if(k == e.ESC){
24918             ed.cancelEdit();
24919         }
24920         if(newCell){
24921             g.startEditing(newCell[0], newCell[1]);
24922         }
24923     }
24924 });
24925 /*
24926  * Based on:
24927  * Ext JS Library 1.1.1
24928  * Copyright(c) 2006-2007, Ext JS, LLC.
24929  *
24930  * Originally Released Under LGPL - original licence link has changed is not relivant.
24931  *
24932  * Fork - LGPL
24933  * <script type="text/javascript">
24934  */
24935  
24936 /**
24937  * @class Roo.bootstrap.PagingToolbar
24938  * @extends Roo.bootstrap.NavSimplebar
24939  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24940  * @constructor
24941  * Create a new PagingToolbar
24942  * @param {Object} config The config object
24943  * @param {Roo.data.Store} store
24944  */
24945 Roo.bootstrap.PagingToolbar = function(config)
24946 {
24947     // old args format still supported... - xtype is prefered..
24948         // created from xtype...
24949     
24950     this.ds = config.dataSource;
24951     
24952     if (config.store && !this.ds) {
24953         this.store= Roo.factory(config.store, Roo.data);
24954         this.ds = this.store;
24955         this.ds.xmodule = this.xmodule || false;
24956     }
24957     
24958     this.toolbarItems = [];
24959     if (config.items) {
24960         this.toolbarItems = config.items;
24961     }
24962     
24963     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24964     
24965     this.cursor = 0;
24966     
24967     if (this.ds) { 
24968         this.bind(this.ds);
24969     }
24970     
24971     if (Roo.bootstrap.version == 4) {
24972         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24973     } else {
24974         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24975     }
24976     
24977 };
24978
24979 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24980     /**
24981      * @cfg {Roo.data.Store} dataSource
24982      * The underlying data store providing the paged data
24983      */
24984     /**
24985      * @cfg {String/HTMLElement/Element} container
24986      * container The id or element that will contain the toolbar
24987      */
24988     /**
24989      * @cfg {Boolean} displayInfo
24990      * True to display the displayMsg (defaults to false)
24991      */
24992     /**
24993      * @cfg {Number} pageSize
24994      * The number of records to display per page (defaults to 20)
24995      */
24996     pageSize: 20,
24997     /**
24998      * @cfg {String} displayMsg
24999      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
25000      */
25001     displayMsg : 'Displaying {0} - {1} of {2}',
25002     /**
25003      * @cfg {String} emptyMsg
25004      * The message to display when no records are found (defaults to "No data to display")
25005      */
25006     emptyMsg : 'No data to display',
25007     /**
25008      * Customizable piece of the default paging text (defaults to "Page")
25009      * @type String
25010      */
25011     beforePageText : "Page",
25012     /**
25013      * Customizable piece of the default paging text (defaults to "of %0")
25014      * @type String
25015      */
25016     afterPageText : "of {0}",
25017     /**
25018      * Customizable piece of the default paging text (defaults to "First Page")
25019      * @type String
25020      */
25021     firstText : "First Page",
25022     /**
25023      * Customizable piece of the default paging text (defaults to "Previous Page")
25024      * @type String
25025      */
25026     prevText : "Previous Page",
25027     /**
25028      * Customizable piece of the default paging text (defaults to "Next Page")
25029      * @type String
25030      */
25031     nextText : "Next Page",
25032     /**
25033      * Customizable piece of the default paging text (defaults to "Last Page")
25034      * @type String
25035      */
25036     lastText : "Last Page",
25037     /**
25038      * Customizable piece of the default paging text (defaults to "Refresh")
25039      * @type String
25040      */
25041     refreshText : "Refresh",
25042
25043     buttons : false,
25044     // private
25045     onRender : function(ct, position) 
25046     {
25047         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
25048         this.navgroup.parentId = this.id;
25049         this.navgroup.onRender(this.el, null);
25050         // add the buttons to the navgroup
25051         
25052         if(this.displayInfo){
25053             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
25054             this.displayEl = this.el.select('.x-paging-info', true).first();
25055 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
25056 //            this.displayEl = navel.el.select('span',true).first();
25057         }
25058         
25059         var _this = this;
25060         
25061         if(this.buttons){
25062             Roo.each(_this.buttons, function(e){ // this might need to use render????
25063                Roo.factory(e).render(_this.el);
25064             });
25065         }
25066             
25067         Roo.each(_this.toolbarItems, function(e) {
25068             _this.navgroup.addItem(e);
25069         });
25070         
25071         
25072         this.first = this.navgroup.addItem({
25073             tooltip: this.firstText,
25074             cls: "prev btn-outline-secondary",
25075             html : ' <i class="fa fa-step-backward"></i>',
25076             disabled: true,
25077             preventDefault: true,
25078             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
25079         });
25080         
25081         this.prev =  this.navgroup.addItem({
25082             tooltip: this.prevText,
25083             cls: "prev btn-outline-secondary",
25084             html : ' <i class="fa fa-backward"></i>',
25085             disabled: true,
25086             preventDefault: true,
25087             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
25088         });
25089     //this.addSeparator();
25090         
25091         
25092         var field = this.navgroup.addItem( {
25093             tagtype : 'span',
25094             cls : 'x-paging-position  btn-outline-secondary',
25095              disabled: true,
25096             html : this.beforePageText  +
25097                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
25098                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
25099          } ); //?? escaped?
25100         
25101         this.field = field.el.select('input', true).first();
25102         this.field.on("keydown", this.onPagingKeydown, this);
25103         this.field.on("focus", function(){this.dom.select();});
25104     
25105     
25106         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
25107         //this.field.setHeight(18);
25108         //this.addSeparator();
25109         this.next = this.navgroup.addItem({
25110             tooltip: this.nextText,
25111             cls: "next btn-outline-secondary",
25112             html : ' <i class="fa fa-forward"></i>',
25113             disabled: true,
25114             preventDefault: true,
25115             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
25116         });
25117         this.last = this.navgroup.addItem({
25118             tooltip: this.lastText,
25119             html : ' <i class="fa fa-step-forward"></i>',
25120             cls: "next btn-outline-secondary",
25121             disabled: true,
25122             preventDefault: true,
25123             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
25124         });
25125     //this.addSeparator();
25126         this.loading = this.navgroup.addItem({
25127             tooltip: this.refreshText,
25128             cls: "btn-outline-secondary",
25129             html : ' <i class="fa fa-refresh"></i>',
25130             preventDefault: true,
25131             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
25132         });
25133         
25134     },
25135
25136     // private
25137     updateInfo : function(){
25138         if(this.displayEl){
25139             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
25140             var msg = count == 0 ?
25141                 this.emptyMsg :
25142                 String.format(
25143                     this.displayMsg,
25144                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
25145                 );
25146             this.displayEl.update(msg);
25147         }
25148     },
25149
25150     // private
25151     onLoad : function(ds, r, o)
25152     {
25153         this.cursor = o.params.start ? o.params.start : 0;
25154         
25155         var d = this.getPageData(),
25156             ap = d.activePage,
25157             ps = d.pages;
25158         
25159         
25160         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
25161         this.field.dom.value = ap;
25162         this.first.setDisabled(ap == 1);
25163         this.prev.setDisabled(ap == 1);
25164         this.next.setDisabled(ap == ps);
25165         this.last.setDisabled(ap == ps);
25166         this.loading.enable();
25167         this.updateInfo();
25168     },
25169
25170     // private
25171     getPageData : function(){
25172         var total = this.ds.getTotalCount();
25173         return {
25174             total : total,
25175             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
25176             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
25177         };
25178     },
25179
25180     // private
25181     onLoadError : function(){
25182         this.loading.enable();
25183     },
25184
25185     // private
25186     onPagingKeydown : function(e){
25187         var k = e.getKey();
25188         var d = this.getPageData();
25189         if(k == e.RETURN){
25190             var v = this.field.dom.value, pageNum;
25191             if(!v || isNaN(pageNum = parseInt(v, 10))){
25192                 this.field.dom.value = d.activePage;
25193                 return;
25194             }
25195             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
25196             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25197             e.stopEvent();
25198         }
25199         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))
25200         {
25201           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
25202           this.field.dom.value = pageNum;
25203           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
25204           e.stopEvent();
25205         }
25206         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
25207         {
25208           var v = this.field.dom.value, pageNum; 
25209           var increment = (e.shiftKey) ? 10 : 1;
25210           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
25211                 increment *= -1;
25212           }
25213           if(!v || isNaN(pageNum = parseInt(v, 10))) {
25214             this.field.dom.value = d.activePage;
25215             return;
25216           }
25217           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25218           {
25219             this.field.dom.value = parseInt(v, 10) + increment;
25220             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25221             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25222           }
25223           e.stopEvent();
25224         }
25225     },
25226
25227     // private
25228     beforeLoad : function(){
25229         if(this.loading){
25230             this.loading.disable();
25231         }
25232     },
25233
25234     // private
25235     onClick : function(which){
25236         
25237         var ds = this.ds;
25238         if (!ds) {
25239             return;
25240         }
25241         
25242         switch(which){
25243             case "first":
25244                 ds.load({params:{start: 0, limit: this.pageSize}});
25245             break;
25246             case "prev":
25247                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25248             break;
25249             case "next":
25250                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25251             break;
25252             case "last":
25253                 var total = ds.getTotalCount();
25254                 var extra = total % this.pageSize;
25255                 var lastStart = extra ? (total - extra) : total-this.pageSize;
25256                 ds.load({params:{start: lastStart, limit: this.pageSize}});
25257             break;
25258             case "refresh":
25259                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25260             break;
25261         }
25262     },
25263
25264     /**
25265      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25266      * @param {Roo.data.Store} store The data store to unbind
25267      */
25268     unbind : function(ds){
25269         ds.un("beforeload", this.beforeLoad, this);
25270         ds.un("load", this.onLoad, this);
25271         ds.un("loadexception", this.onLoadError, this);
25272         ds.un("remove", this.updateInfo, this);
25273         ds.un("add", this.updateInfo, this);
25274         this.ds = undefined;
25275     },
25276
25277     /**
25278      * Binds the paging toolbar to the specified {@link Roo.data.Store}
25279      * @param {Roo.data.Store} store The data store to bind
25280      */
25281     bind : function(ds){
25282         ds.on("beforeload", this.beforeLoad, this);
25283         ds.on("load", this.onLoad, this);
25284         ds.on("loadexception", this.onLoadError, this);
25285         ds.on("remove", this.updateInfo, this);
25286         ds.on("add", this.updateInfo, this);
25287         this.ds = ds;
25288     }
25289 });/*
25290  * - LGPL
25291  *
25292  * element
25293  * 
25294  */
25295
25296 /**
25297  * @class Roo.bootstrap.MessageBar
25298  * @extends Roo.bootstrap.Component
25299  * Bootstrap MessageBar class
25300  * @cfg {String} html contents of the MessageBar
25301  * @cfg {String} weight (info | success | warning | danger) default info
25302  * @cfg {String} beforeClass insert the bar before the given class
25303  * @cfg {Boolean} closable (true | false) default false
25304  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25305  * 
25306  * @constructor
25307  * Create a new Element
25308  * @param {Object} config The config object
25309  */
25310
25311 Roo.bootstrap.MessageBar = function(config){
25312     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25313 };
25314
25315 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25316     
25317     html: '',
25318     weight: 'info',
25319     closable: false,
25320     fixed: false,
25321     beforeClass: 'bootstrap-sticky-wrap',
25322     
25323     getAutoCreate : function(){
25324         
25325         var cfg = {
25326             tag: 'div',
25327             cls: 'alert alert-dismissable alert-' + this.weight,
25328             cn: [
25329                 {
25330                     tag: 'span',
25331                     cls: 'message',
25332                     html: this.html || ''
25333                 }
25334             ]
25335         };
25336         
25337         if(this.fixed){
25338             cfg.cls += ' alert-messages-fixed';
25339         }
25340         
25341         if(this.closable){
25342             cfg.cn.push({
25343                 tag: 'button',
25344                 cls: 'close',
25345                 html: 'x'
25346             });
25347         }
25348         
25349         return cfg;
25350     },
25351     
25352     onRender : function(ct, position)
25353     {
25354         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25355         
25356         if(!this.el){
25357             var cfg = Roo.apply({},  this.getAutoCreate());
25358             cfg.id = Roo.id();
25359             
25360             if (this.cls) {
25361                 cfg.cls += ' ' + this.cls;
25362             }
25363             if (this.style) {
25364                 cfg.style = this.style;
25365             }
25366             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25367             
25368             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25369         }
25370         
25371         this.el.select('>button.close').on('click', this.hide, this);
25372         
25373     },
25374     
25375     show : function()
25376     {
25377         if (!this.rendered) {
25378             this.render();
25379         }
25380         
25381         this.el.show();
25382         
25383         this.fireEvent('show', this);
25384         
25385     },
25386     
25387     hide : function()
25388     {
25389         if (!this.rendered) {
25390             this.render();
25391         }
25392         
25393         this.el.hide();
25394         
25395         this.fireEvent('hide', this);
25396     },
25397     
25398     update : function()
25399     {
25400 //        var e = this.el.dom.firstChild;
25401 //        
25402 //        if(this.closable){
25403 //            e = e.nextSibling;
25404 //        }
25405 //        
25406 //        e.data = this.html || '';
25407
25408         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25409     }
25410    
25411 });
25412
25413  
25414
25415      /*
25416  * - LGPL
25417  *
25418  * Graph
25419  * 
25420  */
25421
25422
25423 /**
25424  * @class Roo.bootstrap.Graph
25425  * @extends Roo.bootstrap.Component
25426  * Bootstrap Graph class
25427 > Prameters
25428  -sm {number} sm 4
25429  -md {number} md 5
25430  @cfg {String} graphtype  bar | vbar | pie
25431  @cfg {number} g_x coodinator | centre x (pie)
25432  @cfg {number} g_y coodinator | centre y (pie)
25433  @cfg {number} g_r radius (pie)
25434  @cfg {number} g_height height of the chart (respected by all elements in the set)
25435  @cfg {number} g_width width of the chart (respected by all elements in the set)
25436  @cfg {Object} title The title of the chart
25437     
25438  -{Array}  values
25439  -opts (object) options for the chart 
25440      o {
25441      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25442      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25443      o vgutter (number)
25444      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.
25445      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25446      o to
25447      o stretch (boolean)
25448      o }
25449  -opts (object) options for the pie
25450      o{
25451      o cut
25452      o startAngle (number)
25453      o endAngle (number)
25454      } 
25455  *
25456  * @constructor
25457  * Create a new Input
25458  * @param {Object} config The config object
25459  */
25460
25461 Roo.bootstrap.Graph = function(config){
25462     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25463     
25464     this.addEvents({
25465         // img events
25466         /**
25467          * @event click
25468          * The img click event for the img.
25469          * @param {Roo.EventObject} e
25470          */
25471         "click" : true
25472     });
25473 };
25474
25475 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25476     
25477     sm: 4,
25478     md: 5,
25479     graphtype: 'bar',
25480     g_height: 250,
25481     g_width: 400,
25482     g_x: 50,
25483     g_y: 50,
25484     g_r: 30,
25485     opts:{
25486         //g_colors: this.colors,
25487         g_type: 'soft',
25488         g_gutter: '20%'
25489
25490     },
25491     title : false,
25492
25493     getAutoCreate : function(){
25494         
25495         var cfg = {
25496             tag: 'div',
25497             html : null
25498         };
25499         
25500         
25501         return  cfg;
25502     },
25503
25504     onRender : function(ct,position){
25505         
25506         
25507         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25508         
25509         if (typeof(Raphael) == 'undefined') {
25510             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25511             return;
25512         }
25513         
25514         this.raphael = Raphael(this.el.dom);
25515         
25516                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25517                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25518                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25519                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25520                 /*
25521                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25522                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25523                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25524                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25525                 
25526                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25527                 r.barchart(330, 10, 300, 220, data1);
25528                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25529                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25530                 */
25531                 
25532                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25533                 // r.barchart(30, 30, 560, 250,  xdata, {
25534                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25535                 //     axis : "0 0 1 1",
25536                 //     axisxlabels :  xdata
25537                 //     //yvalues : cols,
25538                    
25539                 // });
25540 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25541 //        
25542 //        this.load(null,xdata,{
25543 //                axis : "0 0 1 1",
25544 //                axisxlabels :  xdata
25545 //                });
25546
25547     },
25548
25549     load : function(graphtype,xdata,opts)
25550     {
25551         this.raphael.clear();
25552         if(!graphtype) {
25553             graphtype = this.graphtype;
25554         }
25555         if(!opts){
25556             opts = this.opts;
25557         }
25558         var r = this.raphael,
25559             fin = function () {
25560                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25561             },
25562             fout = function () {
25563                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25564             },
25565             pfin = function() {
25566                 this.sector.stop();
25567                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25568
25569                 if (this.label) {
25570                     this.label[0].stop();
25571                     this.label[0].attr({ r: 7.5 });
25572                     this.label[1].attr({ "font-weight": 800 });
25573                 }
25574             },
25575             pfout = function() {
25576                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25577
25578                 if (this.label) {
25579                     this.label[0].animate({ r: 5 }, 500, "bounce");
25580                     this.label[1].attr({ "font-weight": 400 });
25581                 }
25582             };
25583
25584         switch(graphtype){
25585             case 'bar':
25586                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25587                 break;
25588             case 'hbar':
25589                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25590                 break;
25591             case 'pie':
25592 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25593 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25594 //            
25595                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25596                 
25597                 break;
25598
25599         }
25600         
25601         if(this.title){
25602             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25603         }
25604         
25605     },
25606     
25607     setTitle: function(o)
25608     {
25609         this.title = o;
25610     },
25611     
25612     initEvents: function() {
25613         
25614         if(!this.href){
25615             this.el.on('click', this.onClick, this);
25616         }
25617     },
25618     
25619     onClick : function(e)
25620     {
25621         Roo.log('img onclick');
25622         this.fireEvent('click', this, e);
25623     }
25624    
25625 });
25626
25627  
25628 /*
25629  * - LGPL
25630  *
25631  * numberBox
25632  * 
25633  */
25634 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25635
25636 /**
25637  * @class Roo.bootstrap.dash.NumberBox
25638  * @extends Roo.bootstrap.Component
25639  * Bootstrap NumberBox class
25640  * @cfg {String} headline Box headline
25641  * @cfg {String} content Box content
25642  * @cfg {String} icon Box icon
25643  * @cfg {String} footer Footer text
25644  * @cfg {String} fhref Footer href
25645  * 
25646  * @constructor
25647  * Create a new NumberBox
25648  * @param {Object} config The config object
25649  */
25650
25651
25652 Roo.bootstrap.dash.NumberBox = function(config){
25653     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25654     
25655 };
25656
25657 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25658     
25659     headline : '',
25660     content : '',
25661     icon : '',
25662     footer : '',
25663     fhref : '',
25664     ficon : '',
25665     
25666     getAutoCreate : function(){
25667         
25668         var cfg = {
25669             tag : 'div',
25670             cls : 'small-box ',
25671             cn : [
25672                 {
25673                     tag : 'div',
25674                     cls : 'inner',
25675                     cn :[
25676                         {
25677                             tag : 'h3',
25678                             cls : 'roo-headline',
25679                             html : this.headline
25680                         },
25681                         {
25682                             tag : 'p',
25683                             cls : 'roo-content',
25684                             html : this.content
25685                         }
25686                     ]
25687                 }
25688             ]
25689         };
25690         
25691         if(this.icon){
25692             cfg.cn.push({
25693                 tag : 'div',
25694                 cls : 'icon',
25695                 cn :[
25696                     {
25697                         tag : 'i',
25698                         cls : 'ion ' + this.icon
25699                     }
25700                 ]
25701             });
25702         }
25703         
25704         if(this.footer){
25705             var footer = {
25706                 tag : 'a',
25707                 cls : 'small-box-footer',
25708                 href : this.fhref || '#',
25709                 html : this.footer
25710             };
25711             
25712             cfg.cn.push(footer);
25713             
25714         }
25715         
25716         return  cfg;
25717     },
25718
25719     onRender : function(ct,position){
25720         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25721
25722
25723        
25724                 
25725     },
25726
25727     setHeadline: function (value)
25728     {
25729         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25730     },
25731     
25732     setFooter: function (value, href)
25733     {
25734         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25735         
25736         if(href){
25737             this.el.select('a.small-box-footer',true).first().attr('href', href);
25738         }
25739         
25740     },
25741
25742     setContent: function (value)
25743     {
25744         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25745     },
25746
25747     initEvents: function() 
25748     {   
25749         
25750     }
25751     
25752 });
25753
25754  
25755 /*
25756  * - LGPL
25757  *
25758  * TabBox
25759  * 
25760  */
25761 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25762
25763 /**
25764  * @class Roo.bootstrap.dash.TabBox
25765  * @extends Roo.bootstrap.Component
25766  * Bootstrap TabBox class
25767  * @cfg {String} title Title of the TabBox
25768  * @cfg {String} icon Icon of the TabBox
25769  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25770  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25771  * 
25772  * @constructor
25773  * Create a new TabBox
25774  * @param {Object} config The config object
25775  */
25776
25777
25778 Roo.bootstrap.dash.TabBox = function(config){
25779     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25780     this.addEvents({
25781         // raw events
25782         /**
25783          * @event addpane
25784          * When a pane is added
25785          * @param {Roo.bootstrap.dash.TabPane} pane
25786          */
25787         "addpane" : true,
25788         /**
25789          * @event activatepane
25790          * When a pane is activated
25791          * @param {Roo.bootstrap.dash.TabPane} pane
25792          */
25793         "activatepane" : true
25794         
25795          
25796     });
25797     
25798     this.panes = [];
25799 };
25800
25801 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25802
25803     title : '',
25804     icon : false,
25805     showtabs : true,
25806     tabScrollable : false,
25807     
25808     getChildContainer : function()
25809     {
25810         return this.el.select('.tab-content', true).first();
25811     },
25812     
25813     getAutoCreate : function(){
25814         
25815         var header = {
25816             tag: 'li',
25817             cls: 'pull-left header',
25818             html: this.title,
25819             cn : []
25820         };
25821         
25822         if(this.icon){
25823             header.cn.push({
25824                 tag: 'i',
25825                 cls: 'fa ' + this.icon
25826             });
25827         }
25828         
25829         var h = {
25830             tag: 'ul',
25831             cls: 'nav nav-tabs pull-right',
25832             cn: [
25833                 header
25834             ]
25835         };
25836         
25837         if(this.tabScrollable){
25838             h = {
25839                 tag: 'div',
25840                 cls: 'tab-header',
25841                 cn: [
25842                     {
25843                         tag: 'ul',
25844                         cls: 'nav nav-tabs pull-right',
25845                         cn: [
25846                             header
25847                         ]
25848                     }
25849                 ]
25850             };
25851         }
25852         
25853         var cfg = {
25854             tag: 'div',
25855             cls: 'nav-tabs-custom',
25856             cn: [
25857                 h,
25858                 {
25859                     tag: 'div',
25860                     cls: 'tab-content no-padding',
25861                     cn: []
25862                 }
25863             ]
25864         };
25865
25866         return  cfg;
25867     },
25868     initEvents : function()
25869     {
25870         //Roo.log('add add pane handler');
25871         this.on('addpane', this.onAddPane, this);
25872     },
25873      /**
25874      * Updates the box title
25875      * @param {String} html to set the title to.
25876      */
25877     setTitle : function(value)
25878     {
25879         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25880     },
25881     onAddPane : function(pane)
25882     {
25883         this.panes.push(pane);
25884         //Roo.log('addpane');
25885         //Roo.log(pane);
25886         // tabs are rendere left to right..
25887         if(!this.showtabs){
25888             return;
25889         }
25890         
25891         var ctr = this.el.select('.nav-tabs', true).first();
25892          
25893          
25894         var existing = ctr.select('.nav-tab',true);
25895         var qty = existing.getCount();;
25896         
25897         
25898         var tab = ctr.createChild({
25899             tag : 'li',
25900             cls : 'nav-tab' + (qty ? '' : ' active'),
25901             cn : [
25902                 {
25903                     tag : 'a',
25904                     href:'#',
25905                     html : pane.title
25906                 }
25907             ]
25908         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25909         pane.tab = tab;
25910         
25911         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25912         if (!qty) {
25913             pane.el.addClass('active');
25914         }
25915         
25916                 
25917     },
25918     onTabClick : function(ev,un,ob,pane)
25919     {
25920         //Roo.log('tab - prev default');
25921         ev.preventDefault();
25922         
25923         
25924         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25925         pane.tab.addClass('active');
25926         //Roo.log(pane.title);
25927         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25928         // technically we should have a deactivate event.. but maybe add later.
25929         // and it should not de-activate the selected tab...
25930         this.fireEvent('activatepane', pane);
25931         pane.el.addClass('active');
25932         pane.fireEvent('activate');
25933         
25934         
25935     },
25936     
25937     getActivePane : function()
25938     {
25939         var r = false;
25940         Roo.each(this.panes, function(p) {
25941             if(p.el.hasClass('active')){
25942                 r = p;
25943                 return false;
25944             }
25945             
25946             return;
25947         });
25948         
25949         return r;
25950     }
25951     
25952     
25953 });
25954
25955  
25956 /*
25957  * - LGPL
25958  *
25959  * Tab pane
25960  * 
25961  */
25962 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25963 /**
25964  * @class Roo.bootstrap.TabPane
25965  * @extends Roo.bootstrap.Component
25966  * Bootstrap TabPane class
25967  * @cfg {Boolean} active (false | true) Default false
25968  * @cfg {String} title title of panel
25969
25970  * 
25971  * @constructor
25972  * Create a new TabPane
25973  * @param {Object} config The config object
25974  */
25975
25976 Roo.bootstrap.dash.TabPane = function(config){
25977     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25978     
25979     this.addEvents({
25980         // raw events
25981         /**
25982          * @event activate
25983          * When a pane is activated
25984          * @param {Roo.bootstrap.dash.TabPane} pane
25985          */
25986         "activate" : true
25987          
25988     });
25989 };
25990
25991 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25992     
25993     active : false,
25994     title : '',
25995     
25996     // the tabBox that this is attached to.
25997     tab : false,
25998      
25999     getAutoCreate : function() 
26000     {
26001         var cfg = {
26002             tag: 'div',
26003             cls: 'tab-pane'
26004         };
26005         
26006         if(this.active){
26007             cfg.cls += ' active';
26008         }
26009         
26010         return cfg;
26011     },
26012     initEvents  : function()
26013     {
26014         //Roo.log('trigger add pane handler');
26015         this.parent().fireEvent('addpane', this)
26016     },
26017     
26018      /**
26019      * Updates the tab title 
26020      * @param {String} html to set the title to.
26021      */
26022     setTitle: function(str)
26023     {
26024         if (!this.tab) {
26025             return;
26026         }
26027         this.title = str;
26028         this.tab.select('a', true).first().dom.innerHTML = str;
26029         
26030     }
26031     
26032     
26033     
26034 });
26035
26036  
26037
26038
26039  /*
26040  * - LGPL
26041  *
26042  * menu
26043  * 
26044  */
26045 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26046
26047 /**
26048  * @class Roo.bootstrap.menu.Menu
26049  * @extends Roo.bootstrap.Component
26050  * Bootstrap Menu class - container for Menu
26051  * @cfg {String} html Text of the menu
26052  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
26053  * @cfg {String} icon Font awesome icon
26054  * @cfg {String} pos Menu align to (top | bottom) default bottom
26055  * 
26056  * 
26057  * @constructor
26058  * Create a new Menu
26059  * @param {Object} config The config object
26060  */
26061
26062
26063 Roo.bootstrap.menu.Menu = function(config){
26064     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
26065     
26066     this.addEvents({
26067         /**
26068          * @event beforeshow
26069          * Fires before this menu is displayed
26070          * @param {Roo.bootstrap.menu.Menu} this
26071          */
26072         beforeshow : true,
26073         /**
26074          * @event beforehide
26075          * Fires before this menu is hidden
26076          * @param {Roo.bootstrap.menu.Menu} this
26077          */
26078         beforehide : true,
26079         /**
26080          * @event show
26081          * Fires after this menu is displayed
26082          * @param {Roo.bootstrap.menu.Menu} this
26083          */
26084         show : true,
26085         /**
26086          * @event hide
26087          * Fires after this menu is hidden
26088          * @param {Roo.bootstrap.menu.Menu} this
26089          */
26090         hide : true,
26091         /**
26092          * @event click
26093          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
26094          * @param {Roo.bootstrap.menu.Menu} this
26095          * @param {Roo.EventObject} e
26096          */
26097         click : true
26098     });
26099     
26100 };
26101
26102 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
26103     
26104     submenu : false,
26105     html : '',
26106     weight : 'default',
26107     icon : false,
26108     pos : 'bottom',
26109     
26110     
26111     getChildContainer : function() {
26112         if(this.isSubMenu){
26113             return this.el;
26114         }
26115         
26116         return this.el.select('ul.dropdown-menu', true).first();  
26117     },
26118     
26119     getAutoCreate : function()
26120     {
26121         var text = [
26122             {
26123                 tag : 'span',
26124                 cls : 'roo-menu-text',
26125                 html : this.html
26126             }
26127         ];
26128         
26129         if(this.icon){
26130             text.unshift({
26131                 tag : 'i',
26132                 cls : 'fa ' + this.icon
26133             })
26134         }
26135         
26136         
26137         var cfg = {
26138             tag : 'div',
26139             cls : 'btn-group',
26140             cn : [
26141                 {
26142                     tag : 'button',
26143                     cls : 'dropdown-button btn btn-' + this.weight,
26144                     cn : text
26145                 },
26146                 {
26147                     tag : 'button',
26148                     cls : 'dropdown-toggle btn btn-' + this.weight,
26149                     cn : [
26150                         {
26151                             tag : 'span',
26152                             cls : 'caret'
26153                         }
26154                     ]
26155                 },
26156                 {
26157                     tag : 'ul',
26158                     cls : 'dropdown-menu'
26159                 }
26160             ]
26161             
26162         };
26163         
26164         if(this.pos == 'top'){
26165             cfg.cls += ' dropup';
26166         }
26167         
26168         if(this.isSubMenu){
26169             cfg = {
26170                 tag : 'ul',
26171                 cls : 'dropdown-menu'
26172             }
26173         }
26174         
26175         return cfg;
26176     },
26177     
26178     onRender : function(ct, position)
26179     {
26180         this.isSubMenu = ct.hasClass('dropdown-submenu');
26181         
26182         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
26183     },
26184     
26185     initEvents : function() 
26186     {
26187         if(this.isSubMenu){
26188             return;
26189         }
26190         
26191         this.hidden = true;
26192         
26193         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
26194         this.triggerEl.on('click', this.onTriggerPress, this);
26195         
26196         this.buttonEl = this.el.select('button.dropdown-button', true).first();
26197         this.buttonEl.on('click', this.onClick, this);
26198         
26199     },
26200     
26201     list : function()
26202     {
26203         if(this.isSubMenu){
26204             return this.el;
26205         }
26206         
26207         return this.el.select('ul.dropdown-menu', true).first();
26208     },
26209     
26210     onClick : function(e)
26211     {
26212         this.fireEvent("click", this, e);
26213     },
26214     
26215     onTriggerPress  : function(e)
26216     {   
26217         if (this.isVisible()) {
26218             this.hide();
26219         } else {
26220             this.show();
26221         }
26222     },
26223     
26224     isVisible : function(){
26225         return !this.hidden;
26226     },
26227     
26228     show : function()
26229     {
26230         this.fireEvent("beforeshow", this);
26231         
26232         this.hidden = false;
26233         this.el.addClass('open');
26234         
26235         Roo.get(document).on("mouseup", this.onMouseUp, this);
26236         
26237         this.fireEvent("show", this);
26238         
26239         
26240     },
26241     
26242     hide : function()
26243     {
26244         this.fireEvent("beforehide", this);
26245         
26246         this.hidden = true;
26247         this.el.removeClass('open');
26248         
26249         Roo.get(document).un("mouseup", this.onMouseUp);
26250         
26251         this.fireEvent("hide", this);
26252     },
26253     
26254     onMouseUp : function()
26255     {
26256         this.hide();
26257     }
26258     
26259 });
26260
26261  
26262  /*
26263  * - LGPL
26264  *
26265  * menu item
26266  * 
26267  */
26268 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26269
26270 /**
26271  * @class Roo.bootstrap.menu.Item
26272  * @extends Roo.bootstrap.Component
26273  * Bootstrap MenuItem class
26274  * @cfg {Boolean} submenu (true | false) default false
26275  * @cfg {String} html text of the item
26276  * @cfg {String} href the link
26277  * @cfg {Boolean} disable (true | false) default false
26278  * @cfg {Boolean} preventDefault (true | false) default true
26279  * @cfg {String} icon Font awesome icon
26280  * @cfg {String} pos Submenu align to (left | right) default right 
26281  * 
26282  * 
26283  * @constructor
26284  * Create a new Item
26285  * @param {Object} config The config object
26286  */
26287
26288
26289 Roo.bootstrap.menu.Item = function(config){
26290     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26291     this.addEvents({
26292         /**
26293          * @event mouseover
26294          * Fires when the mouse is hovering over this menu
26295          * @param {Roo.bootstrap.menu.Item} this
26296          * @param {Roo.EventObject} e
26297          */
26298         mouseover : true,
26299         /**
26300          * @event mouseout
26301          * Fires when the mouse exits this menu
26302          * @param {Roo.bootstrap.menu.Item} this
26303          * @param {Roo.EventObject} e
26304          */
26305         mouseout : true,
26306         // raw events
26307         /**
26308          * @event click
26309          * The raw click event for the entire grid.
26310          * @param {Roo.EventObject} e
26311          */
26312         click : true
26313     });
26314 };
26315
26316 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26317     
26318     submenu : false,
26319     href : '',
26320     html : '',
26321     preventDefault: true,
26322     disable : false,
26323     icon : false,
26324     pos : 'right',
26325     
26326     getAutoCreate : function()
26327     {
26328         var text = [
26329             {
26330                 tag : 'span',
26331                 cls : 'roo-menu-item-text',
26332                 html : this.html
26333             }
26334         ];
26335         
26336         if(this.icon){
26337             text.unshift({
26338                 tag : 'i',
26339                 cls : 'fa ' + this.icon
26340             })
26341         }
26342         
26343         var cfg = {
26344             tag : 'li',
26345             cn : [
26346                 {
26347                     tag : 'a',
26348                     href : this.href || '#',
26349                     cn : text
26350                 }
26351             ]
26352         };
26353         
26354         if(this.disable){
26355             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26356         }
26357         
26358         if(this.submenu){
26359             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26360             
26361             if(this.pos == 'left'){
26362                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26363             }
26364         }
26365         
26366         return cfg;
26367     },
26368     
26369     initEvents : function() 
26370     {
26371         this.el.on('mouseover', this.onMouseOver, this);
26372         this.el.on('mouseout', this.onMouseOut, this);
26373         
26374         this.el.select('a', true).first().on('click', this.onClick, this);
26375         
26376     },
26377     
26378     onClick : function(e)
26379     {
26380         if(this.preventDefault){
26381             e.preventDefault();
26382         }
26383         
26384         this.fireEvent("click", this, e);
26385     },
26386     
26387     onMouseOver : function(e)
26388     {
26389         if(this.submenu && this.pos == 'left'){
26390             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26391         }
26392         
26393         this.fireEvent("mouseover", this, e);
26394     },
26395     
26396     onMouseOut : function(e)
26397     {
26398         this.fireEvent("mouseout", this, e);
26399     }
26400 });
26401
26402  
26403
26404  /*
26405  * - LGPL
26406  *
26407  * menu separator
26408  * 
26409  */
26410 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26411
26412 /**
26413  * @class Roo.bootstrap.menu.Separator
26414  * @extends Roo.bootstrap.Component
26415  * Bootstrap Separator class
26416  * 
26417  * @constructor
26418  * Create a new Separator
26419  * @param {Object} config The config object
26420  */
26421
26422
26423 Roo.bootstrap.menu.Separator = function(config){
26424     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26425 };
26426
26427 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26428     
26429     getAutoCreate : function(){
26430         var cfg = {
26431             tag : 'li',
26432             cls: 'divider'
26433         };
26434         
26435         return cfg;
26436     }
26437    
26438 });
26439
26440  
26441
26442  /*
26443  * - LGPL
26444  *
26445  * Tooltip
26446  * 
26447  */
26448
26449 /**
26450  * @class Roo.bootstrap.Tooltip
26451  * Bootstrap Tooltip class
26452  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26453  * to determine which dom element triggers the tooltip.
26454  * 
26455  * It needs to add support for additional attributes like tooltip-position
26456  * 
26457  * @constructor
26458  * Create a new Toolti
26459  * @param {Object} config The config object
26460  */
26461
26462 Roo.bootstrap.Tooltip = function(config){
26463     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26464     
26465     this.alignment = Roo.bootstrap.Tooltip.alignment;
26466     
26467     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26468         this.alignment = config.alignment;
26469     }
26470     
26471 };
26472
26473 Roo.apply(Roo.bootstrap.Tooltip, {
26474     /**
26475      * @function init initialize tooltip monitoring.
26476      * @static
26477      */
26478     currentEl : false,
26479     currentTip : false,
26480     currentRegion : false,
26481     
26482     //  init : delay?
26483     
26484     init : function()
26485     {
26486         Roo.get(document).on('mouseover', this.enter ,this);
26487         Roo.get(document).on('mouseout', this.leave, this);
26488          
26489         
26490         this.currentTip = new Roo.bootstrap.Tooltip();
26491     },
26492     
26493     enter : function(ev)
26494     {
26495         var dom = ev.getTarget();
26496         
26497         //Roo.log(['enter',dom]);
26498         var el = Roo.fly(dom);
26499         if (this.currentEl) {
26500             //Roo.log(dom);
26501             //Roo.log(this.currentEl);
26502             //Roo.log(this.currentEl.contains(dom));
26503             if (this.currentEl == el) {
26504                 return;
26505             }
26506             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26507                 return;
26508             }
26509
26510         }
26511         
26512         if (this.currentTip.el) {
26513             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26514         }    
26515         //Roo.log(ev);
26516         
26517         if(!el || el.dom == document){
26518             return;
26519         }
26520         
26521         var bindEl = el;
26522         
26523         // you can not look for children, as if el is the body.. then everythign is the child..
26524         if (!el.attr('tooltip')) { //
26525             if (!el.select("[tooltip]").elements.length) {
26526                 return;
26527             }
26528             // is the mouse over this child...?
26529             bindEl = el.select("[tooltip]").first();
26530             var xy = ev.getXY();
26531             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26532                 //Roo.log("not in region.");
26533                 return;
26534             }
26535             //Roo.log("child element over..");
26536             
26537         }
26538         this.currentEl = bindEl;
26539         this.currentTip.bind(bindEl);
26540         this.currentRegion = Roo.lib.Region.getRegion(dom);
26541         this.currentTip.enter();
26542         
26543     },
26544     leave : function(ev)
26545     {
26546         var dom = ev.getTarget();
26547         //Roo.log(['leave',dom]);
26548         if (!this.currentEl) {
26549             return;
26550         }
26551         
26552         
26553         if (dom != this.currentEl.dom) {
26554             return;
26555         }
26556         var xy = ev.getXY();
26557         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26558             return;
26559         }
26560         // only activate leave if mouse cursor is outside... bounding box..
26561         
26562         
26563         
26564         
26565         if (this.currentTip) {
26566             this.currentTip.leave();
26567         }
26568         //Roo.log('clear currentEl');
26569         this.currentEl = false;
26570         
26571         
26572     },
26573     alignment : {
26574         'left' : ['r-l', [-2,0], 'right'],
26575         'right' : ['l-r', [2,0], 'left'],
26576         'bottom' : ['t-b', [0,2], 'top'],
26577         'top' : [ 'b-t', [0,-2], 'bottom']
26578     }
26579     
26580 });
26581
26582
26583 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26584     
26585     
26586     bindEl : false,
26587     
26588     delay : null, // can be { show : 300 , hide: 500}
26589     
26590     timeout : null,
26591     
26592     hoverState : null, //???
26593     
26594     placement : 'bottom', 
26595     
26596     alignment : false,
26597     
26598     getAutoCreate : function(){
26599     
26600         var cfg = {
26601            cls : 'tooltip',
26602            role : 'tooltip',
26603            cn : [
26604                 {
26605                     cls : 'tooltip-arrow'
26606                 },
26607                 {
26608                     cls : 'tooltip-inner'
26609                 }
26610            ]
26611         };
26612         
26613         return cfg;
26614     },
26615     bind : function(el)
26616     {
26617         this.bindEl = el;
26618     },
26619       
26620     
26621     enter : function () {
26622        
26623         if (this.timeout != null) {
26624             clearTimeout(this.timeout);
26625         }
26626         
26627         this.hoverState = 'in';
26628          //Roo.log("enter - show");
26629         if (!this.delay || !this.delay.show) {
26630             this.show();
26631             return;
26632         }
26633         var _t = this;
26634         this.timeout = setTimeout(function () {
26635             if (_t.hoverState == 'in') {
26636                 _t.show();
26637             }
26638         }, this.delay.show);
26639     },
26640     leave : function()
26641     {
26642         clearTimeout(this.timeout);
26643     
26644         this.hoverState = 'out';
26645          if (!this.delay || !this.delay.hide) {
26646             this.hide();
26647             return;
26648         }
26649        
26650         var _t = this;
26651         this.timeout = setTimeout(function () {
26652             //Roo.log("leave - timeout");
26653             
26654             if (_t.hoverState == 'out') {
26655                 _t.hide();
26656                 Roo.bootstrap.Tooltip.currentEl = false;
26657             }
26658         }, delay);
26659     },
26660     
26661     show : function (msg)
26662     {
26663         if (!this.el) {
26664             this.render(document.body);
26665         }
26666         // set content.
26667         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26668         
26669         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26670         
26671         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26672         
26673         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26674         
26675         var placement = typeof this.placement == 'function' ?
26676             this.placement.call(this, this.el, on_el) :
26677             this.placement;
26678             
26679         var autoToken = /\s?auto?\s?/i;
26680         var autoPlace = autoToken.test(placement);
26681         if (autoPlace) {
26682             placement = placement.replace(autoToken, '') || 'top';
26683         }
26684         
26685         //this.el.detach()
26686         //this.el.setXY([0,0]);
26687         this.el.show();
26688         //this.el.dom.style.display='block';
26689         
26690         //this.el.appendTo(on_el);
26691         
26692         var p = this.getPosition();
26693         var box = this.el.getBox();
26694         
26695         if (autoPlace) {
26696             // fixme..
26697         }
26698         
26699         var align = this.alignment[placement];
26700         
26701         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26702         
26703         if(placement == 'top' || placement == 'bottom'){
26704             if(xy[0] < 0){
26705                 placement = 'right';
26706             }
26707             
26708             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26709                 placement = 'left';
26710             }
26711             
26712             var scroll = Roo.select('body', true).first().getScroll();
26713             
26714             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26715                 placement = 'top';
26716             }
26717             
26718             align = this.alignment[placement];
26719         }
26720         
26721         this.el.alignTo(this.bindEl, align[0],align[1]);
26722         //var arrow = this.el.select('.arrow',true).first();
26723         //arrow.set(align[2], 
26724         
26725         this.el.addClass(placement);
26726         
26727         this.el.addClass('in fade');
26728         
26729         this.hoverState = null;
26730         
26731         if (this.el.hasClass('fade')) {
26732             // fade it?
26733         }
26734         
26735     },
26736     hide : function()
26737     {
26738          
26739         if (!this.el) {
26740             return;
26741         }
26742         //this.el.setXY([0,0]);
26743         this.el.removeClass('in');
26744         //this.el.hide();
26745         
26746     }
26747     
26748 });
26749  
26750
26751  /*
26752  * - LGPL
26753  *
26754  * Location Picker
26755  * 
26756  */
26757
26758 /**
26759  * @class Roo.bootstrap.LocationPicker
26760  * @extends Roo.bootstrap.Component
26761  * Bootstrap LocationPicker class
26762  * @cfg {Number} latitude Position when init default 0
26763  * @cfg {Number} longitude Position when init default 0
26764  * @cfg {Number} zoom default 15
26765  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26766  * @cfg {Boolean} mapTypeControl default false
26767  * @cfg {Boolean} disableDoubleClickZoom default false
26768  * @cfg {Boolean} scrollwheel default true
26769  * @cfg {Boolean} streetViewControl default false
26770  * @cfg {Number} radius default 0
26771  * @cfg {String} locationName
26772  * @cfg {Boolean} draggable default true
26773  * @cfg {Boolean} enableAutocomplete default false
26774  * @cfg {Boolean} enableReverseGeocode default true
26775  * @cfg {String} markerTitle
26776  * 
26777  * @constructor
26778  * Create a new LocationPicker
26779  * @param {Object} config The config object
26780  */
26781
26782
26783 Roo.bootstrap.LocationPicker = function(config){
26784     
26785     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26786     
26787     this.addEvents({
26788         /**
26789          * @event initial
26790          * Fires when the picker initialized.
26791          * @param {Roo.bootstrap.LocationPicker} this
26792          * @param {Google Location} location
26793          */
26794         initial : true,
26795         /**
26796          * @event positionchanged
26797          * Fires when the picker position changed.
26798          * @param {Roo.bootstrap.LocationPicker} this
26799          * @param {Google Location} location
26800          */
26801         positionchanged : true,
26802         /**
26803          * @event resize
26804          * Fires when the map resize.
26805          * @param {Roo.bootstrap.LocationPicker} this
26806          */
26807         resize : true,
26808         /**
26809          * @event show
26810          * Fires when the map show.
26811          * @param {Roo.bootstrap.LocationPicker} this
26812          */
26813         show : true,
26814         /**
26815          * @event hide
26816          * Fires when the map hide.
26817          * @param {Roo.bootstrap.LocationPicker} this
26818          */
26819         hide : true,
26820         /**
26821          * @event mapClick
26822          * Fires when click the map.
26823          * @param {Roo.bootstrap.LocationPicker} this
26824          * @param {Map event} e
26825          */
26826         mapClick : true,
26827         /**
26828          * @event mapRightClick
26829          * Fires when right click the map.
26830          * @param {Roo.bootstrap.LocationPicker} this
26831          * @param {Map event} e
26832          */
26833         mapRightClick : true,
26834         /**
26835          * @event markerClick
26836          * Fires when click the marker.
26837          * @param {Roo.bootstrap.LocationPicker} this
26838          * @param {Map event} e
26839          */
26840         markerClick : true,
26841         /**
26842          * @event markerRightClick
26843          * Fires when right click the marker.
26844          * @param {Roo.bootstrap.LocationPicker} this
26845          * @param {Map event} e
26846          */
26847         markerRightClick : true,
26848         /**
26849          * @event OverlayViewDraw
26850          * Fires when OverlayView Draw
26851          * @param {Roo.bootstrap.LocationPicker} this
26852          */
26853         OverlayViewDraw : true,
26854         /**
26855          * @event OverlayViewOnAdd
26856          * Fires when OverlayView Draw
26857          * @param {Roo.bootstrap.LocationPicker} this
26858          */
26859         OverlayViewOnAdd : true,
26860         /**
26861          * @event OverlayViewOnRemove
26862          * Fires when OverlayView Draw
26863          * @param {Roo.bootstrap.LocationPicker} this
26864          */
26865         OverlayViewOnRemove : true,
26866         /**
26867          * @event OverlayViewShow
26868          * Fires when OverlayView Draw
26869          * @param {Roo.bootstrap.LocationPicker} this
26870          * @param {Pixel} cpx
26871          */
26872         OverlayViewShow : true,
26873         /**
26874          * @event OverlayViewHide
26875          * Fires when OverlayView Draw
26876          * @param {Roo.bootstrap.LocationPicker} this
26877          */
26878         OverlayViewHide : true,
26879         /**
26880          * @event loadexception
26881          * Fires when load google lib failed.
26882          * @param {Roo.bootstrap.LocationPicker} this
26883          */
26884         loadexception : true
26885     });
26886         
26887 };
26888
26889 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26890     
26891     gMapContext: false,
26892     
26893     latitude: 0,
26894     longitude: 0,
26895     zoom: 15,
26896     mapTypeId: false,
26897     mapTypeControl: false,
26898     disableDoubleClickZoom: false,
26899     scrollwheel: true,
26900     streetViewControl: false,
26901     radius: 0,
26902     locationName: '',
26903     draggable: true,
26904     enableAutocomplete: false,
26905     enableReverseGeocode: true,
26906     markerTitle: '',
26907     
26908     getAutoCreate: function()
26909     {
26910
26911         var cfg = {
26912             tag: 'div',
26913             cls: 'roo-location-picker'
26914         };
26915         
26916         return cfg
26917     },
26918     
26919     initEvents: function(ct, position)
26920     {       
26921         if(!this.el.getWidth() || this.isApplied()){
26922             return;
26923         }
26924         
26925         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26926         
26927         this.initial();
26928     },
26929     
26930     initial: function()
26931     {
26932         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26933             this.fireEvent('loadexception', this);
26934             return;
26935         }
26936         
26937         if(!this.mapTypeId){
26938             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26939         }
26940         
26941         this.gMapContext = this.GMapContext();
26942         
26943         this.initOverlayView();
26944         
26945         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26946         
26947         var _this = this;
26948                 
26949         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26950             _this.setPosition(_this.gMapContext.marker.position);
26951         });
26952         
26953         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26954             _this.fireEvent('mapClick', this, event);
26955             
26956         });
26957
26958         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26959             _this.fireEvent('mapRightClick', this, event);
26960             
26961         });
26962         
26963         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26964             _this.fireEvent('markerClick', this, event);
26965             
26966         });
26967
26968         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26969             _this.fireEvent('markerRightClick', this, event);
26970             
26971         });
26972         
26973         this.setPosition(this.gMapContext.location);
26974         
26975         this.fireEvent('initial', this, this.gMapContext.location);
26976     },
26977     
26978     initOverlayView: function()
26979     {
26980         var _this = this;
26981         
26982         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26983             
26984             draw: function()
26985             {
26986                 _this.fireEvent('OverlayViewDraw', _this);
26987             },
26988             
26989             onAdd: function()
26990             {
26991                 _this.fireEvent('OverlayViewOnAdd', _this);
26992             },
26993             
26994             onRemove: function()
26995             {
26996                 _this.fireEvent('OverlayViewOnRemove', _this);
26997             },
26998             
26999             show: function(cpx)
27000             {
27001                 _this.fireEvent('OverlayViewShow', _this, cpx);
27002             },
27003             
27004             hide: function()
27005             {
27006                 _this.fireEvent('OverlayViewHide', _this);
27007             }
27008             
27009         });
27010     },
27011     
27012     fromLatLngToContainerPixel: function(event)
27013     {
27014         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
27015     },
27016     
27017     isApplied: function() 
27018     {
27019         return this.getGmapContext() == false ? false : true;
27020     },
27021     
27022     getGmapContext: function() 
27023     {
27024         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
27025     },
27026     
27027     GMapContext: function() 
27028     {
27029         var position = new google.maps.LatLng(this.latitude, this.longitude);
27030         
27031         var _map = new google.maps.Map(this.el.dom, {
27032             center: position,
27033             zoom: this.zoom,
27034             mapTypeId: this.mapTypeId,
27035             mapTypeControl: this.mapTypeControl,
27036             disableDoubleClickZoom: this.disableDoubleClickZoom,
27037             scrollwheel: this.scrollwheel,
27038             streetViewControl: this.streetViewControl,
27039             locationName: this.locationName,
27040             draggable: this.draggable,
27041             enableAutocomplete: this.enableAutocomplete,
27042             enableReverseGeocode: this.enableReverseGeocode
27043         });
27044         
27045         var _marker = new google.maps.Marker({
27046             position: position,
27047             map: _map,
27048             title: this.markerTitle,
27049             draggable: this.draggable
27050         });
27051         
27052         return {
27053             map: _map,
27054             marker: _marker,
27055             circle: null,
27056             location: position,
27057             radius: this.radius,
27058             locationName: this.locationName,
27059             addressComponents: {
27060                 formatted_address: null,
27061                 addressLine1: null,
27062                 addressLine2: null,
27063                 streetName: null,
27064                 streetNumber: null,
27065                 city: null,
27066                 district: null,
27067                 state: null,
27068                 stateOrProvince: null
27069             },
27070             settings: this,
27071             domContainer: this.el.dom,
27072             geodecoder: new google.maps.Geocoder()
27073         };
27074     },
27075     
27076     drawCircle: function(center, radius, options) 
27077     {
27078         if (this.gMapContext.circle != null) {
27079             this.gMapContext.circle.setMap(null);
27080         }
27081         if (radius > 0) {
27082             radius *= 1;
27083             options = Roo.apply({}, options, {
27084                 strokeColor: "#0000FF",
27085                 strokeOpacity: .35,
27086                 strokeWeight: 2,
27087                 fillColor: "#0000FF",
27088                 fillOpacity: .2
27089             });
27090             
27091             options.map = this.gMapContext.map;
27092             options.radius = radius;
27093             options.center = center;
27094             this.gMapContext.circle = new google.maps.Circle(options);
27095             return this.gMapContext.circle;
27096         }
27097         
27098         return null;
27099     },
27100     
27101     setPosition: function(location) 
27102     {
27103         this.gMapContext.location = location;
27104         this.gMapContext.marker.setPosition(location);
27105         this.gMapContext.map.panTo(location);
27106         this.drawCircle(location, this.gMapContext.radius, {});
27107         
27108         var _this = this;
27109         
27110         if (this.gMapContext.settings.enableReverseGeocode) {
27111             this.gMapContext.geodecoder.geocode({
27112                 latLng: this.gMapContext.location
27113             }, function(results, status) {
27114                 
27115                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
27116                     _this.gMapContext.locationName = results[0].formatted_address;
27117                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
27118                     
27119                     _this.fireEvent('positionchanged', this, location);
27120                 }
27121             });
27122             
27123             return;
27124         }
27125         
27126         this.fireEvent('positionchanged', this, location);
27127     },
27128     
27129     resize: function()
27130     {
27131         google.maps.event.trigger(this.gMapContext.map, "resize");
27132         
27133         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
27134         
27135         this.fireEvent('resize', this);
27136     },
27137     
27138     setPositionByLatLng: function(latitude, longitude)
27139     {
27140         this.setPosition(new google.maps.LatLng(latitude, longitude));
27141     },
27142     
27143     getCurrentPosition: function() 
27144     {
27145         return {
27146             latitude: this.gMapContext.location.lat(),
27147             longitude: this.gMapContext.location.lng()
27148         };
27149     },
27150     
27151     getAddressName: function() 
27152     {
27153         return this.gMapContext.locationName;
27154     },
27155     
27156     getAddressComponents: function() 
27157     {
27158         return this.gMapContext.addressComponents;
27159     },
27160     
27161     address_component_from_google_geocode: function(address_components) 
27162     {
27163         var result = {};
27164         
27165         for (var i = 0; i < address_components.length; i++) {
27166             var component = address_components[i];
27167             if (component.types.indexOf("postal_code") >= 0) {
27168                 result.postalCode = component.short_name;
27169             } else if (component.types.indexOf("street_number") >= 0) {
27170                 result.streetNumber = component.short_name;
27171             } else if (component.types.indexOf("route") >= 0) {
27172                 result.streetName = component.short_name;
27173             } else if (component.types.indexOf("neighborhood") >= 0) {
27174                 result.city = component.short_name;
27175             } else if (component.types.indexOf("locality") >= 0) {
27176                 result.city = component.short_name;
27177             } else if (component.types.indexOf("sublocality") >= 0) {
27178                 result.district = component.short_name;
27179             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
27180                 result.stateOrProvince = component.short_name;
27181             } else if (component.types.indexOf("country") >= 0) {
27182                 result.country = component.short_name;
27183             }
27184         }
27185         
27186         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
27187         result.addressLine2 = "";
27188         return result;
27189     },
27190     
27191     setZoomLevel: function(zoom)
27192     {
27193         this.gMapContext.map.setZoom(zoom);
27194     },
27195     
27196     show: function()
27197     {
27198         if(!this.el){
27199             return;
27200         }
27201         
27202         this.el.show();
27203         
27204         this.resize();
27205         
27206         this.fireEvent('show', this);
27207     },
27208     
27209     hide: function()
27210     {
27211         if(!this.el){
27212             return;
27213         }
27214         
27215         this.el.hide();
27216         
27217         this.fireEvent('hide', this);
27218     }
27219     
27220 });
27221
27222 Roo.apply(Roo.bootstrap.LocationPicker, {
27223     
27224     OverlayView : function(map, options)
27225     {
27226         options = options || {};
27227         
27228         this.setMap(map);
27229     }
27230     
27231     
27232 });/**
27233  * @class Roo.bootstrap.Alert
27234  * @extends Roo.bootstrap.Component
27235  * Bootstrap Alert class - shows an alert area box
27236  * eg
27237  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
27238   Enter a valid email address
27239 </div>
27240  * @licence LGPL
27241  * @cfg {String} title The title of alert
27242  * @cfg {String} html The content of alert
27243  * @cfg {String} weight (  success | info | warning | danger )
27244  * @cfg {String} faicon font-awesomeicon
27245  * 
27246  * @constructor
27247  * Create a new alert
27248  * @param {Object} config The config object
27249  */
27250
27251
27252 Roo.bootstrap.Alert = function(config){
27253     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27254     
27255 };
27256
27257 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
27258     
27259     title: '',
27260     html: '',
27261     weight: false,
27262     faicon: false,
27263     
27264     getAutoCreate : function()
27265     {
27266         
27267         var cfg = {
27268             tag : 'div',
27269             cls : 'alert',
27270             cn : [
27271                 {
27272                     tag : 'i',
27273                     cls : 'roo-alert-icon'
27274                     
27275                 },
27276                 {
27277                     tag : 'b',
27278                     cls : 'roo-alert-title',
27279                     html : this.title
27280                 },
27281                 {
27282                     tag : 'span',
27283                     cls : 'roo-alert-text',
27284                     html : this.html
27285                 }
27286             ]
27287         };
27288         
27289         if(this.faicon){
27290             cfg.cn[0].cls += ' fa ' + this.faicon;
27291         }
27292         
27293         if(this.weight){
27294             cfg.cls += ' alert-' + this.weight;
27295         }
27296         
27297         return cfg;
27298     },
27299     
27300     initEvents: function() 
27301     {
27302         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27303     },
27304     
27305     setTitle : function(str)
27306     {
27307         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27308     },
27309     
27310     setText : function(str)
27311     {
27312         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27313     },
27314     
27315     setWeight : function(weight)
27316     {
27317         if(this.weight){
27318             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27319         }
27320         
27321         this.weight = weight;
27322         
27323         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27324     },
27325     
27326     setIcon : function(icon)
27327     {
27328         if(this.faicon){
27329             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27330         }
27331         
27332         this.faicon = icon;
27333         
27334         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27335     },
27336     
27337     hide: function() 
27338     {
27339         this.el.hide();   
27340     },
27341     
27342     show: function() 
27343     {  
27344         this.el.show();   
27345     }
27346     
27347 });
27348
27349  
27350 /*
27351 * Licence: LGPL
27352 */
27353
27354 /**
27355  * @class Roo.bootstrap.UploadCropbox
27356  * @extends Roo.bootstrap.Component
27357  * Bootstrap UploadCropbox class
27358  * @cfg {String} emptyText show when image has been loaded
27359  * @cfg {String} rotateNotify show when image too small to rotate
27360  * @cfg {Number} errorTimeout default 3000
27361  * @cfg {Number} minWidth default 300
27362  * @cfg {Number} minHeight default 300
27363  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27364  * @cfg {Boolean} isDocument (true|false) default false
27365  * @cfg {String} url action url
27366  * @cfg {String} paramName default 'imageUpload'
27367  * @cfg {String} method default POST
27368  * @cfg {Boolean} loadMask (true|false) default true
27369  * @cfg {Boolean} loadingText default 'Loading...'
27370  * 
27371  * @constructor
27372  * Create a new UploadCropbox
27373  * @param {Object} config The config object
27374  */
27375
27376 Roo.bootstrap.UploadCropbox = function(config){
27377     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27378     
27379     this.addEvents({
27380         /**
27381          * @event beforeselectfile
27382          * Fire before select file
27383          * @param {Roo.bootstrap.UploadCropbox} this
27384          */
27385         "beforeselectfile" : true,
27386         /**
27387          * @event initial
27388          * Fire after initEvent
27389          * @param {Roo.bootstrap.UploadCropbox} this
27390          */
27391         "initial" : true,
27392         /**
27393          * @event crop
27394          * Fire after initEvent
27395          * @param {Roo.bootstrap.UploadCropbox} this
27396          * @param {String} data
27397          */
27398         "crop" : true,
27399         /**
27400          * @event prepare
27401          * Fire when preparing the file data
27402          * @param {Roo.bootstrap.UploadCropbox} this
27403          * @param {Object} file
27404          */
27405         "prepare" : true,
27406         /**
27407          * @event exception
27408          * Fire when get exception
27409          * @param {Roo.bootstrap.UploadCropbox} this
27410          * @param {XMLHttpRequest} xhr
27411          */
27412         "exception" : true,
27413         /**
27414          * @event beforeloadcanvas
27415          * Fire before load the canvas
27416          * @param {Roo.bootstrap.UploadCropbox} this
27417          * @param {String} src
27418          */
27419         "beforeloadcanvas" : true,
27420         /**
27421          * @event trash
27422          * Fire when trash image
27423          * @param {Roo.bootstrap.UploadCropbox} this
27424          */
27425         "trash" : true,
27426         /**
27427          * @event download
27428          * Fire when download the image
27429          * @param {Roo.bootstrap.UploadCropbox} this
27430          */
27431         "download" : true,
27432         /**
27433          * @event footerbuttonclick
27434          * Fire when footerbuttonclick
27435          * @param {Roo.bootstrap.UploadCropbox} this
27436          * @param {String} type
27437          */
27438         "footerbuttonclick" : true,
27439         /**
27440          * @event resize
27441          * Fire when resize
27442          * @param {Roo.bootstrap.UploadCropbox} this
27443          */
27444         "resize" : true,
27445         /**
27446          * @event rotate
27447          * Fire when rotate the image
27448          * @param {Roo.bootstrap.UploadCropbox} this
27449          * @param {String} pos
27450          */
27451         "rotate" : true,
27452         /**
27453          * @event inspect
27454          * Fire when inspect the file
27455          * @param {Roo.bootstrap.UploadCropbox} this
27456          * @param {Object} file
27457          */
27458         "inspect" : true,
27459         /**
27460          * @event upload
27461          * Fire when xhr upload the file
27462          * @param {Roo.bootstrap.UploadCropbox} this
27463          * @param {Object} data
27464          */
27465         "upload" : true,
27466         /**
27467          * @event arrange
27468          * Fire when arrange the file data
27469          * @param {Roo.bootstrap.UploadCropbox} this
27470          * @param {Object} formData
27471          */
27472         "arrange" : true
27473     });
27474     
27475     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27476 };
27477
27478 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27479     
27480     emptyText : 'Click to upload image',
27481     rotateNotify : 'Image is too small to rotate',
27482     errorTimeout : 3000,
27483     scale : 0,
27484     baseScale : 1,
27485     rotate : 0,
27486     dragable : false,
27487     pinching : false,
27488     mouseX : 0,
27489     mouseY : 0,
27490     cropData : false,
27491     minWidth : 300,
27492     minHeight : 300,
27493     file : false,
27494     exif : {},
27495     baseRotate : 1,
27496     cropType : 'image/jpeg',
27497     buttons : false,
27498     canvasLoaded : false,
27499     isDocument : false,
27500     method : 'POST',
27501     paramName : 'imageUpload',
27502     loadMask : true,
27503     loadingText : 'Loading...',
27504     maskEl : false,
27505     
27506     getAutoCreate : function()
27507     {
27508         var cfg = {
27509             tag : 'div',
27510             cls : 'roo-upload-cropbox',
27511             cn : [
27512                 {
27513                     tag : 'input',
27514                     cls : 'roo-upload-cropbox-selector',
27515                     type : 'file'
27516                 },
27517                 {
27518                     tag : 'div',
27519                     cls : 'roo-upload-cropbox-body',
27520                     style : 'cursor:pointer',
27521                     cn : [
27522                         {
27523                             tag : 'div',
27524                             cls : 'roo-upload-cropbox-preview'
27525                         },
27526                         {
27527                             tag : 'div',
27528                             cls : 'roo-upload-cropbox-thumb'
27529                         },
27530                         {
27531                             tag : 'div',
27532                             cls : 'roo-upload-cropbox-empty-notify',
27533                             html : this.emptyText
27534                         },
27535                         {
27536                             tag : 'div',
27537                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27538                             html : this.rotateNotify
27539                         }
27540                     ]
27541                 },
27542                 {
27543                     tag : 'div',
27544                     cls : 'roo-upload-cropbox-footer',
27545                     cn : {
27546                         tag : 'div',
27547                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27548                         cn : []
27549                     }
27550                 }
27551             ]
27552         };
27553         
27554         return cfg;
27555     },
27556     
27557     onRender : function(ct, position)
27558     {
27559         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27560         
27561         if (this.buttons.length) {
27562             
27563             Roo.each(this.buttons, function(bb) {
27564                 
27565                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27566                 
27567                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27568                 
27569             }, this);
27570         }
27571         
27572         if(this.loadMask){
27573             this.maskEl = this.el;
27574         }
27575     },
27576     
27577     initEvents : function()
27578     {
27579         this.urlAPI = (window.createObjectURL && window) || 
27580                                 (window.URL && URL.revokeObjectURL && URL) || 
27581                                 (window.webkitURL && webkitURL);
27582                         
27583         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27584         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27585         
27586         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27587         this.selectorEl.hide();
27588         
27589         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27590         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27591         
27592         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27593         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27594         this.thumbEl.hide();
27595         
27596         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27597         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27598         
27599         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27600         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27601         this.errorEl.hide();
27602         
27603         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27604         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27605         this.footerEl.hide();
27606         
27607         this.setThumbBoxSize();
27608         
27609         this.bind();
27610         
27611         this.resize();
27612         
27613         this.fireEvent('initial', this);
27614     },
27615
27616     bind : function()
27617     {
27618         var _this = this;
27619         
27620         window.addEventListener("resize", function() { _this.resize(); } );
27621         
27622         this.bodyEl.on('click', this.beforeSelectFile, this);
27623         
27624         if(Roo.isTouch){
27625             this.bodyEl.on('touchstart', this.onTouchStart, this);
27626             this.bodyEl.on('touchmove', this.onTouchMove, this);
27627             this.bodyEl.on('touchend', this.onTouchEnd, this);
27628         }
27629         
27630         if(!Roo.isTouch){
27631             this.bodyEl.on('mousedown', this.onMouseDown, this);
27632             this.bodyEl.on('mousemove', this.onMouseMove, this);
27633             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27634             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27635             Roo.get(document).on('mouseup', this.onMouseUp, this);
27636         }
27637         
27638         this.selectorEl.on('change', this.onFileSelected, this);
27639     },
27640     
27641     reset : function()
27642     {    
27643         this.scale = 0;
27644         this.baseScale = 1;
27645         this.rotate = 0;
27646         this.baseRotate = 1;
27647         this.dragable = false;
27648         this.pinching = false;
27649         this.mouseX = 0;
27650         this.mouseY = 0;
27651         this.cropData = false;
27652         this.notifyEl.dom.innerHTML = this.emptyText;
27653         
27654         this.selectorEl.dom.value = '';
27655         
27656     },
27657     
27658     resize : function()
27659     {
27660         if(this.fireEvent('resize', this) != false){
27661             this.setThumbBoxPosition();
27662             this.setCanvasPosition();
27663         }
27664     },
27665     
27666     onFooterButtonClick : function(e, el, o, type)
27667     {
27668         switch (type) {
27669             case 'rotate-left' :
27670                 this.onRotateLeft(e);
27671                 break;
27672             case 'rotate-right' :
27673                 this.onRotateRight(e);
27674                 break;
27675             case 'picture' :
27676                 this.beforeSelectFile(e);
27677                 break;
27678             case 'trash' :
27679                 this.trash(e);
27680                 break;
27681             case 'crop' :
27682                 this.crop(e);
27683                 break;
27684             case 'download' :
27685                 this.download(e);
27686                 break;
27687             default :
27688                 break;
27689         }
27690         
27691         this.fireEvent('footerbuttonclick', this, type);
27692     },
27693     
27694     beforeSelectFile : function(e)
27695     {
27696         e.preventDefault();
27697         
27698         if(this.fireEvent('beforeselectfile', this) != false){
27699             this.selectorEl.dom.click();
27700         }
27701     },
27702     
27703     onFileSelected : function(e)
27704     {
27705         e.preventDefault();
27706         
27707         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27708             return;
27709         }
27710         
27711         var file = this.selectorEl.dom.files[0];
27712         
27713         if(this.fireEvent('inspect', this, file) != false){
27714             this.prepare(file);
27715         }
27716         
27717     },
27718     
27719     trash : function(e)
27720     {
27721         this.fireEvent('trash', this);
27722     },
27723     
27724     download : function(e)
27725     {
27726         this.fireEvent('download', this);
27727     },
27728     
27729     loadCanvas : function(src)
27730     {   
27731         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27732             
27733             this.reset();
27734             
27735             this.imageEl = document.createElement('img');
27736             
27737             var _this = this;
27738             
27739             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27740             
27741             this.imageEl.src = src;
27742         }
27743     },
27744     
27745     onLoadCanvas : function()
27746     {   
27747         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27748         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27749         
27750         this.bodyEl.un('click', this.beforeSelectFile, this);
27751         
27752         this.notifyEl.hide();
27753         this.thumbEl.show();
27754         this.footerEl.show();
27755         
27756         this.baseRotateLevel();
27757         
27758         if(this.isDocument){
27759             this.setThumbBoxSize();
27760         }
27761         
27762         this.setThumbBoxPosition();
27763         
27764         this.baseScaleLevel();
27765         
27766         this.draw();
27767         
27768         this.resize();
27769         
27770         this.canvasLoaded = true;
27771         
27772         if(this.loadMask){
27773             this.maskEl.unmask();
27774         }
27775         
27776     },
27777     
27778     setCanvasPosition : function()
27779     {   
27780         if(!this.canvasEl){
27781             return;
27782         }
27783         
27784         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27785         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27786         
27787         this.previewEl.setLeft(pw);
27788         this.previewEl.setTop(ph);
27789         
27790     },
27791     
27792     onMouseDown : function(e)
27793     {   
27794         e.stopEvent();
27795         
27796         this.dragable = true;
27797         this.pinching = false;
27798         
27799         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27800             this.dragable = false;
27801             return;
27802         }
27803         
27804         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27805         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27806         
27807     },
27808     
27809     onMouseMove : function(e)
27810     {   
27811         e.stopEvent();
27812         
27813         if(!this.canvasLoaded){
27814             return;
27815         }
27816         
27817         if (!this.dragable){
27818             return;
27819         }
27820         
27821         var minX = Math.ceil(this.thumbEl.getLeft(true));
27822         var minY = Math.ceil(this.thumbEl.getTop(true));
27823         
27824         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27825         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27826         
27827         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27828         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27829         
27830         x = x - this.mouseX;
27831         y = y - this.mouseY;
27832         
27833         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27834         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27835         
27836         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27837         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27838         
27839         this.previewEl.setLeft(bgX);
27840         this.previewEl.setTop(bgY);
27841         
27842         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27843         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27844     },
27845     
27846     onMouseUp : function(e)
27847     {   
27848         e.stopEvent();
27849         
27850         this.dragable = false;
27851     },
27852     
27853     onMouseWheel : function(e)
27854     {   
27855         e.stopEvent();
27856         
27857         this.startScale = this.scale;
27858         
27859         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27860         
27861         if(!this.zoomable()){
27862             this.scale = this.startScale;
27863             return;
27864         }
27865         
27866         this.draw();
27867         
27868         return;
27869     },
27870     
27871     zoomable : function()
27872     {
27873         var minScale = this.thumbEl.getWidth() / this.minWidth;
27874         
27875         if(this.minWidth < this.minHeight){
27876             minScale = this.thumbEl.getHeight() / this.minHeight;
27877         }
27878         
27879         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27880         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27881         
27882         if(
27883                 this.isDocument &&
27884                 (this.rotate == 0 || this.rotate == 180) && 
27885                 (
27886                     width > this.imageEl.OriginWidth || 
27887                     height > this.imageEl.OriginHeight ||
27888                     (width < this.minWidth && height < this.minHeight)
27889                 )
27890         ){
27891             return false;
27892         }
27893         
27894         if(
27895                 this.isDocument &&
27896                 (this.rotate == 90 || this.rotate == 270) && 
27897                 (
27898                     width > this.imageEl.OriginWidth || 
27899                     height > this.imageEl.OriginHeight ||
27900                     (width < this.minHeight && height < this.minWidth)
27901                 )
27902         ){
27903             return false;
27904         }
27905         
27906         if(
27907                 !this.isDocument &&
27908                 (this.rotate == 0 || this.rotate == 180) && 
27909                 (
27910                     width < this.minWidth || 
27911                     width > this.imageEl.OriginWidth || 
27912                     height < this.minHeight || 
27913                     height > this.imageEl.OriginHeight
27914                 )
27915         ){
27916             return false;
27917         }
27918         
27919         if(
27920                 !this.isDocument &&
27921                 (this.rotate == 90 || this.rotate == 270) && 
27922                 (
27923                     width < this.minHeight || 
27924                     width > this.imageEl.OriginWidth || 
27925                     height < this.minWidth || 
27926                     height > this.imageEl.OriginHeight
27927                 )
27928         ){
27929             return false;
27930         }
27931         
27932         return true;
27933         
27934     },
27935     
27936     onRotateLeft : function(e)
27937     {   
27938         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27939             
27940             var minScale = this.thumbEl.getWidth() / this.minWidth;
27941             
27942             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27943             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27944             
27945             this.startScale = this.scale;
27946             
27947             while (this.getScaleLevel() < minScale){
27948             
27949                 this.scale = this.scale + 1;
27950                 
27951                 if(!this.zoomable()){
27952                     break;
27953                 }
27954                 
27955                 if(
27956                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27957                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27958                 ){
27959                     continue;
27960                 }
27961                 
27962                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27963
27964                 this.draw();
27965                 
27966                 return;
27967             }
27968             
27969             this.scale = this.startScale;
27970             
27971             this.onRotateFail();
27972             
27973             return false;
27974         }
27975         
27976         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27977
27978         if(this.isDocument){
27979             this.setThumbBoxSize();
27980             this.setThumbBoxPosition();
27981             this.setCanvasPosition();
27982         }
27983         
27984         this.draw();
27985         
27986         this.fireEvent('rotate', this, 'left');
27987         
27988     },
27989     
27990     onRotateRight : function(e)
27991     {
27992         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27993             
27994             var minScale = this.thumbEl.getWidth() / this.minWidth;
27995         
27996             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27997             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27998             
27999             this.startScale = this.scale;
28000             
28001             while (this.getScaleLevel() < minScale){
28002             
28003                 this.scale = this.scale + 1;
28004                 
28005                 if(!this.zoomable()){
28006                     break;
28007                 }
28008                 
28009                 if(
28010                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28011                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28012                 ){
28013                     continue;
28014                 }
28015                 
28016                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28017
28018                 this.draw();
28019                 
28020                 return;
28021             }
28022             
28023             this.scale = this.startScale;
28024             
28025             this.onRotateFail();
28026             
28027             return false;
28028         }
28029         
28030         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28031
28032         if(this.isDocument){
28033             this.setThumbBoxSize();
28034             this.setThumbBoxPosition();
28035             this.setCanvasPosition();
28036         }
28037         
28038         this.draw();
28039         
28040         this.fireEvent('rotate', this, 'right');
28041     },
28042     
28043     onRotateFail : function()
28044     {
28045         this.errorEl.show(true);
28046         
28047         var _this = this;
28048         
28049         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
28050     },
28051     
28052     draw : function()
28053     {
28054         this.previewEl.dom.innerHTML = '';
28055         
28056         var canvasEl = document.createElement("canvas");
28057         
28058         var contextEl = canvasEl.getContext("2d");
28059         
28060         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28061         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28062         var center = this.imageEl.OriginWidth / 2;
28063         
28064         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
28065             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28066             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28067             center = this.imageEl.OriginHeight / 2;
28068         }
28069         
28070         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
28071         
28072         contextEl.translate(center, center);
28073         contextEl.rotate(this.rotate * Math.PI / 180);
28074
28075         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28076         
28077         this.canvasEl = document.createElement("canvas");
28078         
28079         this.contextEl = this.canvasEl.getContext("2d");
28080         
28081         switch (this.rotate) {
28082             case 0 :
28083                 
28084                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28085                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28086                 
28087                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28088                 
28089                 break;
28090             case 90 : 
28091                 
28092                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28093                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28094                 
28095                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28096                     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);
28097                     break;
28098                 }
28099                 
28100                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28101                 
28102                 break;
28103             case 180 :
28104                 
28105                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28106                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28107                 
28108                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28109                     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);
28110                     break;
28111                 }
28112                 
28113                 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);
28114                 
28115                 break;
28116             case 270 :
28117                 
28118                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28119                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28120         
28121                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28122                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28123                     break;
28124                 }
28125                 
28126                 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);
28127                 
28128                 break;
28129             default : 
28130                 break;
28131         }
28132         
28133         this.previewEl.appendChild(this.canvasEl);
28134         
28135         this.setCanvasPosition();
28136     },
28137     
28138     crop : function()
28139     {
28140         if(!this.canvasLoaded){
28141             return;
28142         }
28143         
28144         var imageCanvas = document.createElement("canvas");
28145         
28146         var imageContext = imageCanvas.getContext("2d");
28147         
28148         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28149         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28150         
28151         var center = imageCanvas.width / 2;
28152         
28153         imageContext.translate(center, center);
28154         
28155         imageContext.rotate(this.rotate * Math.PI / 180);
28156         
28157         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28158         
28159         var canvas = document.createElement("canvas");
28160         
28161         var context = canvas.getContext("2d");
28162                 
28163         canvas.width = this.minWidth;
28164         canvas.height = this.minHeight;
28165
28166         switch (this.rotate) {
28167             case 0 :
28168                 
28169                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28170                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28171                 
28172                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28173                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28174                 
28175                 var targetWidth = this.minWidth - 2 * x;
28176                 var targetHeight = this.minHeight - 2 * y;
28177                 
28178                 var scale = 1;
28179                 
28180                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28181                     scale = targetWidth / width;
28182                 }
28183                 
28184                 if(x > 0 && y == 0){
28185                     scale = targetHeight / height;
28186                 }
28187                 
28188                 if(x > 0 && y > 0){
28189                     scale = targetWidth / width;
28190                     
28191                     if(width < height){
28192                         scale = targetHeight / height;
28193                     }
28194                 }
28195                 
28196                 context.scale(scale, scale);
28197                 
28198                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28199                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28200
28201                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28202                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28203
28204                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28205                 
28206                 break;
28207             case 90 : 
28208                 
28209                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28210                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28211                 
28212                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28213                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28214                 
28215                 var targetWidth = this.minWidth - 2 * x;
28216                 var targetHeight = this.minHeight - 2 * y;
28217                 
28218                 var scale = 1;
28219                 
28220                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28221                     scale = targetWidth / width;
28222                 }
28223                 
28224                 if(x > 0 && y == 0){
28225                     scale = targetHeight / height;
28226                 }
28227                 
28228                 if(x > 0 && y > 0){
28229                     scale = targetWidth / width;
28230                     
28231                     if(width < height){
28232                         scale = targetHeight / height;
28233                     }
28234                 }
28235                 
28236                 context.scale(scale, scale);
28237                 
28238                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28239                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28240
28241                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28242                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28243                 
28244                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28245                 
28246                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28247                 
28248                 break;
28249             case 180 :
28250                 
28251                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28252                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28253                 
28254                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28255                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28256                 
28257                 var targetWidth = this.minWidth - 2 * x;
28258                 var targetHeight = this.minHeight - 2 * y;
28259                 
28260                 var scale = 1;
28261                 
28262                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28263                     scale = targetWidth / width;
28264                 }
28265                 
28266                 if(x > 0 && y == 0){
28267                     scale = targetHeight / height;
28268                 }
28269                 
28270                 if(x > 0 && y > 0){
28271                     scale = targetWidth / width;
28272                     
28273                     if(width < height){
28274                         scale = targetHeight / height;
28275                     }
28276                 }
28277                 
28278                 context.scale(scale, scale);
28279                 
28280                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28281                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28282
28283                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28284                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28285
28286                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28287                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28288                 
28289                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28290                 
28291                 break;
28292             case 270 :
28293                 
28294                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28295                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28296                 
28297                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28298                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28299                 
28300                 var targetWidth = this.minWidth - 2 * x;
28301                 var targetHeight = this.minHeight - 2 * y;
28302                 
28303                 var scale = 1;
28304                 
28305                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28306                     scale = targetWidth / width;
28307                 }
28308                 
28309                 if(x > 0 && y == 0){
28310                     scale = targetHeight / height;
28311                 }
28312                 
28313                 if(x > 0 && y > 0){
28314                     scale = targetWidth / width;
28315                     
28316                     if(width < height){
28317                         scale = targetHeight / height;
28318                     }
28319                 }
28320                 
28321                 context.scale(scale, scale);
28322                 
28323                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28324                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28325
28326                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28327                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28328                 
28329                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28330                 
28331                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28332                 
28333                 break;
28334             default : 
28335                 break;
28336         }
28337         
28338         this.cropData = canvas.toDataURL(this.cropType);
28339         
28340         if(this.fireEvent('crop', this, this.cropData) !== false){
28341             this.process(this.file, this.cropData);
28342         }
28343         
28344         return;
28345         
28346     },
28347     
28348     setThumbBoxSize : function()
28349     {
28350         var width, height;
28351         
28352         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28353             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28354             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28355             
28356             this.minWidth = width;
28357             this.minHeight = height;
28358             
28359             if(this.rotate == 90 || this.rotate == 270){
28360                 this.minWidth = height;
28361                 this.minHeight = width;
28362             }
28363         }
28364         
28365         height = 300;
28366         width = Math.ceil(this.minWidth * height / this.minHeight);
28367         
28368         if(this.minWidth > this.minHeight){
28369             width = 300;
28370             height = Math.ceil(this.minHeight * width / this.minWidth);
28371         }
28372         
28373         this.thumbEl.setStyle({
28374             width : width + 'px',
28375             height : height + 'px'
28376         });
28377
28378         return;
28379             
28380     },
28381     
28382     setThumbBoxPosition : function()
28383     {
28384         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28385         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28386         
28387         this.thumbEl.setLeft(x);
28388         this.thumbEl.setTop(y);
28389         
28390     },
28391     
28392     baseRotateLevel : function()
28393     {
28394         this.baseRotate = 1;
28395         
28396         if(
28397                 typeof(this.exif) != 'undefined' &&
28398                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28399                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28400         ){
28401             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28402         }
28403         
28404         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28405         
28406     },
28407     
28408     baseScaleLevel : function()
28409     {
28410         var width, height;
28411         
28412         if(this.isDocument){
28413             
28414             if(this.baseRotate == 6 || this.baseRotate == 8){
28415             
28416                 height = this.thumbEl.getHeight();
28417                 this.baseScale = height / this.imageEl.OriginWidth;
28418
28419                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28420                     width = this.thumbEl.getWidth();
28421                     this.baseScale = width / this.imageEl.OriginHeight;
28422                 }
28423
28424                 return;
28425             }
28426
28427             height = this.thumbEl.getHeight();
28428             this.baseScale = height / this.imageEl.OriginHeight;
28429
28430             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28431                 width = this.thumbEl.getWidth();
28432                 this.baseScale = width / this.imageEl.OriginWidth;
28433             }
28434
28435             return;
28436         }
28437         
28438         if(this.baseRotate == 6 || this.baseRotate == 8){
28439             
28440             width = this.thumbEl.getHeight();
28441             this.baseScale = width / this.imageEl.OriginHeight;
28442             
28443             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28444                 height = this.thumbEl.getWidth();
28445                 this.baseScale = height / this.imageEl.OriginHeight;
28446             }
28447             
28448             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28449                 height = this.thumbEl.getWidth();
28450                 this.baseScale = height / this.imageEl.OriginHeight;
28451                 
28452                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28453                     width = this.thumbEl.getHeight();
28454                     this.baseScale = width / this.imageEl.OriginWidth;
28455                 }
28456             }
28457             
28458             return;
28459         }
28460         
28461         width = this.thumbEl.getWidth();
28462         this.baseScale = width / this.imageEl.OriginWidth;
28463         
28464         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28465             height = this.thumbEl.getHeight();
28466             this.baseScale = height / this.imageEl.OriginHeight;
28467         }
28468         
28469         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28470             
28471             height = this.thumbEl.getHeight();
28472             this.baseScale = height / this.imageEl.OriginHeight;
28473             
28474             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28475                 width = this.thumbEl.getWidth();
28476                 this.baseScale = width / this.imageEl.OriginWidth;
28477             }
28478             
28479         }
28480         
28481         return;
28482     },
28483     
28484     getScaleLevel : function()
28485     {
28486         return this.baseScale * Math.pow(1.1, this.scale);
28487     },
28488     
28489     onTouchStart : function(e)
28490     {
28491         if(!this.canvasLoaded){
28492             this.beforeSelectFile(e);
28493             return;
28494         }
28495         
28496         var touches = e.browserEvent.touches;
28497         
28498         if(!touches){
28499             return;
28500         }
28501         
28502         if(touches.length == 1){
28503             this.onMouseDown(e);
28504             return;
28505         }
28506         
28507         if(touches.length != 2){
28508             return;
28509         }
28510         
28511         var coords = [];
28512         
28513         for(var i = 0, finger; finger = touches[i]; i++){
28514             coords.push(finger.pageX, finger.pageY);
28515         }
28516         
28517         var x = Math.pow(coords[0] - coords[2], 2);
28518         var y = Math.pow(coords[1] - coords[3], 2);
28519         
28520         this.startDistance = Math.sqrt(x + y);
28521         
28522         this.startScale = this.scale;
28523         
28524         this.pinching = true;
28525         this.dragable = false;
28526         
28527     },
28528     
28529     onTouchMove : function(e)
28530     {
28531         if(!this.pinching && !this.dragable){
28532             return;
28533         }
28534         
28535         var touches = e.browserEvent.touches;
28536         
28537         if(!touches){
28538             return;
28539         }
28540         
28541         if(this.dragable){
28542             this.onMouseMove(e);
28543             return;
28544         }
28545         
28546         var coords = [];
28547         
28548         for(var i = 0, finger; finger = touches[i]; i++){
28549             coords.push(finger.pageX, finger.pageY);
28550         }
28551         
28552         var x = Math.pow(coords[0] - coords[2], 2);
28553         var y = Math.pow(coords[1] - coords[3], 2);
28554         
28555         this.endDistance = Math.sqrt(x + y);
28556         
28557         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28558         
28559         if(!this.zoomable()){
28560             this.scale = this.startScale;
28561             return;
28562         }
28563         
28564         this.draw();
28565         
28566     },
28567     
28568     onTouchEnd : function(e)
28569     {
28570         this.pinching = false;
28571         this.dragable = false;
28572         
28573     },
28574     
28575     process : function(file, crop)
28576     {
28577         if(this.loadMask){
28578             this.maskEl.mask(this.loadingText);
28579         }
28580         
28581         this.xhr = new XMLHttpRequest();
28582         
28583         file.xhr = this.xhr;
28584
28585         this.xhr.open(this.method, this.url, true);
28586         
28587         var headers = {
28588             "Accept": "application/json",
28589             "Cache-Control": "no-cache",
28590             "X-Requested-With": "XMLHttpRequest"
28591         };
28592         
28593         for (var headerName in headers) {
28594             var headerValue = headers[headerName];
28595             if (headerValue) {
28596                 this.xhr.setRequestHeader(headerName, headerValue);
28597             }
28598         }
28599         
28600         var _this = this;
28601         
28602         this.xhr.onload = function()
28603         {
28604             _this.xhrOnLoad(_this.xhr);
28605         }
28606         
28607         this.xhr.onerror = function()
28608         {
28609             _this.xhrOnError(_this.xhr);
28610         }
28611         
28612         var formData = new FormData();
28613
28614         formData.append('returnHTML', 'NO');
28615         
28616         if(crop){
28617             formData.append('crop', crop);
28618         }
28619         
28620         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28621             formData.append(this.paramName, file, file.name);
28622         }
28623         
28624         if(typeof(file.filename) != 'undefined'){
28625             formData.append('filename', file.filename);
28626         }
28627         
28628         if(typeof(file.mimetype) != 'undefined'){
28629             formData.append('mimetype', file.mimetype);
28630         }
28631         
28632         if(this.fireEvent('arrange', this, formData) != false){
28633             this.xhr.send(formData);
28634         };
28635     },
28636     
28637     xhrOnLoad : function(xhr)
28638     {
28639         if(this.loadMask){
28640             this.maskEl.unmask();
28641         }
28642         
28643         if (xhr.readyState !== 4) {
28644             this.fireEvent('exception', this, xhr);
28645             return;
28646         }
28647
28648         var response = Roo.decode(xhr.responseText);
28649         
28650         if(!response.success){
28651             this.fireEvent('exception', this, xhr);
28652             return;
28653         }
28654         
28655         var response = Roo.decode(xhr.responseText);
28656         
28657         this.fireEvent('upload', this, response);
28658         
28659     },
28660     
28661     xhrOnError : function()
28662     {
28663         if(this.loadMask){
28664             this.maskEl.unmask();
28665         }
28666         
28667         Roo.log('xhr on error');
28668         
28669         var response = Roo.decode(xhr.responseText);
28670           
28671         Roo.log(response);
28672         
28673     },
28674     
28675     prepare : function(file)
28676     {   
28677         if(this.loadMask){
28678             this.maskEl.mask(this.loadingText);
28679         }
28680         
28681         this.file = false;
28682         this.exif = {};
28683         
28684         if(typeof(file) === 'string'){
28685             this.loadCanvas(file);
28686             return;
28687         }
28688         
28689         if(!file || !this.urlAPI){
28690             return;
28691         }
28692         
28693         this.file = file;
28694         this.cropType = file.type;
28695         
28696         var _this = this;
28697         
28698         if(this.fireEvent('prepare', this, this.file) != false){
28699             
28700             var reader = new FileReader();
28701             
28702             reader.onload = function (e) {
28703                 if (e.target.error) {
28704                     Roo.log(e.target.error);
28705                     return;
28706                 }
28707                 
28708                 var buffer = e.target.result,
28709                     dataView = new DataView(buffer),
28710                     offset = 2,
28711                     maxOffset = dataView.byteLength - 4,
28712                     markerBytes,
28713                     markerLength;
28714                 
28715                 if (dataView.getUint16(0) === 0xffd8) {
28716                     while (offset < maxOffset) {
28717                         markerBytes = dataView.getUint16(offset);
28718                         
28719                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28720                             markerLength = dataView.getUint16(offset + 2) + 2;
28721                             if (offset + markerLength > dataView.byteLength) {
28722                                 Roo.log('Invalid meta data: Invalid segment size.');
28723                                 break;
28724                             }
28725                             
28726                             if(markerBytes == 0xffe1){
28727                                 _this.parseExifData(
28728                                     dataView,
28729                                     offset,
28730                                     markerLength
28731                                 );
28732                             }
28733                             
28734                             offset += markerLength;
28735                             
28736                             continue;
28737                         }
28738                         
28739                         break;
28740                     }
28741                     
28742                 }
28743                 
28744                 var url = _this.urlAPI.createObjectURL(_this.file);
28745                 
28746                 _this.loadCanvas(url);
28747                 
28748                 return;
28749             }
28750             
28751             reader.readAsArrayBuffer(this.file);
28752             
28753         }
28754         
28755     },
28756     
28757     parseExifData : function(dataView, offset, length)
28758     {
28759         var tiffOffset = offset + 10,
28760             littleEndian,
28761             dirOffset;
28762     
28763         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28764             // No Exif data, might be XMP data instead
28765             return;
28766         }
28767         
28768         // Check for the ASCII code for "Exif" (0x45786966):
28769         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28770             // No Exif data, might be XMP data instead
28771             return;
28772         }
28773         if (tiffOffset + 8 > dataView.byteLength) {
28774             Roo.log('Invalid Exif data: Invalid segment size.');
28775             return;
28776         }
28777         // Check for the two null bytes:
28778         if (dataView.getUint16(offset + 8) !== 0x0000) {
28779             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28780             return;
28781         }
28782         // Check the byte alignment:
28783         switch (dataView.getUint16(tiffOffset)) {
28784         case 0x4949:
28785             littleEndian = true;
28786             break;
28787         case 0x4D4D:
28788             littleEndian = false;
28789             break;
28790         default:
28791             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28792             return;
28793         }
28794         // Check for the TIFF tag marker (0x002A):
28795         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28796             Roo.log('Invalid Exif data: Missing TIFF marker.');
28797             return;
28798         }
28799         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28800         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28801         
28802         this.parseExifTags(
28803             dataView,
28804             tiffOffset,
28805             tiffOffset + dirOffset,
28806             littleEndian
28807         );
28808     },
28809     
28810     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28811     {
28812         var tagsNumber,
28813             dirEndOffset,
28814             i;
28815         if (dirOffset + 6 > dataView.byteLength) {
28816             Roo.log('Invalid Exif data: Invalid directory offset.');
28817             return;
28818         }
28819         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28820         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28821         if (dirEndOffset + 4 > dataView.byteLength) {
28822             Roo.log('Invalid Exif data: Invalid directory size.');
28823             return;
28824         }
28825         for (i = 0; i < tagsNumber; i += 1) {
28826             this.parseExifTag(
28827                 dataView,
28828                 tiffOffset,
28829                 dirOffset + 2 + 12 * i, // tag offset
28830                 littleEndian
28831             );
28832         }
28833         // Return the offset to the next directory:
28834         return dataView.getUint32(dirEndOffset, littleEndian);
28835     },
28836     
28837     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28838     {
28839         var tag = dataView.getUint16(offset, littleEndian);
28840         
28841         this.exif[tag] = this.getExifValue(
28842             dataView,
28843             tiffOffset,
28844             offset,
28845             dataView.getUint16(offset + 2, littleEndian), // tag type
28846             dataView.getUint32(offset + 4, littleEndian), // tag length
28847             littleEndian
28848         );
28849     },
28850     
28851     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28852     {
28853         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28854             tagSize,
28855             dataOffset,
28856             values,
28857             i,
28858             str,
28859             c;
28860     
28861         if (!tagType) {
28862             Roo.log('Invalid Exif data: Invalid tag type.');
28863             return;
28864         }
28865         
28866         tagSize = tagType.size * length;
28867         // Determine if the value is contained in the dataOffset bytes,
28868         // or if the value at the dataOffset is a pointer to the actual data:
28869         dataOffset = tagSize > 4 ?
28870                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28871         if (dataOffset + tagSize > dataView.byteLength) {
28872             Roo.log('Invalid Exif data: Invalid data offset.');
28873             return;
28874         }
28875         if (length === 1) {
28876             return tagType.getValue(dataView, dataOffset, littleEndian);
28877         }
28878         values = [];
28879         for (i = 0; i < length; i += 1) {
28880             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28881         }
28882         
28883         if (tagType.ascii) {
28884             str = '';
28885             // Concatenate the chars:
28886             for (i = 0; i < values.length; i += 1) {
28887                 c = values[i];
28888                 // Ignore the terminating NULL byte(s):
28889                 if (c === '\u0000') {
28890                     break;
28891                 }
28892                 str += c;
28893             }
28894             return str;
28895         }
28896         return values;
28897     }
28898     
28899 });
28900
28901 Roo.apply(Roo.bootstrap.UploadCropbox, {
28902     tags : {
28903         'Orientation': 0x0112
28904     },
28905     
28906     Orientation: {
28907             1: 0, //'top-left',
28908 //            2: 'top-right',
28909             3: 180, //'bottom-right',
28910 //            4: 'bottom-left',
28911 //            5: 'left-top',
28912             6: 90, //'right-top',
28913 //            7: 'right-bottom',
28914             8: 270 //'left-bottom'
28915     },
28916     
28917     exifTagTypes : {
28918         // byte, 8-bit unsigned int:
28919         1: {
28920             getValue: function (dataView, dataOffset) {
28921                 return dataView.getUint8(dataOffset);
28922             },
28923             size: 1
28924         },
28925         // ascii, 8-bit byte:
28926         2: {
28927             getValue: function (dataView, dataOffset) {
28928                 return String.fromCharCode(dataView.getUint8(dataOffset));
28929             },
28930             size: 1,
28931             ascii: true
28932         },
28933         // short, 16 bit int:
28934         3: {
28935             getValue: function (dataView, dataOffset, littleEndian) {
28936                 return dataView.getUint16(dataOffset, littleEndian);
28937             },
28938             size: 2
28939         },
28940         // long, 32 bit int:
28941         4: {
28942             getValue: function (dataView, dataOffset, littleEndian) {
28943                 return dataView.getUint32(dataOffset, littleEndian);
28944             },
28945             size: 4
28946         },
28947         // rational = two long values, first is numerator, second is denominator:
28948         5: {
28949             getValue: function (dataView, dataOffset, littleEndian) {
28950                 return dataView.getUint32(dataOffset, littleEndian) /
28951                     dataView.getUint32(dataOffset + 4, littleEndian);
28952             },
28953             size: 8
28954         },
28955         // slong, 32 bit signed int:
28956         9: {
28957             getValue: function (dataView, dataOffset, littleEndian) {
28958                 return dataView.getInt32(dataOffset, littleEndian);
28959             },
28960             size: 4
28961         },
28962         // srational, two slongs, first is numerator, second is denominator:
28963         10: {
28964             getValue: function (dataView, dataOffset, littleEndian) {
28965                 return dataView.getInt32(dataOffset, littleEndian) /
28966                     dataView.getInt32(dataOffset + 4, littleEndian);
28967             },
28968             size: 8
28969         }
28970     },
28971     
28972     footer : {
28973         STANDARD : [
28974             {
28975                 tag : 'div',
28976                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28977                 action : 'rotate-left',
28978                 cn : [
28979                     {
28980                         tag : 'button',
28981                         cls : 'btn btn-default',
28982                         html : '<i class="fa fa-undo"></i>'
28983                     }
28984                 ]
28985             },
28986             {
28987                 tag : 'div',
28988                 cls : 'btn-group roo-upload-cropbox-picture',
28989                 action : 'picture',
28990                 cn : [
28991                     {
28992                         tag : 'button',
28993                         cls : 'btn btn-default',
28994                         html : '<i class="fa fa-picture-o"></i>'
28995                     }
28996                 ]
28997             },
28998             {
28999                 tag : 'div',
29000                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29001                 action : 'rotate-right',
29002                 cn : [
29003                     {
29004                         tag : 'button',
29005                         cls : 'btn btn-default',
29006                         html : '<i class="fa fa-repeat"></i>'
29007                     }
29008                 ]
29009             }
29010         ],
29011         DOCUMENT : [
29012             {
29013                 tag : 'div',
29014                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29015                 action : 'rotate-left',
29016                 cn : [
29017                     {
29018                         tag : 'button',
29019                         cls : 'btn btn-default',
29020                         html : '<i class="fa fa-undo"></i>'
29021                     }
29022                 ]
29023             },
29024             {
29025                 tag : 'div',
29026                 cls : 'btn-group roo-upload-cropbox-download',
29027                 action : 'download',
29028                 cn : [
29029                     {
29030                         tag : 'button',
29031                         cls : 'btn btn-default',
29032                         html : '<i class="fa fa-download"></i>'
29033                     }
29034                 ]
29035             },
29036             {
29037                 tag : 'div',
29038                 cls : 'btn-group roo-upload-cropbox-crop',
29039                 action : 'crop',
29040                 cn : [
29041                     {
29042                         tag : 'button',
29043                         cls : 'btn btn-default',
29044                         html : '<i class="fa fa-crop"></i>'
29045                     }
29046                 ]
29047             },
29048             {
29049                 tag : 'div',
29050                 cls : 'btn-group roo-upload-cropbox-trash',
29051                 action : 'trash',
29052                 cn : [
29053                     {
29054                         tag : 'button',
29055                         cls : 'btn btn-default',
29056                         html : '<i class="fa fa-trash"></i>'
29057                     }
29058                 ]
29059             },
29060             {
29061                 tag : 'div',
29062                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29063                 action : 'rotate-right',
29064                 cn : [
29065                     {
29066                         tag : 'button',
29067                         cls : 'btn btn-default',
29068                         html : '<i class="fa fa-repeat"></i>'
29069                     }
29070                 ]
29071             }
29072         ],
29073         ROTATOR : [
29074             {
29075                 tag : 'div',
29076                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29077                 action : 'rotate-left',
29078                 cn : [
29079                     {
29080                         tag : 'button',
29081                         cls : 'btn btn-default',
29082                         html : '<i class="fa fa-undo"></i>'
29083                     }
29084                 ]
29085             },
29086             {
29087                 tag : 'div',
29088                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29089                 action : 'rotate-right',
29090                 cn : [
29091                     {
29092                         tag : 'button',
29093                         cls : 'btn btn-default',
29094                         html : '<i class="fa fa-repeat"></i>'
29095                     }
29096                 ]
29097             }
29098         ]
29099     }
29100 });
29101
29102 /*
29103 * Licence: LGPL
29104 */
29105
29106 /**
29107  * @class Roo.bootstrap.DocumentManager
29108  * @extends Roo.bootstrap.Component
29109  * Bootstrap DocumentManager class
29110  * @cfg {String} paramName default 'imageUpload'
29111  * @cfg {String} toolTipName default 'filename'
29112  * @cfg {String} method default POST
29113  * @cfg {String} url action url
29114  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
29115  * @cfg {Boolean} multiple multiple upload default true
29116  * @cfg {Number} thumbSize default 300
29117  * @cfg {String} fieldLabel
29118  * @cfg {Number} labelWidth default 4
29119  * @cfg {String} labelAlign (left|top) default left
29120  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
29121 * @cfg {Number} labellg set the width of label (1-12)
29122  * @cfg {Number} labelmd set the width of label (1-12)
29123  * @cfg {Number} labelsm set the width of label (1-12)
29124  * @cfg {Number} labelxs set the width of label (1-12)
29125  * 
29126  * @constructor
29127  * Create a new DocumentManager
29128  * @param {Object} config The config object
29129  */
29130
29131 Roo.bootstrap.DocumentManager = function(config){
29132     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
29133     
29134     this.files = [];
29135     this.delegates = [];
29136     
29137     this.addEvents({
29138         /**
29139          * @event initial
29140          * Fire when initial the DocumentManager
29141          * @param {Roo.bootstrap.DocumentManager} this
29142          */
29143         "initial" : true,
29144         /**
29145          * @event inspect
29146          * inspect selected file
29147          * @param {Roo.bootstrap.DocumentManager} this
29148          * @param {File} file
29149          */
29150         "inspect" : true,
29151         /**
29152          * @event exception
29153          * Fire when xhr load exception
29154          * @param {Roo.bootstrap.DocumentManager} this
29155          * @param {XMLHttpRequest} xhr
29156          */
29157         "exception" : true,
29158         /**
29159          * @event afterupload
29160          * Fire when xhr load exception
29161          * @param {Roo.bootstrap.DocumentManager} this
29162          * @param {XMLHttpRequest} xhr
29163          */
29164         "afterupload" : true,
29165         /**
29166          * @event prepare
29167          * prepare the form data
29168          * @param {Roo.bootstrap.DocumentManager} this
29169          * @param {Object} formData
29170          */
29171         "prepare" : true,
29172         /**
29173          * @event remove
29174          * Fire when remove the file
29175          * @param {Roo.bootstrap.DocumentManager} this
29176          * @param {Object} file
29177          */
29178         "remove" : true,
29179         /**
29180          * @event refresh
29181          * Fire after refresh the file
29182          * @param {Roo.bootstrap.DocumentManager} this
29183          */
29184         "refresh" : true,
29185         /**
29186          * @event click
29187          * Fire after click the image
29188          * @param {Roo.bootstrap.DocumentManager} this
29189          * @param {Object} file
29190          */
29191         "click" : true,
29192         /**
29193          * @event edit
29194          * Fire when upload a image and editable set to true
29195          * @param {Roo.bootstrap.DocumentManager} this
29196          * @param {Object} file
29197          */
29198         "edit" : true,
29199         /**
29200          * @event beforeselectfile
29201          * Fire before select file
29202          * @param {Roo.bootstrap.DocumentManager} this
29203          */
29204         "beforeselectfile" : true,
29205         /**
29206          * @event process
29207          * Fire before process file
29208          * @param {Roo.bootstrap.DocumentManager} this
29209          * @param {Object} file
29210          */
29211         "process" : true,
29212         /**
29213          * @event previewrendered
29214          * Fire when preview rendered
29215          * @param {Roo.bootstrap.DocumentManager} this
29216          * @param {Object} file
29217          */
29218         "previewrendered" : true,
29219         /**
29220          */
29221         "previewResize" : true
29222         
29223     });
29224 };
29225
29226 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
29227     
29228     boxes : 0,
29229     inputName : '',
29230     thumbSize : 300,
29231     multiple : true,
29232     files : false,
29233     method : 'POST',
29234     url : '',
29235     paramName : 'imageUpload',
29236     toolTipName : 'filename',
29237     fieldLabel : '',
29238     labelWidth : 4,
29239     labelAlign : 'left',
29240     editable : true,
29241     delegates : false,
29242     xhr : false, 
29243     
29244     labellg : 0,
29245     labelmd : 0,
29246     labelsm : 0,
29247     labelxs : 0,
29248     
29249     getAutoCreate : function()
29250     {   
29251         var managerWidget = {
29252             tag : 'div',
29253             cls : 'roo-document-manager',
29254             cn : [
29255                 {
29256                     tag : 'input',
29257                     cls : 'roo-document-manager-selector',
29258                     type : 'file'
29259                 },
29260                 {
29261                     tag : 'div',
29262                     cls : 'roo-document-manager-uploader',
29263                     cn : [
29264                         {
29265                             tag : 'div',
29266                             cls : 'roo-document-manager-upload-btn',
29267                             html : '<i class="fa fa-plus"></i>'
29268                         }
29269                     ]
29270                     
29271                 }
29272             ]
29273         };
29274         
29275         var content = [
29276             {
29277                 tag : 'div',
29278                 cls : 'column col-md-12',
29279                 cn : managerWidget
29280             }
29281         ];
29282         
29283         if(this.fieldLabel.length){
29284             
29285             content = [
29286                 {
29287                     tag : 'div',
29288                     cls : 'column col-md-12',
29289                     html : this.fieldLabel
29290                 },
29291                 {
29292                     tag : 'div',
29293                     cls : 'column col-md-12',
29294                     cn : managerWidget
29295                 }
29296             ];
29297
29298             if(this.labelAlign == 'left'){
29299                 content = [
29300                     {
29301                         tag : 'div',
29302                         cls : 'column',
29303                         html : this.fieldLabel
29304                     },
29305                     {
29306                         tag : 'div',
29307                         cls : 'column',
29308                         cn : managerWidget
29309                     }
29310                 ];
29311                 
29312                 if(this.labelWidth > 12){
29313                     content[0].style = "width: " + this.labelWidth + 'px';
29314                 }
29315
29316                 if(this.labelWidth < 13 && this.labelmd == 0){
29317                     this.labelmd = this.labelWidth;
29318                 }
29319
29320                 if(this.labellg > 0){
29321                     content[0].cls += ' col-lg-' + this.labellg;
29322                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29323                 }
29324
29325                 if(this.labelmd > 0){
29326                     content[0].cls += ' col-md-' + this.labelmd;
29327                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29328                 }
29329
29330                 if(this.labelsm > 0){
29331                     content[0].cls += ' col-sm-' + this.labelsm;
29332                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29333                 }
29334
29335                 if(this.labelxs > 0){
29336                     content[0].cls += ' col-xs-' + this.labelxs;
29337                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29338                 }
29339                 
29340             }
29341         }
29342         
29343         var cfg = {
29344             tag : 'div',
29345             cls : 'row clearfix',
29346             cn : content
29347         };
29348         
29349         return cfg;
29350         
29351     },
29352     
29353     initEvents : function()
29354     {
29355         this.managerEl = this.el.select('.roo-document-manager', true).first();
29356         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29357         
29358         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29359         this.selectorEl.hide();
29360         
29361         if(this.multiple){
29362             this.selectorEl.attr('multiple', 'multiple');
29363         }
29364         
29365         this.selectorEl.on('change', this.onFileSelected, this);
29366         
29367         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29368         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29369         
29370         this.uploader.on('click', this.onUploaderClick, this);
29371         
29372         this.renderProgressDialog();
29373         
29374         var _this = this;
29375         
29376         window.addEventListener("resize", function() { _this.refresh(); } );
29377         
29378         this.fireEvent('initial', this);
29379     },
29380     
29381     renderProgressDialog : function()
29382     {
29383         var _this = this;
29384         
29385         this.progressDialog = new Roo.bootstrap.Modal({
29386             cls : 'roo-document-manager-progress-dialog',
29387             allow_close : false,
29388             animate : false,
29389             title : '',
29390             buttons : [
29391                 {
29392                     name  :'cancel',
29393                     weight : 'danger',
29394                     html : 'Cancel'
29395                 }
29396             ], 
29397             listeners : { 
29398                 btnclick : function() {
29399                     _this.uploadCancel();
29400                     this.hide();
29401                 }
29402             }
29403         });
29404          
29405         this.progressDialog.render(Roo.get(document.body));
29406          
29407         this.progress = new Roo.bootstrap.Progress({
29408             cls : 'roo-document-manager-progress',
29409             active : true,
29410             striped : true
29411         });
29412         
29413         this.progress.render(this.progressDialog.getChildContainer());
29414         
29415         this.progressBar = new Roo.bootstrap.ProgressBar({
29416             cls : 'roo-document-manager-progress-bar',
29417             aria_valuenow : 0,
29418             aria_valuemin : 0,
29419             aria_valuemax : 12,
29420             panel : 'success'
29421         });
29422         
29423         this.progressBar.render(this.progress.getChildContainer());
29424     },
29425     
29426     onUploaderClick : function(e)
29427     {
29428         e.preventDefault();
29429      
29430         if(this.fireEvent('beforeselectfile', this) != false){
29431             this.selectorEl.dom.click();
29432         }
29433         
29434     },
29435     
29436     onFileSelected : function(e)
29437     {
29438         e.preventDefault();
29439         
29440         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29441             return;
29442         }
29443         
29444         Roo.each(this.selectorEl.dom.files, function(file){
29445             if(this.fireEvent('inspect', this, file) != false){
29446                 this.files.push(file);
29447             }
29448         }, this);
29449         
29450         this.queue();
29451         
29452     },
29453     
29454     queue : function()
29455     {
29456         this.selectorEl.dom.value = '';
29457         
29458         if(!this.files || !this.files.length){
29459             return;
29460         }
29461         
29462         if(this.boxes > 0 && this.files.length > this.boxes){
29463             this.files = this.files.slice(0, this.boxes);
29464         }
29465         
29466         this.uploader.show();
29467         
29468         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29469             this.uploader.hide();
29470         }
29471         
29472         var _this = this;
29473         
29474         var files = [];
29475         
29476         var docs = [];
29477         
29478         Roo.each(this.files, function(file){
29479             
29480             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29481                 var f = this.renderPreview(file);
29482                 files.push(f);
29483                 return;
29484             }
29485             
29486             if(file.type.indexOf('image') != -1){
29487                 this.delegates.push(
29488                     (function(){
29489                         _this.process(file);
29490                     }).createDelegate(this)
29491                 );
29492         
29493                 return;
29494             }
29495             
29496             docs.push(
29497                 (function(){
29498                     _this.process(file);
29499                 }).createDelegate(this)
29500             );
29501             
29502         }, this);
29503         
29504         this.files = files;
29505         
29506         this.delegates = this.delegates.concat(docs);
29507         
29508         if(!this.delegates.length){
29509             this.refresh();
29510             return;
29511         }
29512         
29513         this.progressBar.aria_valuemax = this.delegates.length;
29514         
29515         this.arrange();
29516         
29517         return;
29518     },
29519     
29520     arrange : function()
29521     {
29522         if(!this.delegates.length){
29523             this.progressDialog.hide();
29524             this.refresh();
29525             return;
29526         }
29527         
29528         var delegate = this.delegates.shift();
29529         
29530         this.progressDialog.show();
29531         
29532         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29533         
29534         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29535         
29536         delegate();
29537     },
29538     
29539     refresh : function()
29540     {
29541         this.uploader.show();
29542         
29543         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29544             this.uploader.hide();
29545         }
29546         
29547         Roo.isTouch ? this.closable(false) : this.closable(true);
29548         
29549         this.fireEvent('refresh', this);
29550     },
29551     
29552     onRemove : function(e, el, o)
29553     {
29554         e.preventDefault();
29555         
29556         this.fireEvent('remove', this, o);
29557         
29558     },
29559     
29560     remove : function(o)
29561     {
29562         var files = [];
29563         
29564         Roo.each(this.files, function(file){
29565             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29566                 files.push(file);
29567                 return;
29568             }
29569
29570             o.target.remove();
29571
29572         }, this);
29573         
29574         this.files = files;
29575         
29576         this.refresh();
29577     },
29578     
29579     clear : function()
29580     {
29581         Roo.each(this.files, function(file){
29582             if(!file.target){
29583                 return;
29584             }
29585             
29586             file.target.remove();
29587
29588         }, this);
29589         
29590         this.files = [];
29591         
29592         this.refresh();
29593     },
29594     
29595     onClick : function(e, el, o)
29596     {
29597         e.preventDefault();
29598         
29599         this.fireEvent('click', this, o);
29600         
29601     },
29602     
29603     closable : function(closable)
29604     {
29605         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29606             
29607             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29608             
29609             if(closable){
29610                 el.show();
29611                 return;
29612             }
29613             
29614             el.hide();
29615             
29616         }, this);
29617     },
29618     
29619     xhrOnLoad : function(xhr)
29620     {
29621         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29622             el.remove();
29623         }, this);
29624         
29625         if (xhr.readyState !== 4) {
29626             this.arrange();
29627             this.fireEvent('exception', this, xhr);
29628             return;
29629         }
29630
29631         var response = Roo.decode(xhr.responseText);
29632         
29633         if(!response.success){
29634             this.arrange();
29635             this.fireEvent('exception', this, xhr);
29636             return;
29637         }
29638         
29639         var file = this.renderPreview(response.data);
29640         
29641         this.files.push(file);
29642         
29643         this.arrange();
29644         
29645         this.fireEvent('afterupload', this, xhr);
29646         
29647     },
29648     
29649     xhrOnError : function(xhr)
29650     {
29651         Roo.log('xhr on error');
29652         
29653         var response = Roo.decode(xhr.responseText);
29654           
29655         Roo.log(response);
29656         
29657         this.arrange();
29658     },
29659     
29660     process : function(file)
29661     {
29662         if(this.fireEvent('process', this, file) !== false){
29663             if(this.editable && file.type.indexOf('image') != -1){
29664                 this.fireEvent('edit', this, file);
29665                 return;
29666             }
29667
29668             this.uploadStart(file, false);
29669
29670             return;
29671         }
29672         
29673     },
29674     
29675     uploadStart : function(file, crop)
29676     {
29677         this.xhr = new XMLHttpRequest();
29678         
29679         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29680             this.arrange();
29681             return;
29682         }
29683         
29684         file.xhr = this.xhr;
29685             
29686         this.managerEl.createChild({
29687             tag : 'div',
29688             cls : 'roo-document-manager-loading',
29689             cn : [
29690                 {
29691                     tag : 'div',
29692                     tooltip : file.name,
29693                     cls : 'roo-document-manager-thumb',
29694                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29695                 }
29696             ]
29697
29698         });
29699
29700         this.xhr.open(this.method, this.url, true);
29701         
29702         var headers = {
29703             "Accept": "application/json",
29704             "Cache-Control": "no-cache",
29705             "X-Requested-With": "XMLHttpRequest"
29706         };
29707         
29708         for (var headerName in headers) {
29709             var headerValue = headers[headerName];
29710             if (headerValue) {
29711                 this.xhr.setRequestHeader(headerName, headerValue);
29712             }
29713         }
29714         
29715         var _this = this;
29716         
29717         this.xhr.onload = function()
29718         {
29719             _this.xhrOnLoad(_this.xhr);
29720         }
29721         
29722         this.xhr.onerror = function()
29723         {
29724             _this.xhrOnError(_this.xhr);
29725         }
29726         
29727         var formData = new FormData();
29728
29729         formData.append('returnHTML', 'NO');
29730         
29731         if(crop){
29732             formData.append('crop', crop);
29733         }
29734         
29735         formData.append(this.paramName, file, file.name);
29736         
29737         var options = {
29738             file : file, 
29739             manually : false
29740         };
29741         
29742         if(this.fireEvent('prepare', this, formData, options) != false){
29743             
29744             if(options.manually){
29745                 return;
29746             }
29747             
29748             this.xhr.send(formData);
29749             return;
29750         };
29751         
29752         this.uploadCancel();
29753     },
29754     
29755     uploadCancel : function()
29756     {
29757         if (this.xhr) {
29758             this.xhr.abort();
29759         }
29760         
29761         this.delegates = [];
29762         
29763         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29764             el.remove();
29765         }, this);
29766         
29767         this.arrange();
29768     },
29769     
29770     renderPreview : function(file)
29771     {
29772         if(typeof(file.target) != 'undefined' && file.target){
29773             return file;
29774         }
29775         
29776         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29777         
29778         var previewEl = this.managerEl.createChild({
29779             tag : 'div',
29780             cls : 'roo-document-manager-preview',
29781             cn : [
29782                 {
29783                     tag : 'div',
29784                     tooltip : file[this.toolTipName],
29785                     cls : 'roo-document-manager-thumb',
29786                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29787                 },
29788                 {
29789                     tag : 'button',
29790                     cls : 'close',
29791                     html : '<i class="fa fa-times-circle"></i>'
29792                 }
29793             ]
29794         });
29795
29796         var close = previewEl.select('button.close', true).first();
29797
29798         close.on('click', this.onRemove, this, file);
29799
29800         file.target = previewEl;
29801
29802         var image = previewEl.select('img', true).first();
29803         
29804         var _this = this;
29805         
29806         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29807         
29808         image.on('click', this.onClick, this, file);
29809         
29810         this.fireEvent('previewrendered', this, file);
29811         
29812         return file;
29813         
29814     },
29815     
29816     onPreviewLoad : function(file, image)
29817     {
29818         if(typeof(file.target) == 'undefined' || !file.target){
29819             return;
29820         }
29821         
29822         var width = image.dom.naturalWidth || image.dom.width;
29823         var height = image.dom.naturalHeight || image.dom.height;
29824         
29825         if(!this.previewResize) {
29826             return;
29827         }
29828         
29829         if(width > height){
29830             file.target.addClass('wide');
29831             return;
29832         }
29833         
29834         file.target.addClass('tall');
29835         return;
29836         
29837     },
29838     
29839     uploadFromSource : function(file, crop)
29840     {
29841         this.xhr = new XMLHttpRequest();
29842         
29843         this.managerEl.createChild({
29844             tag : 'div',
29845             cls : 'roo-document-manager-loading',
29846             cn : [
29847                 {
29848                     tag : 'div',
29849                     tooltip : file.name,
29850                     cls : 'roo-document-manager-thumb',
29851                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29852                 }
29853             ]
29854
29855         });
29856
29857         this.xhr.open(this.method, this.url, true);
29858         
29859         var headers = {
29860             "Accept": "application/json",
29861             "Cache-Control": "no-cache",
29862             "X-Requested-With": "XMLHttpRequest"
29863         };
29864         
29865         for (var headerName in headers) {
29866             var headerValue = headers[headerName];
29867             if (headerValue) {
29868                 this.xhr.setRequestHeader(headerName, headerValue);
29869             }
29870         }
29871         
29872         var _this = this;
29873         
29874         this.xhr.onload = function()
29875         {
29876             _this.xhrOnLoad(_this.xhr);
29877         }
29878         
29879         this.xhr.onerror = function()
29880         {
29881             _this.xhrOnError(_this.xhr);
29882         }
29883         
29884         var formData = new FormData();
29885
29886         formData.append('returnHTML', 'NO');
29887         
29888         formData.append('crop', crop);
29889         
29890         if(typeof(file.filename) != 'undefined'){
29891             formData.append('filename', file.filename);
29892         }
29893         
29894         if(typeof(file.mimetype) != 'undefined'){
29895             formData.append('mimetype', file.mimetype);
29896         }
29897         
29898         Roo.log(formData);
29899         
29900         if(this.fireEvent('prepare', this, formData) != false){
29901             this.xhr.send(formData);
29902         };
29903     }
29904 });
29905
29906 /*
29907 * Licence: LGPL
29908 */
29909
29910 /**
29911  * @class Roo.bootstrap.DocumentViewer
29912  * @extends Roo.bootstrap.Component
29913  * Bootstrap DocumentViewer class
29914  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29915  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29916  * 
29917  * @constructor
29918  * Create a new DocumentViewer
29919  * @param {Object} config The config object
29920  */
29921
29922 Roo.bootstrap.DocumentViewer = function(config){
29923     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29924     
29925     this.addEvents({
29926         /**
29927          * @event initial
29928          * Fire after initEvent
29929          * @param {Roo.bootstrap.DocumentViewer} this
29930          */
29931         "initial" : true,
29932         /**
29933          * @event click
29934          * Fire after click
29935          * @param {Roo.bootstrap.DocumentViewer} this
29936          */
29937         "click" : true,
29938         /**
29939          * @event download
29940          * Fire after download button
29941          * @param {Roo.bootstrap.DocumentViewer} this
29942          */
29943         "download" : true,
29944         /**
29945          * @event trash
29946          * Fire after trash button
29947          * @param {Roo.bootstrap.DocumentViewer} this
29948          */
29949         "trash" : true
29950         
29951     });
29952 };
29953
29954 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29955     
29956     showDownload : true,
29957     
29958     showTrash : true,
29959     
29960     getAutoCreate : function()
29961     {
29962         var cfg = {
29963             tag : 'div',
29964             cls : 'roo-document-viewer',
29965             cn : [
29966                 {
29967                     tag : 'div',
29968                     cls : 'roo-document-viewer-body',
29969                     cn : [
29970                         {
29971                             tag : 'div',
29972                             cls : 'roo-document-viewer-thumb',
29973                             cn : [
29974                                 {
29975                                     tag : 'img',
29976                                     cls : 'roo-document-viewer-image'
29977                                 }
29978                             ]
29979                         }
29980                     ]
29981                 },
29982                 {
29983                     tag : 'div',
29984                     cls : 'roo-document-viewer-footer',
29985                     cn : {
29986                         tag : 'div',
29987                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29988                         cn : [
29989                             {
29990                                 tag : 'div',
29991                                 cls : 'btn-group roo-document-viewer-download',
29992                                 cn : [
29993                                     {
29994                                         tag : 'button',
29995                                         cls : 'btn btn-default',
29996                                         html : '<i class="fa fa-download"></i>'
29997                                     }
29998                                 ]
29999                             },
30000                             {
30001                                 tag : 'div',
30002                                 cls : 'btn-group roo-document-viewer-trash',
30003                                 cn : [
30004                                     {
30005                                         tag : 'button',
30006                                         cls : 'btn btn-default',
30007                                         html : '<i class="fa fa-trash"></i>'
30008                                     }
30009                                 ]
30010                             }
30011                         ]
30012                     }
30013                 }
30014             ]
30015         };
30016         
30017         return cfg;
30018     },
30019     
30020     initEvents : function()
30021     {
30022         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
30023         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
30024         
30025         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
30026         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
30027         
30028         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
30029         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
30030         
30031         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
30032         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
30033         
30034         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
30035         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
30036         
30037         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
30038         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
30039         
30040         this.bodyEl.on('click', this.onClick, this);
30041         this.downloadBtn.on('click', this.onDownload, this);
30042         this.trashBtn.on('click', this.onTrash, this);
30043         
30044         this.downloadBtn.hide();
30045         this.trashBtn.hide();
30046         
30047         if(this.showDownload){
30048             this.downloadBtn.show();
30049         }
30050         
30051         if(this.showTrash){
30052             this.trashBtn.show();
30053         }
30054         
30055         if(!this.showDownload && !this.showTrash) {
30056             this.footerEl.hide();
30057         }
30058         
30059     },
30060     
30061     initial : function()
30062     {
30063         this.fireEvent('initial', this);
30064         
30065     },
30066     
30067     onClick : function(e)
30068     {
30069         e.preventDefault();
30070         
30071         this.fireEvent('click', this);
30072     },
30073     
30074     onDownload : function(e)
30075     {
30076         e.preventDefault();
30077         
30078         this.fireEvent('download', this);
30079     },
30080     
30081     onTrash : function(e)
30082     {
30083         e.preventDefault();
30084         
30085         this.fireEvent('trash', this);
30086     }
30087     
30088 });
30089 /*
30090  * - LGPL
30091  *
30092  * nav progress bar
30093  * 
30094  */
30095
30096 /**
30097  * @class Roo.bootstrap.NavProgressBar
30098  * @extends Roo.bootstrap.Component
30099  * Bootstrap NavProgressBar class
30100  * 
30101  * @constructor
30102  * Create a new nav progress bar
30103  * @param {Object} config The config object
30104  */
30105
30106 Roo.bootstrap.NavProgressBar = function(config){
30107     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
30108
30109     this.bullets = this.bullets || [];
30110    
30111 //    Roo.bootstrap.NavProgressBar.register(this);
30112      this.addEvents({
30113         /**
30114              * @event changed
30115              * Fires when the active item changes
30116              * @param {Roo.bootstrap.NavProgressBar} this
30117              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
30118              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
30119          */
30120         'changed': true
30121      });
30122     
30123 };
30124
30125 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
30126     
30127     bullets : [],
30128     barItems : [],
30129     
30130     getAutoCreate : function()
30131     {
30132         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
30133         
30134         cfg = {
30135             tag : 'div',
30136             cls : 'roo-navigation-bar-group',
30137             cn : [
30138                 {
30139                     tag : 'div',
30140                     cls : 'roo-navigation-top-bar'
30141                 },
30142                 {
30143                     tag : 'div',
30144                     cls : 'roo-navigation-bullets-bar',
30145                     cn : [
30146                         {
30147                             tag : 'ul',
30148                             cls : 'roo-navigation-bar'
30149                         }
30150                     ]
30151                 },
30152                 
30153                 {
30154                     tag : 'div',
30155                     cls : 'roo-navigation-bottom-bar'
30156                 }
30157             ]
30158             
30159         };
30160         
30161         return cfg;
30162         
30163     },
30164     
30165     initEvents: function() 
30166     {
30167         
30168     },
30169     
30170     onRender : function(ct, position) 
30171     {
30172         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30173         
30174         if(this.bullets.length){
30175             Roo.each(this.bullets, function(b){
30176                this.addItem(b);
30177             }, this);
30178         }
30179         
30180         this.format();
30181         
30182     },
30183     
30184     addItem : function(cfg)
30185     {
30186         var item = new Roo.bootstrap.NavProgressItem(cfg);
30187         
30188         item.parentId = this.id;
30189         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
30190         
30191         if(cfg.html){
30192             var top = new Roo.bootstrap.Element({
30193                 tag : 'div',
30194                 cls : 'roo-navigation-bar-text'
30195             });
30196             
30197             var bottom = new Roo.bootstrap.Element({
30198                 tag : 'div',
30199                 cls : 'roo-navigation-bar-text'
30200             });
30201             
30202             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
30203             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
30204             
30205             var topText = new Roo.bootstrap.Element({
30206                 tag : 'span',
30207                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
30208             });
30209             
30210             var bottomText = new Roo.bootstrap.Element({
30211                 tag : 'span',
30212                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30213             });
30214             
30215             topText.onRender(top.el, null);
30216             bottomText.onRender(bottom.el, null);
30217             
30218             item.topEl = top;
30219             item.bottomEl = bottom;
30220         }
30221         
30222         this.barItems.push(item);
30223         
30224         return item;
30225     },
30226     
30227     getActive : function()
30228     {
30229         var active = false;
30230         
30231         Roo.each(this.barItems, function(v){
30232             
30233             if (!v.isActive()) {
30234                 return;
30235             }
30236             
30237             active = v;
30238             return false;
30239             
30240         });
30241         
30242         return active;
30243     },
30244     
30245     setActiveItem : function(item)
30246     {
30247         var prev = false;
30248         
30249         Roo.each(this.barItems, function(v){
30250             if (v.rid == item.rid) {
30251                 return ;
30252             }
30253             
30254             if (v.isActive()) {
30255                 v.setActive(false);
30256                 prev = v;
30257             }
30258         });
30259
30260         item.setActive(true);
30261         
30262         this.fireEvent('changed', this, item, prev);
30263     },
30264     
30265     getBarItem: function(rid)
30266     {
30267         var ret = false;
30268         
30269         Roo.each(this.barItems, function(e) {
30270             if (e.rid != rid) {
30271                 return;
30272             }
30273             
30274             ret =  e;
30275             return false;
30276         });
30277         
30278         return ret;
30279     },
30280     
30281     indexOfItem : function(item)
30282     {
30283         var index = false;
30284         
30285         Roo.each(this.barItems, function(v, i){
30286             
30287             if (v.rid != item.rid) {
30288                 return;
30289             }
30290             
30291             index = i;
30292             return false
30293         });
30294         
30295         return index;
30296     },
30297     
30298     setActiveNext : function()
30299     {
30300         var i = this.indexOfItem(this.getActive());
30301         
30302         if (i > this.barItems.length) {
30303             return;
30304         }
30305         
30306         this.setActiveItem(this.barItems[i+1]);
30307     },
30308     
30309     setActivePrev : function()
30310     {
30311         var i = this.indexOfItem(this.getActive());
30312         
30313         if (i  < 1) {
30314             return;
30315         }
30316         
30317         this.setActiveItem(this.barItems[i-1]);
30318     },
30319     
30320     format : function()
30321     {
30322         if(!this.barItems.length){
30323             return;
30324         }
30325      
30326         var width = 100 / this.barItems.length;
30327         
30328         Roo.each(this.barItems, function(i){
30329             i.el.setStyle('width', width + '%');
30330             i.topEl.el.setStyle('width', width + '%');
30331             i.bottomEl.el.setStyle('width', width + '%');
30332         }, this);
30333         
30334     }
30335     
30336 });
30337 /*
30338  * - LGPL
30339  *
30340  * Nav Progress Item
30341  * 
30342  */
30343
30344 /**
30345  * @class Roo.bootstrap.NavProgressItem
30346  * @extends Roo.bootstrap.Component
30347  * Bootstrap NavProgressItem class
30348  * @cfg {String} rid the reference id
30349  * @cfg {Boolean} active (true|false) Is item active default false
30350  * @cfg {Boolean} disabled (true|false) Is item active default false
30351  * @cfg {String} html
30352  * @cfg {String} position (top|bottom) text position default bottom
30353  * @cfg {String} icon show icon instead of number
30354  * 
30355  * @constructor
30356  * Create a new NavProgressItem
30357  * @param {Object} config The config object
30358  */
30359 Roo.bootstrap.NavProgressItem = function(config){
30360     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30361     this.addEvents({
30362         // raw events
30363         /**
30364          * @event click
30365          * The raw click event for the entire grid.
30366          * @param {Roo.bootstrap.NavProgressItem} this
30367          * @param {Roo.EventObject} e
30368          */
30369         "click" : true
30370     });
30371    
30372 };
30373
30374 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30375     
30376     rid : '',
30377     active : false,
30378     disabled : false,
30379     html : '',
30380     position : 'bottom',
30381     icon : false,
30382     
30383     getAutoCreate : function()
30384     {
30385         var iconCls = 'roo-navigation-bar-item-icon';
30386         
30387         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30388         
30389         var cfg = {
30390             tag: 'li',
30391             cls: 'roo-navigation-bar-item',
30392             cn : [
30393                 {
30394                     tag : 'i',
30395                     cls : iconCls
30396                 }
30397             ]
30398         };
30399         
30400         if(this.active){
30401             cfg.cls += ' active';
30402         }
30403         if(this.disabled){
30404             cfg.cls += ' disabled';
30405         }
30406         
30407         return cfg;
30408     },
30409     
30410     disable : function()
30411     {
30412         this.setDisabled(true);
30413     },
30414     
30415     enable : function()
30416     {
30417         this.setDisabled(false);
30418     },
30419     
30420     initEvents: function() 
30421     {
30422         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30423         
30424         this.iconEl.on('click', this.onClick, this);
30425     },
30426     
30427     onClick : function(e)
30428     {
30429         e.preventDefault();
30430         
30431         if(this.disabled){
30432             return;
30433         }
30434         
30435         if(this.fireEvent('click', this, e) === false){
30436             return;
30437         };
30438         
30439         this.parent().setActiveItem(this);
30440     },
30441     
30442     isActive: function () 
30443     {
30444         return this.active;
30445     },
30446     
30447     setActive : function(state)
30448     {
30449         if(this.active == state){
30450             return;
30451         }
30452         
30453         this.active = state;
30454         
30455         if (state) {
30456             this.el.addClass('active');
30457             return;
30458         }
30459         
30460         this.el.removeClass('active');
30461         
30462         return;
30463     },
30464     
30465     setDisabled : function(state)
30466     {
30467         if(this.disabled == state){
30468             return;
30469         }
30470         
30471         this.disabled = state;
30472         
30473         if (state) {
30474             this.el.addClass('disabled');
30475             return;
30476         }
30477         
30478         this.el.removeClass('disabled');
30479     },
30480     
30481     tooltipEl : function()
30482     {
30483         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30484     }
30485 });
30486  
30487
30488  /*
30489  * - LGPL
30490  *
30491  * FieldLabel
30492  * 
30493  */
30494
30495 /**
30496  * @class Roo.bootstrap.FieldLabel
30497  * @extends Roo.bootstrap.Component
30498  * Bootstrap FieldLabel class
30499  * @cfg {String} html contents of the element
30500  * @cfg {String} tag tag of the element default label
30501  * @cfg {String} cls class of the element
30502  * @cfg {String} target label target 
30503  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30504  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
30505  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
30506  * @cfg {String} iconTooltip default "This field is required"
30507  * @cfg {String} indicatorpos (left|right) default left
30508  * 
30509  * @constructor
30510  * Create a new FieldLabel
30511  * @param {Object} config The config object
30512  */
30513
30514 Roo.bootstrap.FieldLabel = function(config){
30515     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30516     
30517     this.addEvents({
30518             /**
30519              * @event invalid
30520              * Fires after the field has been marked as invalid.
30521              * @param {Roo.form.FieldLabel} this
30522              * @param {String} msg The validation message
30523              */
30524             invalid : true,
30525             /**
30526              * @event valid
30527              * Fires after the field has been validated with no errors.
30528              * @param {Roo.form.FieldLabel} this
30529              */
30530             valid : true
30531         });
30532 };
30533
30534 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30535     
30536     tag: 'label',
30537     cls: '',
30538     html: '',
30539     target: '',
30540     allowBlank : true,
30541     invalidClass : 'has-warning',
30542     validClass : 'has-success',
30543     iconTooltip : 'This field is required',
30544     indicatorpos : 'left',
30545     
30546     getAutoCreate : function(){
30547         
30548         var cls = "";
30549         if (!this.allowBlank) {
30550             cls  = "visible";
30551         }
30552         
30553         var cfg = {
30554             tag : this.tag,
30555             cls : 'roo-bootstrap-field-label ' + this.cls,
30556             for : this.target,
30557             cn : [
30558                 {
30559                     tag : 'i',
30560                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30561                     tooltip : this.iconTooltip
30562                 },
30563                 {
30564                     tag : 'span',
30565                     html : this.html
30566                 }
30567             ] 
30568         };
30569         
30570         if(this.indicatorpos == 'right'){
30571             var cfg = {
30572                 tag : this.tag,
30573                 cls : 'roo-bootstrap-field-label ' + this.cls,
30574                 for : this.target,
30575                 cn : [
30576                     {
30577                         tag : 'span',
30578                         html : this.html
30579                     },
30580                     {
30581                         tag : 'i',
30582                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30583                         tooltip : this.iconTooltip
30584                     }
30585                 ] 
30586             };
30587         }
30588         
30589         return cfg;
30590     },
30591     
30592     initEvents: function() 
30593     {
30594         Roo.bootstrap.Element.superclass.initEvents.call(this);
30595         
30596         this.indicator = this.indicatorEl();
30597         
30598         if(this.indicator){
30599             this.indicator.removeClass('visible');
30600             this.indicator.addClass('invisible');
30601         }
30602         
30603         Roo.bootstrap.FieldLabel.register(this);
30604     },
30605     
30606     indicatorEl : function()
30607     {
30608         var indicator = this.el.select('i.roo-required-indicator',true).first();
30609         
30610         if(!indicator){
30611             return false;
30612         }
30613         
30614         return indicator;
30615         
30616     },
30617     
30618     /**
30619      * Mark this field as valid
30620      */
30621     markValid : function()
30622     {
30623         if(this.indicator){
30624             this.indicator.removeClass('visible');
30625             this.indicator.addClass('invisible');
30626         }
30627         if (Roo.bootstrap.version == 3) {
30628             this.el.removeClass(this.invalidClass);
30629             this.el.addClass(this.validClass);
30630         } else {
30631             this.el.removeClass('is-invalid');
30632             this.el.addClass('is-valid');
30633         }
30634         
30635         
30636         this.fireEvent('valid', this);
30637     },
30638     
30639     /**
30640      * Mark this field as invalid
30641      * @param {String} msg The validation message
30642      */
30643     markInvalid : function(msg)
30644     {
30645         if(this.indicator){
30646             this.indicator.removeClass('invisible');
30647             this.indicator.addClass('visible');
30648         }
30649           if (Roo.bootstrap.version == 3) {
30650             this.el.removeClass(this.validClass);
30651             this.el.addClass(this.invalidClass);
30652         } else {
30653             this.el.removeClass('is-valid');
30654             this.el.addClass('is-invalid');
30655         }
30656         
30657         
30658         this.fireEvent('invalid', this, msg);
30659     }
30660     
30661    
30662 });
30663
30664 Roo.apply(Roo.bootstrap.FieldLabel, {
30665     
30666     groups: {},
30667     
30668      /**
30669     * register a FieldLabel Group
30670     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30671     */
30672     register : function(label)
30673     {
30674         if(this.groups.hasOwnProperty(label.target)){
30675             return;
30676         }
30677      
30678         this.groups[label.target] = label;
30679         
30680     },
30681     /**
30682     * fetch a FieldLabel Group based on the target
30683     * @param {string} target
30684     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30685     */
30686     get: function(target) {
30687         if (typeof(this.groups[target]) == 'undefined') {
30688             return false;
30689         }
30690         
30691         return this.groups[target] ;
30692     }
30693 });
30694
30695  
30696
30697  /*
30698  * - LGPL
30699  *
30700  * page DateSplitField.
30701  * 
30702  */
30703
30704
30705 /**
30706  * @class Roo.bootstrap.DateSplitField
30707  * @extends Roo.bootstrap.Component
30708  * Bootstrap DateSplitField class
30709  * @cfg {string} fieldLabel - the label associated
30710  * @cfg {Number} labelWidth set the width of label (0-12)
30711  * @cfg {String} labelAlign (top|left)
30712  * @cfg {Boolean} dayAllowBlank (true|false) default false
30713  * @cfg {Boolean} monthAllowBlank (true|false) default false
30714  * @cfg {Boolean} yearAllowBlank (true|false) default false
30715  * @cfg {string} dayPlaceholder 
30716  * @cfg {string} monthPlaceholder
30717  * @cfg {string} yearPlaceholder
30718  * @cfg {string} dayFormat default 'd'
30719  * @cfg {string} monthFormat default 'm'
30720  * @cfg {string} yearFormat default 'Y'
30721  * @cfg {Number} labellg set the width of label (1-12)
30722  * @cfg {Number} labelmd set the width of label (1-12)
30723  * @cfg {Number} labelsm set the width of label (1-12)
30724  * @cfg {Number} labelxs set the width of label (1-12)
30725
30726  *     
30727  * @constructor
30728  * Create a new DateSplitField
30729  * @param {Object} config The config object
30730  */
30731
30732 Roo.bootstrap.DateSplitField = function(config){
30733     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30734     
30735     this.addEvents({
30736         // raw events
30737          /**
30738          * @event years
30739          * getting the data of years
30740          * @param {Roo.bootstrap.DateSplitField} this
30741          * @param {Object} years
30742          */
30743         "years" : true,
30744         /**
30745          * @event days
30746          * getting the data of days
30747          * @param {Roo.bootstrap.DateSplitField} this
30748          * @param {Object} days
30749          */
30750         "days" : true,
30751         /**
30752          * @event invalid
30753          * Fires after the field has been marked as invalid.
30754          * @param {Roo.form.Field} this
30755          * @param {String} msg The validation message
30756          */
30757         invalid : true,
30758        /**
30759          * @event valid
30760          * Fires after the field has been validated with no errors.
30761          * @param {Roo.form.Field} this
30762          */
30763         valid : true
30764     });
30765 };
30766
30767 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30768     
30769     fieldLabel : '',
30770     labelAlign : 'top',
30771     labelWidth : 3,
30772     dayAllowBlank : false,
30773     monthAllowBlank : false,
30774     yearAllowBlank : false,
30775     dayPlaceholder : '',
30776     monthPlaceholder : '',
30777     yearPlaceholder : '',
30778     dayFormat : 'd',
30779     monthFormat : 'm',
30780     yearFormat : 'Y',
30781     isFormField : true,
30782     labellg : 0,
30783     labelmd : 0,
30784     labelsm : 0,
30785     labelxs : 0,
30786     
30787     getAutoCreate : function()
30788     {
30789         var cfg = {
30790             tag : 'div',
30791             cls : 'row roo-date-split-field-group',
30792             cn : [
30793                 {
30794                     tag : 'input',
30795                     type : 'hidden',
30796                     cls : 'form-hidden-field roo-date-split-field-group-value',
30797                     name : this.name
30798                 }
30799             ]
30800         };
30801         
30802         var labelCls = 'col-md-12';
30803         var contentCls = 'col-md-4';
30804         
30805         if(this.fieldLabel){
30806             
30807             var label = {
30808                 tag : 'div',
30809                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30810                 cn : [
30811                     {
30812                         tag : 'label',
30813                         html : this.fieldLabel
30814                     }
30815                 ]
30816             };
30817             
30818             if(this.labelAlign == 'left'){
30819             
30820                 if(this.labelWidth > 12){
30821                     label.style = "width: " + this.labelWidth + 'px';
30822                 }
30823
30824                 if(this.labelWidth < 13 && this.labelmd == 0){
30825                     this.labelmd = this.labelWidth;
30826                 }
30827
30828                 if(this.labellg > 0){
30829                     labelCls = ' col-lg-' + this.labellg;
30830                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30831                 }
30832
30833                 if(this.labelmd > 0){
30834                     labelCls = ' col-md-' + this.labelmd;
30835                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30836                 }
30837
30838                 if(this.labelsm > 0){
30839                     labelCls = ' col-sm-' + this.labelsm;
30840                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30841                 }
30842
30843                 if(this.labelxs > 0){
30844                     labelCls = ' col-xs-' + this.labelxs;
30845                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30846                 }
30847             }
30848             
30849             label.cls += ' ' + labelCls;
30850             
30851             cfg.cn.push(label);
30852         }
30853         
30854         Roo.each(['day', 'month', 'year'], function(t){
30855             cfg.cn.push({
30856                 tag : 'div',
30857                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30858             });
30859         }, this);
30860         
30861         return cfg;
30862     },
30863     
30864     inputEl: function ()
30865     {
30866         return this.el.select('.roo-date-split-field-group-value', true).first();
30867     },
30868     
30869     onRender : function(ct, position) 
30870     {
30871         var _this = this;
30872         
30873         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30874         
30875         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30876         
30877         this.dayField = new Roo.bootstrap.ComboBox({
30878             allowBlank : this.dayAllowBlank,
30879             alwaysQuery : true,
30880             displayField : 'value',
30881             editable : false,
30882             fieldLabel : '',
30883             forceSelection : true,
30884             mode : 'local',
30885             placeholder : this.dayPlaceholder,
30886             selectOnFocus : true,
30887             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30888             triggerAction : 'all',
30889             typeAhead : true,
30890             valueField : 'value',
30891             store : new Roo.data.SimpleStore({
30892                 data : (function() {    
30893                     var days = [];
30894                     _this.fireEvent('days', _this, days);
30895                     return days;
30896                 })(),
30897                 fields : [ 'value' ]
30898             }),
30899             listeners : {
30900                 select : function (_self, record, index)
30901                 {
30902                     _this.setValue(_this.getValue());
30903                 }
30904             }
30905         });
30906
30907         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30908         
30909         this.monthField = new Roo.bootstrap.MonthField({
30910             after : '<i class=\"fa fa-calendar\"></i>',
30911             allowBlank : this.monthAllowBlank,
30912             placeholder : this.monthPlaceholder,
30913             readOnly : true,
30914             listeners : {
30915                 render : function (_self)
30916                 {
30917                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30918                         e.preventDefault();
30919                         _self.focus();
30920                     });
30921                 },
30922                 select : function (_self, oldvalue, newvalue)
30923                 {
30924                     _this.setValue(_this.getValue());
30925                 }
30926             }
30927         });
30928         
30929         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30930         
30931         this.yearField = new Roo.bootstrap.ComboBox({
30932             allowBlank : this.yearAllowBlank,
30933             alwaysQuery : true,
30934             displayField : 'value',
30935             editable : false,
30936             fieldLabel : '',
30937             forceSelection : true,
30938             mode : 'local',
30939             placeholder : this.yearPlaceholder,
30940             selectOnFocus : true,
30941             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30942             triggerAction : 'all',
30943             typeAhead : true,
30944             valueField : 'value',
30945             store : new Roo.data.SimpleStore({
30946                 data : (function() {
30947                     var years = [];
30948                     _this.fireEvent('years', _this, years);
30949                     return years;
30950                 })(),
30951                 fields : [ 'value' ]
30952             }),
30953             listeners : {
30954                 select : function (_self, record, index)
30955                 {
30956                     _this.setValue(_this.getValue());
30957                 }
30958             }
30959         });
30960
30961         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30962     },
30963     
30964     setValue : function(v, format)
30965     {
30966         this.inputEl.dom.value = v;
30967         
30968         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30969         
30970         var d = Date.parseDate(v, f);
30971         
30972         if(!d){
30973             this.validate();
30974             return;
30975         }
30976         
30977         this.setDay(d.format(this.dayFormat));
30978         this.setMonth(d.format(this.monthFormat));
30979         this.setYear(d.format(this.yearFormat));
30980         
30981         this.validate();
30982         
30983         return;
30984     },
30985     
30986     setDay : function(v)
30987     {
30988         this.dayField.setValue(v);
30989         this.inputEl.dom.value = this.getValue();
30990         this.validate();
30991         return;
30992     },
30993     
30994     setMonth : function(v)
30995     {
30996         this.monthField.setValue(v, true);
30997         this.inputEl.dom.value = this.getValue();
30998         this.validate();
30999         return;
31000     },
31001     
31002     setYear : function(v)
31003     {
31004         this.yearField.setValue(v);
31005         this.inputEl.dom.value = this.getValue();
31006         this.validate();
31007         return;
31008     },
31009     
31010     getDay : function()
31011     {
31012         return this.dayField.getValue();
31013     },
31014     
31015     getMonth : function()
31016     {
31017         return this.monthField.getValue();
31018     },
31019     
31020     getYear : function()
31021     {
31022         return this.yearField.getValue();
31023     },
31024     
31025     getValue : function()
31026     {
31027         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
31028         
31029         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
31030         
31031         return date;
31032     },
31033     
31034     reset : function()
31035     {
31036         this.setDay('');
31037         this.setMonth('');
31038         this.setYear('');
31039         this.inputEl.dom.value = '';
31040         this.validate();
31041         return;
31042     },
31043     
31044     validate : function()
31045     {
31046         var d = this.dayField.validate();
31047         var m = this.monthField.validate();
31048         var y = this.yearField.validate();
31049         
31050         var valid = true;
31051         
31052         if(
31053                 (!this.dayAllowBlank && !d) ||
31054                 (!this.monthAllowBlank && !m) ||
31055                 (!this.yearAllowBlank && !y)
31056         ){
31057             valid = false;
31058         }
31059         
31060         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
31061             return valid;
31062         }
31063         
31064         if(valid){
31065             this.markValid();
31066             return valid;
31067         }
31068         
31069         this.markInvalid();
31070         
31071         return valid;
31072     },
31073     
31074     markValid : function()
31075     {
31076         
31077         var label = this.el.select('label', true).first();
31078         var icon = this.el.select('i.fa-star', true).first();
31079
31080         if(label && icon){
31081             icon.remove();
31082         }
31083         
31084         this.fireEvent('valid', this);
31085     },
31086     
31087      /**
31088      * Mark this field as invalid
31089      * @param {String} msg The validation message
31090      */
31091     markInvalid : function(msg)
31092     {
31093         
31094         var label = this.el.select('label', true).first();
31095         var icon = this.el.select('i.fa-star', true).first();
31096
31097         if(label && !icon){
31098             this.el.select('.roo-date-split-field-label', true).createChild({
31099                 tag : 'i',
31100                 cls : 'text-danger fa fa-lg fa-star',
31101                 tooltip : 'This field is required',
31102                 style : 'margin-right:5px;'
31103             }, label, true);
31104         }
31105         
31106         this.fireEvent('invalid', this, msg);
31107     },
31108     
31109     clearInvalid : function()
31110     {
31111         var label = this.el.select('label', true).first();
31112         var icon = this.el.select('i.fa-star', true).first();
31113
31114         if(label && icon){
31115             icon.remove();
31116         }
31117         
31118         this.fireEvent('valid', this);
31119     },
31120     
31121     getName: function()
31122     {
31123         return this.name;
31124     }
31125     
31126 });
31127
31128  /**
31129  *
31130  * This is based on 
31131  * http://masonry.desandro.com
31132  *
31133  * The idea is to render all the bricks based on vertical width...
31134  *
31135  * The original code extends 'outlayer' - we might need to use that....
31136  * 
31137  */
31138
31139
31140 /**
31141  * @class Roo.bootstrap.LayoutMasonry
31142  * @extends Roo.bootstrap.Component
31143  * Bootstrap Layout Masonry class
31144  * 
31145  * @constructor
31146  * Create a new Element
31147  * @param {Object} config The config object
31148  */
31149
31150 Roo.bootstrap.LayoutMasonry = function(config){
31151     
31152     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
31153     
31154     this.bricks = [];
31155     
31156     Roo.bootstrap.LayoutMasonry.register(this);
31157     
31158     this.addEvents({
31159         // raw events
31160         /**
31161          * @event layout
31162          * Fire after layout the items
31163          * @param {Roo.bootstrap.LayoutMasonry} this
31164          * @param {Roo.EventObject} e
31165          */
31166         "layout" : true
31167     });
31168     
31169 };
31170
31171 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
31172     
31173     /**
31174      * @cfg {Boolean} isLayoutInstant = no animation?
31175      */   
31176     isLayoutInstant : false, // needed?
31177    
31178     /**
31179      * @cfg {Number} boxWidth  width of the columns
31180      */   
31181     boxWidth : 450,
31182     
31183       /**
31184      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
31185      */   
31186     boxHeight : 0,
31187     
31188     /**
31189      * @cfg {Number} padWidth padding below box..
31190      */   
31191     padWidth : 10, 
31192     
31193     /**
31194      * @cfg {Number} gutter gutter width..
31195      */   
31196     gutter : 10,
31197     
31198      /**
31199      * @cfg {Number} maxCols maximum number of columns
31200      */   
31201     
31202     maxCols: 0,
31203     
31204     /**
31205      * @cfg {Boolean} isAutoInitial defalut true
31206      */   
31207     isAutoInitial : true, 
31208     
31209     containerWidth: 0,
31210     
31211     /**
31212      * @cfg {Boolean} isHorizontal defalut false
31213      */   
31214     isHorizontal : false, 
31215
31216     currentSize : null,
31217     
31218     tag: 'div',
31219     
31220     cls: '',
31221     
31222     bricks: null, //CompositeElement
31223     
31224     cols : 1,
31225     
31226     _isLayoutInited : false,
31227     
31228 //    isAlternative : false, // only use for vertical layout...
31229     
31230     /**
31231      * @cfg {Number} alternativePadWidth padding below box..
31232      */   
31233     alternativePadWidth : 50,
31234     
31235     selectedBrick : [],
31236     
31237     getAutoCreate : function(){
31238         
31239         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31240         
31241         var cfg = {
31242             tag: this.tag,
31243             cls: 'blog-masonary-wrapper ' + this.cls,
31244             cn : {
31245                 cls : 'mas-boxes masonary'
31246             }
31247         };
31248         
31249         return cfg;
31250     },
31251     
31252     getChildContainer: function( )
31253     {
31254         if (this.boxesEl) {
31255             return this.boxesEl;
31256         }
31257         
31258         this.boxesEl = this.el.select('.mas-boxes').first();
31259         
31260         return this.boxesEl;
31261     },
31262     
31263     
31264     initEvents : function()
31265     {
31266         var _this = this;
31267         
31268         if(this.isAutoInitial){
31269             Roo.log('hook children rendered');
31270             this.on('childrenrendered', function() {
31271                 Roo.log('children rendered');
31272                 _this.initial();
31273             } ,this);
31274         }
31275     },
31276     
31277     initial : function()
31278     {
31279         this.selectedBrick = [];
31280         
31281         this.currentSize = this.el.getBox(true);
31282         
31283         Roo.EventManager.onWindowResize(this.resize, this); 
31284
31285         if(!this.isAutoInitial){
31286             this.layout();
31287             return;
31288         }
31289         
31290         this.layout();
31291         
31292         return;
31293         //this.layout.defer(500,this);
31294         
31295     },
31296     
31297     resize : function()
31298     {
31299         var cs = this.el.getBox(true);
31300         
31301         if (
31302                 this.currentSize.width == cs.width && 
31303                 this.currentSize.x == cs.x && 
31304                 this.currentSize.height == cs.height && 
31305                 this.currentSize.y == cs.y 
31306         ) {
31307             Roo.log("no change in with or X or Y");
31308             return;
31309         }
31310         
31311         this.currentSize = cs;
31312         
31313         this.layout();
31314         
31315     },
31316     
31317     layout : function()
31318     {   
31319         this._resetLayout();
31320         
31321         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31322         
31323         this.layoutItems( isInstant );
31324       
31325         this._isLayoutInited = true;
31326         
31327         this.fireEvent('layout', this);
31328         
31329     },
31330     
31331     _resetLayout : function()
31332     {
31333         if(this.isHorizontal){
31334             this.horizontalMeasureColumns();
31335             return;
31336         }
31337         
31338         this.verticalMeasureColumns();
31339         
31340     },
31341     
31342     verticalMeasureColumns : function()
31343     {
31344         this.getContainerWidth();
31345         
31346 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31347 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31348 //            return;
31349 //        }
31350         
31351         var boxWidth = this.boxWidth + this.padWidth;
31352         
31353         if(this.containerWidth < this.boxWidth){
31354             boxWidth = this.containerWidth
31355         }
31356         
31357         var containerWidth = this.containerWidth;
31358         
31359         var cols = Math.floor(containerWidth / boxWidth);
31360         
31361         this.cols = Math.max( cols, 1 );
31362         
31363         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31364         
31365         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31366         
31367         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31368         
31369         this.colWidth = boxWidth + avail - this.padWidth;
31370         
31371         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31372         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31373     },
31374     
31375     horizontalMeasureColumns : function()
31376     {
31377         this.getContainerWidth();
31378         
31379         var boxWidth = this.boxWidth;
31380         
31381         if(this.containerWidth < boxWidth){
31382             boxWidth = this.containerWidth;
31383         }
31384         
31385         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31386         
31387         this.el.setHeight(boxWidth);
31388         
31389     },
31390     
31391     getContainerWidth : function()
31392     {
31393         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31394     },
31395     
31396     layoutItems : function( isInstant )
31397     {
31398         Roo.log(this.bricks);
31399         
31400         var items = Roo.apply([], this.bricks);
31401         
31402         if(this.isHorizontal){
31403             this._horizontalLayoutItems( items , isInstant );
31404             return;
31405         }
31406         
31407 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31408 //            this._verticalAlternativeLayoutItems( items , isInstant );
31409 //            return;
31410 //        }
31411         
31412         this._verticalLayoutItems( items , isInstant );
31413         
31414     },
31415     
31416     _verticalLayoutItems : function ( items , isInstant)
31417     {
31418         if ( !items || !items.length ) {
31419             return;
31420         }
31421         
31422         var standard = [
31423             ['xs', 'xs', 'xs', 'tall'],
31424             ['xs', 'xs', 'tall'],
31425             ['xs', 'xs', 'sm'],
31426             ['xs', 'xs', 'xs'],
31427             ['xs', 'tall'],
31428             ['xs', 'sm'],
31429             ['xs', 'xs'],
31430             ['xs'],
31431             
31432             ['sm', 'xs', 'xs'],
31433             ['sm', 'xs'],
31434             ['sm'],
31435             
31436             ['tall', 'xs', 'xs', 'xs'],
31437             ['tall', 'xs', 'xs'],
31438             ['tall', 'xs'],
31439             ['tall']
31440             
31441         ];
31442         
31443         var queue = [];
31444         
31445         var boxes = [];
31446         
31447         var box = [];
31448         
31449         Roo.each(items, function(item, k){
31450             
31451             switch (item.size) {
31452                 // these layouts take up a full box,
31453                 case 'md' :
31454                 case 'md-left' :
31455                 case 'md-right' :
31456                 case 'wide' :
31457                     
31458                     if(box.length){
31459                         boxes.push(box);
31460                         box = [];
31461                     }
31462                     
31463                     boxes.push([item]);
31464                     
31465                     break;
31466                     
31467                 case 'xs' :
31468                 case 'sm' :
31469                 case 'tall' :
31470                     
31471                     box.push(item);
31472                     
31473                     break;
31474                 default :
31475                     break;
31476                     
31477             }
31478             
31479         }, this);
31480         
31481         if(box.length){
31482             boxes.push(box);
31483             box = [];
31484         }
31485         
31486         var filterPattern = function(box, length)
31487         {
31488             if(!box.length){
31489                 return;
31490             }
31491             
31492             var match = false;
31493             
31494             var pattern = box.slice(0, length);
31495             
31496             var format = [];
31497             
31498             Roo.each(pattern, function(i){
31499                 format.push(i.size);
31500             }, this);
31501             
31502             Roo.each(standard, function(s){
31503                 
31504                 if(String(s) != String(format)){
31505                     return;
31506                 }
31507                 
31508                 match = true;
31509                 return false;
31510                 
31511             }, this);
31512             
31513             if(!match && length == 1){
31514                 return;
31515             }
31516             
31517             if(!match){
31518                 filterPattern(box, length - 1);
31519                 return;
31520             }
31521                 
31522             queue.push(pattern);
31523
31524             box = box.slice(length, box.length);
31525
31526             filterPattern(box, 4);
31527
31528             return;
31529             
31530         }
31531         
31532         Roo.each(boxes, function(box, k){
31533             
31534             if(!box.length){
31535                 return;
31536             }
31537             
31538             if(box.length == 1){
31539                 queue.push(box);
31540                 return;
31541             }
31542             
31543             filterPattern(box, 4);
31544             
31545         }, this);
31546         
31547         this._processVerticalLayoutQueue( queue, isInstant );
31548         
31549     },
31550     
31551 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31552 //    {
31553 //        if ( !items || !items.length ) {
31554 //            return;
31555 //        }
31556 //
31557 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31558 //        
31559 //    },
31560     
31561     _horizontalLayoutItems : function ( items , isInstant)
31562     {
31563         if ( !items || !items.length || items.length < 3) {
31564             return;
31565         }
31566         
31567         items.reverse();
31568         
31569         var eItems = items.slice(0, 3);
31570         
31571         items = items.slice(3, items.length);
31572         
31573         var standard = [
31574             ['xs', 'xs', 'xs', 'wide'],
31575             ['xs', 'xs', 'wide'],
31576             ['xs', 'xs', 'sm'],
31577             ['xs', 'xs', 'xs'],
31578             ['xs', 'wide'],
31579             ['xs', 'sm'],
31580             ['xs', 'xs'],
31581             ['xs'],
31582             
31583             ['sm', 'xs', 'xs'],
31584             ['sm', 'xs'],
31585             ['sm'],
31586             
31587             ['wide', 'xs', 'xs', 'xs'],
31588             ['wide', 'xs', 'xs'],
31589             ['wide', 'xs'],
31590             ['wide'],
31591             
31592             ['wide-thin']
31593         ];
31594         
31595         var queue = [];
31596         
31597         var boxes = [];
31598         
31599         var box = [];
31600         
31601         Roo.each(items, function(item, k){
31602             
31603             switch (item.size) {
31604                 case 'md' :
31605                 case 'md-left' :
31606                 case 'md-right' :
31607                 case 'tall' :
31608                     
31609                     if(box.length){
31610                         boxes.push(box);
31611                         box = [];
31612                     }
31613                     
31614                     boxes.push([item]);
31615                     
31616                     break;
31617                     
31618                 case 'xs' :
31619                 case 'sm' :
31620                 case 'wide' :
31621                 case 'wide-thin' :
31622                     
31623                     box.push(item);
31624                     
31625                     break;
31626                 default :
31627                     break;
31628                     
31629             }
31630             
31631         }, this);
31632         
31633         if(box.length){
31634             boxes.push(box);
31635             box = [];
31636         }
31637         
31638         var filterPattern = function(box, length)
31639         {
31640             if(!box.length){
31641                 return;
31642             }
31643             
31644             var match = false;
31645             
31646             var pattern = box.slice(0, length);
31647             
31648             var format = [];
31649             
31650             Roo.each(pattern, function(i){
31651                 format.push(i.size);
31652             }, this);
31653             
31654             Roo.each(standard, function(s){
31655                 
31656                 if(String(s) != String(format)){
31657                     return;
31658                 }
31659                 
31660                 match = true;
31661                 return false;
31662                 
31663             }, this);
31664             
31665             if(!match && length == 1){
31666                 return;
31667             }
31668             
31669             if(!match){
31670                 filterPattern(box, length - 1);
31671                 return;
31672             }
31673                 
31674             queue.push(pattern);
31675
31676             box = box.slice(length, box.length);
31677
31678             filterPattern(box, 4);
31679
31680             return;
31681             
31682         }
31683         
31684         Roo.each(boxes, function(box, k){
31685             
31686             if(!box.length){
31687                 return;
31688             }
31689             
31690             if(box.length == 1){
31691                 queue.push(box);
31692                 return;
31693             }
31694             
31695             filterPattern(box, 4);
31696             
31697         }, this);
31698         
31699         
31700         var prune = [];
31701         
31702         var pos = this.el.getBox(true);
31703         
31704         var minX = pos.x;
31705         
31706         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31707         
31708         var hit_end = false;
31709         
31710         Roo.each(queue, function(box){
31711             
31712             if(hit_end){
31713                 
31714                 Roo.each(box, function(b){
31715                 
31716                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31717                     b.el.hide();
31718
31719                 }, this);
31720
31721                 return;
31722             }
31723             
31724             var mx = 0;
31725             
31726             Roo.each(box, function(b){
31727                 
31728                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31729                 b.el.show();
31730
31731                 mx = Math.max(mx, b.x);
31732                 
31733             }, this);
31734             
31735             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31736             
31737             if(maxX < minX){
31738                 
31739                 Roo.each(box, function(b){
31740                 
31741                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31742                     b.el.hide();
31743                     
31744                 }, this);
31745                 
31746                 hit_end = true;
31747                 
31748                 return;
31749             }
31750             
31751             prune.push(box);
31752             
31753         }, this);
31754         
31755         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31756     },
31757     
31758     /** Sets position of item in DOM
31759     * @param {Element} item
31760     * @param {Number} x - horizontal position
31761     * @param {Number} y - vertical position
31762     * @param {Boolean} isInstant - disables transitions
31763     */
31764     _processVerticalLayoutQueue : function( queue, isInstant )
31765     {
31766         var pos = this.el.getBox(true);
31767         var x = pos.x;
31768         var y = pos.y;
31769         var maxY = [];
31770         
31771         for (var i = 0; i < this.cols; i++){
31772             maxY[i] = pos.y;
31773         }
31774         
31775         Roo.each(queue, function(box, k){
31776             
31777             var col = k % this.cols;
31778             
31779             Roo.each(box, function(b,kk){
31780                 
31781                 b.el.position('absolute');
31782                 
31783                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31784                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31785                 
31786                 if(b.size == 'md-left' || b.size == 'md-right'){
31787                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31788                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31789                 }
31790                 
31791                 b.el.setWidth(width);
31792                 b.el.setHeight(height);
31793                 // iframe?
31794                 b.el.select('iframe',true).setSize(width,height);
31795                 
31796             }, this);
31797             
31798             for (var i = 0; i < this.cols; i++){
31799                 
31800                 if(maxY[i] < maxY[col]){
31801                     col = i;
31802                     continue;
31803                 }
31804                 
31805                 col = Math.min(col, i);
31806                 
31807             }
31808             
31809             x = pos.x + col * (this.colWidth + this.padWidth);
31810             
31811             y = maxY[col];
31812             
31813             var positions = [];
31814             
31815             switch (box.length){
31816                 case 1 :
31817                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31818                     break;
31819                 case 2 :
31820                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31821                     break;
31822                 case 3 :
31823                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31824                     break;
31825                 case 4 :
31826                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31827                     break;
31828                 default :
31829                     break;
31830             }
31831             
31832             Roo.each(box, function(b,kk){
31833                 
31834                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31835                 
31836                 var sz = b.el.getSize();
31837                 
31838                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31839                 
31840             }, this);
31841             
31842         }, this);
31843         
31844         var mY = 0;
31845         
31846         for (var i = 0; i < this.cols; i++){
31847             mY = Math.max(mY, maxY[i]);
31848         }
31849         
31850         this.el.setHeight(mY - pos.y);
31851         
31852     },
31853     
31854 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31855 //    {
31856 //        var pos = this.el.getBox(true);
31857 //        var x = pos.x;
31858 //        var y = pos.y;
31859 //        var maxX = pos.right;
31860 //        
31861 //        var maxHeight = 0;
31862 //        
31863 //        Roo.each(items, function(item, k){
31864 //            
31865 //            var c = k % 2;
31866 //            
31867 //            item.el.position('absolute');
31868 //                
31869 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31870 //
31871 //            item.el.setWidth(width);
31872 //
31873 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31874 //
31875 //            item.el.setHeight(height);
31876 //            
31877 //            if(c == 0){
31878 //                item.el.setXY([x, y], isInstant ? false : true);
31879 //            } else {
31880 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31881 //            }
31882 //            
31883 //            y = y + height + this.alternativePadWidth;
31884 //            
31885 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31886 //            
31887 //        }, this);
31888 //        
31889 //        this.el.setHeight(maxHeight);
31890 //        
31891 //    },
31892     
31893     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31894     {
31895         var pos = this.el.getBox(true);
31896         
31897         var minX = pos.x;
31898         var minY = pos.y;
31899         
31900         var maxX = pos.right;
31901         
31902         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31903         
31904         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31905         
31906         Roo.each(queue, function(box, k){
31907             
31908             Roo.each(box, function(b, kk){
31909                 
31910                 b.el.position('absolute');
31911                 
31912                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31913                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31914                 
31915                 if(b.size == 'md-left' || b.size == 'md-right'){
31916                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31917                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31918                 }
31919                 
31920                 b.el.setWidth(width);
31921                 b.el.setHeight(height);
31922                 
31923             }, this);
31924             
31925             if(!box.length){
31926                 return;
31927             }
31928             
31929             var positions = [];
31930             
31931             switch (box.length){
31932                 case 1 :
31933                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31934                     break;
31935                 case 2 :
31936                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31937                     break;
31938                 case 3 :
31939                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31940                     break;
31941                 case 4 :
31942                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31943                     break;
31944                 default :
31945                     break;
31946             }
31947             
31948             Roo.each(box, function(b,kk){
31949                 
31950                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31951                 
31952                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31953                 
31954             }, this);
31955             
31956         }, this);
31957         
31958     },
31959     
31960     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31961     {
31962         Roo.each(eItems, function(b,k){
31963             
31964             b.size = (k == 0) ? 'sm' : 'xs';
31965             b.x = (k == 0) ? 2 : 1;
31966             b.y = (k == 0) ? 2 : 1;
31967             
31968             b.el.position('absolute');
31969             
31970             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31971                 
31972             b.el.setWidth(width);
31973             
31974             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31975             
31976             b.el.setHeight(height);
31977             
31978         }, this);
31979
31980         var positions = [];
31981         
31982         positions.push({
31983             x : maxX - this.unitWidth * 2 - this.gutter,
31984             y : minY
31985         });
31986         
31987         positions.push({
31988             x : maxX - this.unitWidth,
31989             y : minY + (this.unitWidth + this.gutter) * 2
31990         });
31991         
31992         positions.push({
31993             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31994             y : minY
31995         });
31996         
31997         Roo.each(eItems, function(b,k){
31998             
31999             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
32000
32001         }, this);
32002         
32003     },
32004     
32005     getVerticalOneBoxColPositions : function(x, y, box)
32006     {
32007         var pos = [];
32008         
32009         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
32010         
32011         if(box[0].size == 'md-left'){
32012             rand = 0;
32013         }
32014         
32015         if(box[0].size == 'md-right'){
32016             rand = 1;
32017         }
32018         
32019         pos.push({
32020             x : x + (this.unitWidth + this.gutter) * rand,
32021             y : y
32022         });
32023         
32024         return pos;
32025     },
32026     
32027     getVerticalTwoBoxColPositions : function(x, y, box)
32028     {
32029         var pos = [];
32030         
32031         if(box[0].size == 'xs'){
32032             
32033             pos.push({
32034                 x : x,
32035                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
32036             });
32037
32038             pos.push({
32039                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
32040                 y : y
32041             });
32042             
32043             return pos;
32044             
32045         }
32046         
32047         pos.push({
32048             x : x,
32049             y : y
32050         });
32051
32052         pos.push({
32053             x : x + (this.unitWidth + this.gutter) * 2,
32054             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
32055         });
32056         
32057         return pos;
32058         
32059     },
32060     
32061     getVerticalThreeBoxColPositions : function(x, y, box)
32062     {
32063         var pos = [];
32064         
32065         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32066             
32067             pos.push({
32068                 x : x,
32069                 y : y
32070             });
32071
32072             pos.push({
32073                 x : x + (this.unitWidth + this.gutter) * 1,
32074                 y : y
32075             });
32076             
32077             pos.push({
32078                 x : x + (this.unitWidth + this.gutter) * 2,
32079                 y : y
32080             });
32081             
32082             return pos;
32083             
32084         }
32085         
32086         if(box[0].size == 'xs' && box[1].size == 'xs'){
32087             
32088             pos.push({
32089                 x : x,
32090                 y : y
32091             });
32092
32093             pos.push({
32094                 x : x,
32095                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
32096             });
32097             
32098             pos.push({
32099                 x : x + (this.unitWidth + this.gutter) * 1,
32100                 y : y
32101             });
32102             
32103             return pos;
32104             
32105         }
32106         
32107         pos.push({
32108             x : x,
32109             y : y
32110         });
32111
32112         pos.push({
32113             x : x + (this.unitWidth + this.gutter) * 2,
32114             y : y
32115         });
32116
32117         pos.push({
32118             x : x + (this.unitWidth + this.gutter) * 2,
32119             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
32120         });
32121             
32122         return pos;
32123         
32124     },
32125     
32126     getVerticalFourBoxColPositions : function(x, y, box)
32127     {
32128         var pos = [];
32129         
32130         if(box[0].size == 'xs'){
32131             
32132             pos.push({
32133                 x : x,
32134                 y : y
32135             });
32136
32137             pos.push({
32138                 x : x,
32139                 y : y + (this.unitHeight + this.gutter) * 1
32140             });
32141             
32142             pos.push({
32143                 x : x,
32144                 y : y + (this.unitHeight + this.gutter) * 2
32145             });
32146             
32147             pos.push({
32148                 x : x + (this.unitWidth + this.gutter) * 1,
32149                 y : y
32150             });
32151             
32152             return pos;
32153             
32154         }
32155         
32156         pos.push({
32157             x : x,
32158             y : y
32159         });
32160
32161         pos.push({
32162             x : x + (this.unitWidth + this.gutter) * 2,
32163             y : y
32164         });
32165
32166         pos.push({
32167             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
32168             y : y + (this.unitHeight + this.gutter) * 1
32169         });
32170
32171         pos.push({
32172             x : x + (this.unitWidth + this.gutter) * 2,
32173             y : y + (this.unitWidth + this.gutter) * 2
32174         });
32175
32176         return pos;
32177         
32178     },
32179     
32180     getHorizontalOneBoxColPositions : function(maxX, minY, box)
32181     {
32182         var pos = [];
32183         
32184         if(box[0].size == 'md-left'){
32185             pos.push({
32186                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32187                 y : minY
32188             });
32189             
32190             return pos;
32191         }
32192         
32193         if(box[0].size == 'md-right'){
32194             pos.push({
32195                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32196                 y : minY + (this.unitWidth + this.gutter) * 1
32197             });
32198             
32199             return pos;
32200         }
32201         
32202         var rand = Math.floor(Math.random() * (4 - box[0].y));
32203         
32204         pos.push({
32205             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32206             y : minY + (this.unitWidth + this.gutter) * rand
32207         });
32208         
32209         return pos;
32210         
32211     },
32212     
32213     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
32214     {
32215         var pos = [];
32216         
32217         if(box[0].size == 'xs'){
32218             
32219             pos.push({
32220                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32221                 y : minY
32222             });
32223
32224             pos.push({
32225                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32226                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32227             });
32228             
32229             return pos;
32230             
32231         }
32232         
32233         pos.push({
32234             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32235             y : minY
32236         });
32237
32238         pos.push({
32239             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32240             y : minY + (this.unitWidth + this.gutter) * 2
32241         });
32242         
32243         return pos;
32244         
32245     },
32246     
32247     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32248     {
32249         var pos = [];
32250         
32251         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32252             
32253             pos.push({
32254                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32255                 y : minY
32256             });
32257
32258             pos.push({
32259                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32260                 y : minY + (this.unitWidth + this.gutter) * 1
32261             });
32262             
32263             pos.push({
32264                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32265                 y : minY + (this.unitWidth + this.gutter) * 2
32266             });
32267             
32268             return pos;
32269             
32270         }
32271         
32272         if(box[0].size == 'xs' && box[1].size == 'xs'){
32273             
32274             pos.push({
32275                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32276                 y : minY
32277             });
32278
32279             pos.push({
32280                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32281                 y : minY
32282             });
32283             
32284             pos.push({
32285                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32286                 y : minY + (this.unitWidth + this.gutter) * 1
32287             });
32288             
32289             return pos;
32290             
32291         }
32292         
32293         pos.push({
32294             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32295             y : minY
32296         });
32297
32298         pos.push({
32299             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32300             y : minY + (this.unitWidth + this.gutter) * 2
32301         });
32302
32303         pos.push({
32304             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32305             y : minY + (this.unitWidth + this.gutter) * 2
32306         });
32307             
32308         return pos;
32309         
32310     },
32311     
32312     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32313     {
32314         var pos = [];
32315         
32316         if(box[0].size == 'xs'){
32317             
32318             pos.push({
32319                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32320                 y : minY
32321             });
32322
32323             pos.push({
32324                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32325                 y : minY
32326             });
32327             
32328             pos.push({
32329                 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),
32330                 y : minY
32331             });
32332             
32333             pos.push({
32334                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32335                 y : minY + (this.unitWidth + this.gutter) * 1
32336             });
32337             
32338             return pos;
32339             
32340         }
32341         
32342         pos.push({
32343             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32344             y : minY
32345         });
32346         
32347         pos.push({
32348             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32349             y : minY + (this.unitWidth + this.gutter) * 2
32350         });
32351         
32352         pos.push({
32353             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32354             y : minY + (this.unitWidth + this.gutter) * 2
32355         });
32356         
32357         pos.push({
32358             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),
32359             y : minY + (this.unitWidth + this.gutter) * 2
32360         });
32361
32362         return pos;
32363         
32364     },
32365     
32366     /**
32367     * remove a Masonry Brick
32368     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32369     */
32370     removeBrick : function(brick_id)
32371     {
32372         if (!brick_id) {
32373             return;
32374         }
32375         
32376         for (var i = 0; i<this.bricks.length; i++) {
32377             if (this.bricks[i].id == brick_id) {
32378                 this.bricks.splice(i,1);
32379                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32380                 this.initial();
32381             }
32382         }
32383     },
32384     
32385     /**
32386     * adds a Masonry Brick
32387     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32388     */
32389     addBrick : function(cfg)
32390     {
32391         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32392         //this.register(cn);
32393         cn.parentId = this.id;
32394         cn.render(this.el);
32395         return cn;
32396     },
32397     
32398     /**
32399     * register a Masonry Brick
32400     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32401     */
32402     
32403     register : function(brick)
32404     {
32405         this.bricks.push(brick);
32406         brick.masonryId = this.id;
32407     },
32408     
32409     /**
32410     * clear all the Masonry Brick
32411     */
32412     clearAll : function()
32413     {
32414         this.bricks = [];
32415         //this.getChildContainer().dom.innerHTML = "";
32416         this.el.dom.innerHTML = '';
32417     },
32418     
32419     getSelected : function()
32420     {
32421         if (!this.selectedBrick) {
32422             return false;
32423         }
32424         
32425         return this.selectedBrick;
32426     }
32427 });
32428
32429 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32430     
32431     groups: {},
32432      /**
32433     * register a Masonry Layout
32434     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32435     */
32436     
32437     register : function(layout)
32438     {
32439         this.groups[layout.id] = layout;
32440     },
32441     /**
32442     * fetch a  Masonry Layout based on the masonry layout ID
32443     * @param {string} the masonry layout to add
32444     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32445     */
32446     
32447     get: function(layout_id) {
32448         if (typeof(this.groups[layout_id]) == 'undefined') {
32449             return false;
32450         }
32451         return this.groups[layout_id] ;
32452     }
32453     
32454     
32455     
32456 });
32457
32458  
32459
32460  /**
32461  *
32462  * This is based on 
32463  * http://masonry.desandro.com
32464  *
32465  * The idea is to render all the bricks based on vertical width...
32466  *
32467  * The original code extends 'outlayer' - we might need to use that....
32468  * 
32469  */
32470
32471
32472 /**
32473  * @class Roo.bootstrap.LayoutMasonryAuto
32474  * @extends Roo.bootstrap.Component
32475  * Bootstrap Layout Masonry class
32476  * 
32477  * @constructor
32478  * Create a new Element
32479  * @param {Object} config The config object
32480  */
32481
32482 Roo.bootstrap.LayoutMasonryAuto = function(config){
32483     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32484 };
32485
32486 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32487     
32488       /**
32489      * @cfg {Boolean} isFitWidth  - resize the width..
32490      */   
32491     isFitWidth : false,  // options..
32492     /**
32493      * @cfg {Boolean} isOriginLeft = left align?
32494      */   
32495     isOriginLeft : true,
32496     /**
32497      * @cfg {Boolean} isOriginTop = top align?
32498      */   
32499     isOriginTop : false,
32500     /**
32501      * @cfg {Boolean} isLayoutInstant = no animation?
32502      */   
32503     isLayoutInstant : false, // needed?
32504     /**
32505      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32506      */   
32507     isResizingContainer : true,
32508     /**
32509      * @cfg {Number} columnWidth  width of the columns 
32510      */   
32511     
32512     columnWidth : 0,
32513     
32514     /**
32515      * @cfg {Number} maxCols maximum number of columns
32516      */   
32517     
32518     maxCols: 0,
32519     /**
32520      * @cfg {Number} padHeight padding below box..
32521      */   
32522     
32523     padHeight : 10, 
32524     
32525     /**
32526      * @cfg {Boolean} isAutoInitial defalut true
32527      */   
32528     
32529     isAutoInitial : true, 
32530     
32531     // private?
32532     gutter : 0,
32533     
32534     containerWidth: 0,
32535     initialColumnWidth : 0,
32536     currentSize : null,
32537     
32538     colYs : null, // array.
32539     maxY : 0,
32540     padWidth: 10,
32541     
32542     
32543     tag: 'div',
32544     cls: '',
32545     bricks: null, //CompositeElement
32546     cols : 0, // array?
32547     // element : null, // wrapped now this.el
32548     _isLayoutInited : null, 
32549     
32550     
32551     getAutoCreate : function(){
32552         
32553         var cfg = {
32554             tag: this.tag,
32555             cls: 'blog-masonary-wrapper ' + this.cls,
32556             cn : {
32557                 cls : 'mas-boxes masonary'
32558             }
32559         };
32560         
32561         return cfg;
32562     },
32563     
32564     getChildContainer: function( )
32565     {
32566         if (this.boxesEl) {
32567             return this.boxesEl;
32568         }
32569         
32570         this.boxesEl = this.el.select('.mas-boxes').first();
32571         
32572         return this.boxesEl;
32573     },
32574     
32575     
32576     initEvents : function()
32577     {
32578         var _this = this;
32579         
32580         if(this.isAutoInitial){
32581             Roo.log('hook children rendered');
32582             this.on('childrenrendered', function() {
32583                 Roo.log('children rendered');
32584                 _this.initial();
32585             } ,this);
32586         }
32587         
32588     },
32589     
32590     initial : function()
32591     {
32592         this.reloadItems();
32593
32594         this.currentSize = this.el.getBox(true);
32595
32596         /// was window resize... - let's see if this works..
32597         Roo.EventManager.onWindowResize(this.resize, this); 
32598
32599         if(!this.isAutoInitial){
32600             this.layout();
32601             return;
32602         }
32603         
32604         this.layout.defer(500,this);
32605     },
32606     
32607     reloadItems: function()
32608     {
32609         this.bricks = this.el.select('.masonry-brick', true);
32610         
32611         this.bricks.each(function(b) {
32612             //Roo.log(b.getSize());
32613             if (!b.attr('originalwidth')) {
32614                 b.attr('originalwidth',  b.getSize().width);
32615             }
32616             
32617         });
32618         
32619         Roo.log(this.bricks.elements.length);
32620     },
32621     
32622     resize : function()
32623     {
32624         Roo.log('resize');
32625         var cs = this.el.getBox(true);
32626         
32627         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32628             Roo.log("no change in with or X");
32629             return;
32630         }
32631         this.currentSize = cs;
32632         this.layout();
32633     },
32634     
32635     layout : function()
32636     {
32637          Roo.log('layout');
32638         this._resetLayout();
32639         //this._manageStamps();
32640       
32641         // don't animate first layout
32642         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32643         this.layoutItems( isInstant );
32644       
32645         // flag for initalized
32646         this._isLayoutInited = true;
32647     },
32648     
32649     layoutItems : function( isInstant )
32650     {
32651         //var items = this._getItemsForLayout( this.items );
32652         // original code supports filtering layout items.. we just ignore it..
32653         
32654         this._layoutItems( this.bricks , isInstant );
32655       
32656         this._postLayout();
32657     },
32658     _layoutItems : function ( items , isInstant)
32659     {
32660        //this.fireEvent( 'layout', this, items );
32661     
32662
32663         if ( !items || !items.elements.length ) {
32664           // no items, emit event with empty array
32665             return;
32666         }
32667
32668         var queue = [];
32669         items.each(function(item) {
32670             Roo.log("layout item");
32671             Roo.log(item);
32672             // get x/y object from method
32673             var position = this._getItemLayoutPosition( item );
32674             // enqueue
32675             position.item = item;
32676             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32677             queue.push( position );
32678         }, this);
32679       
32680         this._processLayoutQueue( queue );
32681     },
32682     /** Sets position of item in DOM
32683     * @param {Element} item
32684     * @param {Number} x - horizontal position
32685     * @param {Number} y - vertical position
32686     * @param {Boolean} isInstant - disables transitions
32687     */
32688     _processLayoutQueue : function( queue )
32689     {
32690         for ( var i=0, len = queue.length; i < len; i++ ) {
32691             var obj = queue[i];
32692             obj.item.position('absolute');
32693             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32694         }
32695     },
32696       
32697     
32698     /**
32699     * Any logic you want to do after each layout,
32700     * i.e. size the container
32701     */
32702     _postLayout : function()
32703     {
32704         this.resizeContainer();
32705     },
32706     
32707     resizeContainer : function()
32708     {
32709         if ( !this.isResizingContainer ) {
32710             return;
32711         }
32712         var size = this._getContainerSize();
32713         if ( size ) {
32714             this.el.setSize(size.width,size.height);
32715             this.boxesEl.setSize(size.width,size.height);
32716         }
32717     },
32718     
32719     
32720     
32721     _resetLayout : function()
32722     {
32723         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32724         this.colWidth = this.el.getWidth();
32725         //this.gutter = this.el.getWidth(); 
32726         
32727         this.measureColumns();
32728
32729         // reset column Y
32730         var i = this.cols;
32731         this.colYs = [];
32732         while (i--) {
32733             this.colYs.push( 0 );
32734         }
32735     
32736         this.maxY = 0;
32737     },
32738
32739     measureColumns : function()
32740     {
32741         this.getContainerWidth();
32742       // if columnWidth is 0, default to outerWidth of first item
32743         if ( !this.columnWidth ) {
32744             var firstItem = this.bricks.first();
32745             Roo.log(firstItem);
32746             this.columnWidth  = this.containerWidth;
32747             if (firstItem && firstItem.attr('originalwidth') ) {
32748                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32749             }
32750             // columnWidth fall back to item of first element
32751             Roo.log("set column width?");
32752                         this.initialColumnWidth = this.columnWidth  ;
32753
32754             // if first elem has no width, default to size of container
32755             
32756         }
32757         
32758         
32759         if (this.initialColumnWidth) {
32760             this.columnWidth = this.initialColumnWidth;
32761         }
32762         
32763         
32764             
32765         // column width is fixed at the top - however if container width get's smaller we should
32766         // reduce it...
32767         
32768         // this bit calcs how man columns..
32769             
32770         var columnWidth = this.columnWidth += this.gutter;
32771       
32772         // calculate columns
32773         var containerWidth = this.containerWidth + this.gutter;
32774         
32775         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32776         // fix rounding errors, typically with gutters
32777         var excess = columnWidth - containerWidth % columnWidth;
32778         
32779         
32780         // if overshoot is less than a pixel, round up, otherwise floor it
32781         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32782         cols = Math[ mathMethod ]( cols );
32783         this.cols = Math.max( cols, 1 );
32784         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32785         
32786          // padding positioning..
32787         var totalColWidth = this.cols * this.columnWidth;
32788         var padavail = this.containerWidth - totalColWidth;
32789         // so for 2 columns - we need 3 'pads'
32790         
32791         var padNeeded = (1+this.cols) * this.padWidth;
32792         
32793         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32794         
32795         this.columnWidth += padExtra
32796         //this.padWidth = Math.floor(padavail /  ( this.cols));
32797         
32798         // adjust colum width so that padding is fixed??
32799         
32800         // we have 3 columns ... total = width * 3
32801         // we have X left over... that should be used by 
32802         
32803         //if (this.expandC) {
32804             
32805         //}
32806         
32807         
32808         
32809     },
32810     
32811     getContainerWidth : function()
32812     {
32813        /* // container is parent if fit width
32814         var container = this.isFitWidth ? this.element.parentNode : this.element;
32815         // check that this.size and size are there
32816         // IE8 triggers resize on body size change, so they might not be
32817         
32818         var size = getSize( container );  //FIXME
32819         this.containerWidth = size && size.innerWidth; //FIXME
32820         */
32821          
32822         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32823         
32824     },
32825     
32826     _getItemLayoutPosition : function( item )  // what is item?
32827     {
32828         // we resize the item to our columnWidth..
32829       
32830         item.setWidth(this.columnWidth);
32831         item.autoBoxAdjust  = false;
32832         
32833         var sz = item.getSize();
32834  
32835         // how many columns does this brick span
32836         var remainder = this.containerWidth % this.columnWidth;
32837         
32838         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32839         // round if off by 1 pixel, otherwise use ceil
32840         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32841         colSpan = Math.min( colSpan, this.cols );
32842         
32843         // normally this should be '1' as we dont' currently allow multi width columns..
32844         
32845         var colGroup = this._getColGroup( colSpan );
32846         // get the minimum Y value from the columns
32847         var minimumY = Math.min.apply( Math, colGroup );
32848         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32849         
32850         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32851          
32852         // position the brick
32853         var position = {
32854             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32855             y: this.currentSize.y + minimumY + this.padHeight
32856         };
32857         
32858         Roo.log(position);
32859         // apply setHeight to necessary columns
32860         var setHeight = minimumY + sz.height + this.padHeight;
32861         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32862         
32863         var setSpan = this.cols + 1 - colGroup.length;
32864         for ( var i = 0; i < setSpan; i++ ) {
32865           this.colYs[ shortColIndex + i ] = setHeight ;
32866         }
32867       
32868         return position;
32869     },
32870     
32871     /**
32872      * @param {Number} colSpan - number of columns the element spans
32873      * @returns {Array} colGroup
32874      */
32875     _getColGroup : function( colSpan )
32876     {
32877         if ( colSpan < 2 ) {
32878           // if brick spans only one column, use all the column Ys
32879           return this.colYs;
32880         }
32881       
32882         var colGroup = [];
32883         // how many different places could this brick fit horizontally
32884         var groupCount = this.cols + 1 - colSpan;
32885         // for each group potential horizontal position
32886         for ( var i = 0; i < groupCount; i++ ) {
32887           // make an array of colY values for that one group
32888           var groupColYs = this.colYs.slice( i, i + colSpan );
32889           // and get the max value of the array
32890           colGroup[i] = Math.max.apply( Math, groupColYs );
32891         }
32892         return colGroup;
32893     },
32894     /*
32895     _manageStamp : function( stamp )
32896     {
32897         var stampSize =  stamp.getSize();
32898         var offset = stamp.getBox();
32899         // get the columns that this stamp affects
32900         var firstX = this.isOriginLeft ? offset.x : offset.right;
32901         var lastX = firstX + stampSize.width;
32902         var firstCol = Math.floor( firstX / this.columnWidth );
32903         firstCol = Math.max( 0, firstCol );
32904         
32905         var lastCol = Math.floor( lastX / this.columnWidth );
32906         // lastCol should not go over if multiple of columnWidth #425
32907         lastCol -= lastX % this.columnWidth ? 0 : 1;
32908         lastCol = Math.min( this.cols - 1, lastCol );
32909         
32910         // set colYs to bottom of the stamp
32911         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32912             stampSize.height;
32913             
32914         for ( var i = firstCol; i <= lastCol; i++ ) {
32915           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32916         }
32917     },
32918     */
32919     
32920     _getContainerSize : function()
32921     {
32922         this.maxY = Math.max.apply( Math, this.colYs );
32923         var size = {
32924             height: this.maxY
32925         };
32926       
32927         if ( this.isFitWidth ) {
32928             size.width = this._getContainerFitWidth();
32929         }
32930       
32931         return size;
32932     },
32933     
32934     _getContainerFitWidth : function()
32935     {
32936         var unusedCols = 0;
32937         // count unused columns
32938         var i = this.cols;
32939         while ( --i ) {
32940           if ( this.colYs[i] !== 0 ) {
32941             break;
32942           }
32943           unusedCols++;
32944         }
32945         // fit container to columns that have been used
32946         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32947     },
32948     
32949     needsResizeLayout : function()
32950     {
32951         var previousWidth = this.containerWidth;
32952         this.getContainerWidth();
32953         return previousWidth !== this.containerWidth;
32954     }
32955  
32956 });
32957
32958  
32959
32960  /*
32961  * - LGPL
32962  *
32963  * element
32964  * 
32965  */
32966
32967 /**
32968  * @class Roo.bootstrap.MasonryBrick
32969  * @extends Roo.bootstrap.Component
32970  * Bootstrap MasonryBrick class
32971  * 
32972  * @constructor
32973  * Create a new MasonryBrick
32974  * @param {Object} config The config object
32975  */
32976
32977 Roo.bootstrap.MasonryBrick = function(config){
32978     
32979     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32980     
32981     Roo.bootstrap.MasonryBrick.register(this);
32982     
32983     this.addEvents({
32984         // raw events
32985         /**
32986          * @event click
32987          * When a MasonryBrick is clcik
32988          * @param {Roo.bootstrap.MasonryBrick} this
32989          * @param {Roo.EventObject} e
32990          */
32991         "click" : true
32992     });
32993 };
32994
32995 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32996     
32997     /**
32998      * @cfg {String} title
32999      */   
33000     title : '',
33001     /**
33002      * @cfg {String} html
33003      */   
33004     html : '',
33005     /**
33006      * @cfg {String} bgimage
33007      */   
33008     bgimage : '',
33009     /**
33010      * @cfg {String} videourl
33011      */   
33012     videourl : '',
33013     /**
33014      * @cfg {String} cls
33015      */   
33016     cls : '',
33017     /**
33018      * @cfg {String} href
33019      */   
33020     href : '',
33021     /**
33022      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
33023      */   
33024     size : 'xs',
33025     
33026     /**
33027      * @cfg {String} placetitle (center|bottom)
33028      */   
33029     placetitle : '',
33030     
33031     /**
33032      * @cfg {Boolean} isFitContainer defalut true
33033      */   
33034     isFitContainer : true, 
33035     
33036     /**
33037      * @cfg {Boolean} preventDefault defalut false
33038      */   
33039     preventDefault : false, 
33040     
33041     /**
33042      * @cfg {Boolean} inverse defalut false
33043      */   
33044     maskInverse : false, 
33045     
33046     getAutoCreate : function()
33047     {
33048         if(!this.isFitContainer){
33049             return this.getSplitAutoCreate();
33050         }
33051         
33052         var cls = 'masonry-brick masonry-brick-full';
33053         
33054         if(this.href.length){
33055             cls += ' masonry-brick-link';
33056         }
33057         
33058         if(this.bgimage.length){
33059             cls += ' masonry-brick-image';
33060         }
33061         
33062         if(this.maskInverse){
33063             cls += ' mask-inverse';
33064         }
33065         
33066         if(!this.html.length && !this.maskInverse && !this.videourl.length){
33067             cls += ' enable-mask';
33068         }
33069         
33070         if(this.size){
33071             cls += ' masonry-' + this.size + '-brick';
33072         }
33073         
33074         if(this.placetitle.length){
33075             
33076             switch (this.placetitle) {
33077                 case 'center' :
33078                     cls += ' masonry-center-title';
33079                     break;
33080                 case 'bottom' :
33081                     cls += ' masonry-bottom-title';
33082                     break;
33083                 default:
33084                     break;
33085             }
33086             
33087         } else {
33088             if(!this.html.length && !this.bgimage.length){
33089                 cls += ' masonry-center-title';
33090             }
33091
33092             if(!this.html.length && this.bgimage.length){
33093                 cls += ' masonry-bottom-title';
33094             }
33095         }
33096         
33097         if(this.cls){
33098             cls += ' ' + this.cls;
33099         }
33100         
33101         var cfg = {
33102             tag: (this.href.length) ? 'a' : 'div',
33103             cls: cls,
33104             cn: [
33105                 {
33106                     tag: 'div',
33107                     cls: 'masonry-brick-mask'
33108                 },
33109                 {
33110                     tag: 'div',
33111                     cls: 'masonry-brick-paragraph',
33112                     cn: []
33113                 }
33114             ]
33115         };
33116         
33117         if(this.href.length){
33118             cfg.href = this.href;
33119         }
33120         
33121         var cn = cfg.cn[1].cn;
33122         
33123         if(this.title.length){
33124             cn.push({
33125                 tag: 'h4',
33126                 cls: 'masonry-brick-title',
33127                 html: this.title
33128             });
33129         }
33130         
33131         if(this.html.length){
33132             cn.push({
33133                 tag: 'p',
33134                 cls: 'masonry-brick-text',
33135                 html: this.html
33136             });
33137         }
33138         
33139         if (!this.title.length && !this.html.length) {
33140             cfg.cn[1].cls += ' hide';
33141         }
33142         
33143         if(this.bgimage.length){
33144             cfg.cn.push({
33145                 tag: 'img',
33146                 cls: 'masonry-brick-image-view',
33147                 src: this.bgimage
33148             });
33149         }
33150         
33151         if(this.videourl.length){
33152             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33153             // youtube support only?
33154             cfg.cn.push({
33155                 tag: 'iframe',
33156                 cls: 'masonry-brick-image-view',
33157                 src: vurl,
33158                 frameborder : 0,
33159                 allowfullscreen : true
33160             });
33161         }
33162         
33163         return cfg;
33164         
33165     },
33166     
33167     getSplitAutoCreate : function()
33168     {
33169         var cls = 'masonry-brick masonry-brick-split';
33170         
33171         if(this.href.length){
33172             cls += ' masonry-brick-link';
33173         }
33174         
33175         if(this.bgimage.length){
33176             cls += ' masonry-brick-image';
33177         }
33178         
33179         if(this.size){
33180             cls += ' masonry-' + this.size + '-brick';
33181         }
33182         
33183         switch (this.placetitle) {
33184             case 'center' :
33185                 cls += ' masonry-center-title';
33186                 break;
33187             case 'bottom' :
33188                 cls += ' masonry-bottom-title';
33189                 break;
33190             default:
33191                 if(!this.bgimage.length){
33192                     cls += ' masonry-center-title';
33193                 }
33194
33195                 if(this.bgimage.length){
33196                     cls += ' masonry-bottom-title';
33197                 }
33198                 break;
33199         }
33200         
33201         if(this.cls){
33202             cls += ' ' + this.cls;
33203         }
33204         
33205         var cfg = {
33206             tag: (this.href.length) ? 'a' : 'div',
33207             cls: cls,
33208             cn: [
33209                 {
33210                     tag: 'div',
33211                     cls: 'masonry-brick-split-head',
33212                     cn: [
33213                         {
33214                             tag: 'div',
33215                             cls: 'masonry-brick-paragraph',
33216                             cn: []
33217                         }
33218                     ]
33219                 },
33220                 {
33221                     tag: 'div',
33222                     cls: 'masonry-brick-split-body',
33223                     cn: []
33224                 }
33225             ]
33226         };
33227         
33228         if(this.href.length){
33229             cfg.href = this.href;
33230         }
33231         
33232         if(this.title.length){
33233             cfg.cn[0].cn[0].cn.push({
33234                 tag: 'h4',
33235                 cls: 'masonry-brick-title',
33236                 html: this.title
33237             });
33238         }
33239         
33240         if(this.html.length){
33241             cfg.cn[1].cn.push({
33242                 tag: 'p',
33243                 cls: 'masonry-brick-text',
33244                 html: this.html
33245             });
33246         }
33247
33248         if(this.bgimage.length){
33249             cfg.cn[0].cn.push({
33250                 tag: 'img',
33251                 cls: 'masonry-brick-image-view',
33252                 src: this.bgimage
33253             });
33254         }
33255         
33256         if(this.videourl.length){
33257             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33258             // youtube support only?
33259             cfg.cn[0].cn.cn.push({
33260                 tag: 'iframe',
33261                 cls: 'masonry-brick-image-view',
33262                 src: vurl,
33263                 frameborder : 0,
33264                 allowfullscreen : true
33265             });
33266         }
33267         
33268         return cfg;
33269     },
33270     
33271     initEvents: function() 
33272     {
33273         switch (this.size) {
33274             case 'xs' :
33275                 this.x = 1;
33276                 this.y = 1;
33277                 break;
33278             case 'sm' :
33279                 this.x = 2;
33280                 this.y = 2;
33281                 break;
33282             case 'md' :
33283             case 'md-left' :
33284             case 'md-right' :
33285                 this.x = 3;
33286                 this.y = 3;
33287                 break;
33288             case 'tall' :
33289                 this.x = 2;
33290                 this.y = 3;
33291                 break;
33292             case 'wide' :
33293                 this.x = 3;
33294                 this.y = 2;
33295                 break;
33296             case 'wide-thin' :
33297                 this.x = 3;
33298                 this.y = 1;
33299                 break;
33300                         
33301             default :
33302                 break;
33303         }
33304         
33305         if(Roo.isTouch){
33306             this.el.on('touchstart', this.onTouchStart, this);
33307             this.el.on('touchmove', this.onTouchMove, this);
33308             this.el.on('touchend', this.onTouchEnd, this);
33309             this.el.on('contextmenu', this.onContextMenu, this);
33310         } else {
33311             this.el.on('mouseenter'  ,this.enter, this);
33312             this.el.on('mouseleave', this.leave, this);
33313             this.el.on('click', this.onClick, this);
33314         }
33315         
33316         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33317             this.parent().bricks.push(this);   
33318         }
33319         
33320     },
33321     
33322     onClick: function(e, el)
33323     {
33324         var time = this.endTimer - this.startTimer;
33325         // Roo.log(e.preventDefault());
33326         if(Roo.isTouch){
33327             if(time > 1000){
33328                 e.preventDefault();
33329                 return;
33330             }
33331         }
33332         
33333         if(!this.preventDefault){
33334             return;
33335         }
33336         
33337         e.preventDefault();
33338         
33339         if (this.activeClass != '') {
33340             this.selectBrick();
33341         }
33342         
33343         this.fireEvent('click', this, e);
33344     },
33345     
33346     enter: function(e, el)
33347     {
33348         e.preventDefault();
33349         
33350         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33351             return;
33352         }
33353         
33354         if(this.bgimage.length && this.html.length){
33355             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33356         }
33357     },
33358     
33359     leave: function(e, el)
33360     {
33361         e.preventDefault();
33362         
33363         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33364             return;
33365         }
33366         
33367         if(this.bgimage.length && this.html.length){
33368             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33369         }
33370     },
33371     
33372     onTouchStart: function(e, el)
33373     {
33374 //        e.preventDefault();
33375         
33376         this.touchmoved = false;
33377         
33378         if(!this.isFitContainer){
33379             return;
33380         }
33381         
33382         if(!this.bgimage.length || !this.html.length){
33383             return;
33384         }
33385         
33386         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33387         
33388         this.timer = new Date().getTime();
33389         
33390     },
33391     
33392     onTouchMove: function(e, el)
33393     {
33394         this.touchmoved = true;
33395     },
33396     
33397     onContextMenu : function(e,el)
33398     {
33399         e.preventDefault();
33400         e.stopPropagation();
33401         return false;
33402     },
33403     
33404     onTouchEnd: function(e, el)
33405     {
33406 //        e.preventDefault();
33407         
33408         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33409         
33410             this.leave(e,el);
33411             
33412             return;
33413         }
33414         
33415         if(!this.bgimage.length || !this.html.length){
33416             
33417             if(this.href.length){
33418                 window.location.href = this.href;
33419             }
33420             
33421             return;
33422         }
33423         
33424         if(!this.isFitContainer){
33425             return;
33426         }
33427         
33428         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33429         
33430         window.location.href = this.href;
33431     },
33432     
33433     //selection on single brick only
33434     selectBrick : function() {
33435         
33436         if (!this.parentId) {
33437             return;
33438         }
33439         
33440         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33441         var index = m.selectedBrick.indexOf(this.id);
33442         
33443         if ( index > -1) {
33444             m.selectedBrick.splice(index,1);
33445             this.el.removeClass(this.activeClass);
33446             return;
33447         }
33448         
33449         for(var i = 0; i < m.selectedBrick.length; i++) {
33450             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33451             b.el.removeClass(b.activeClass);
33452         }
33453         
33454         m.selectedBrick = [];
33455         
33456         m.selectedBrick.push(this.id);
33457         this.el.addClass(this.activeClass);
33458         return;
33459     },
33460     
33461     isSelected : function(){
33462         return this.el.hasClass(this.activeClass);
33463         
33464     }
33465 });
33466
33467 Roo.apply(Roo.bootstrap.MasonryBrick, {
33468     
33469     //groups: {},
33470     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33471      /**
33472     * register a Masonry Brick
33473     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33474     */
33475     
33476     register : function(brick)
33477     {
33478         //this.groups[brick.id] = brick;
33479         this.groups.add(brick.id, brick);
33480     },
33481     /**
33482     * fetch a  masonry brick based on the masonry brick ID
33483     * @param {string} the masonry brick to add
33484     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33485     */
33486     
33487     get: function(brick_id) 
33488     {
33489         // if (typeof(this.groups[brick_id]) == 'undefined') {
33490         //     return false;
33491         // }
33492         // return this.groups[brick_id] ;
33493         
33494         if(this.groups.key(brick_id)) {
33495             return this.groups.key(brick_id);
33496         }
33497         
33498         return false;
33499     }
33500     
33501     
33502     
33503 });
33504
33505  /*
33506  * - LGPL
33507  *
33508  * element
33509  * 
33510  */
33511
33512 /**
33513  * @class Roo.bootstrap.Brick
33514  * @extends Roo.bootstrap.Component
33515  * Bootstrap Brick class
33516  * 
33517  * @constructor
33518  * Create a new Brick
33519  * @param {Object} config The config object
33520  */
33521
33522 Roo.bootstrap.Brick = function(config){
33523     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33524     
33525     this.addEvents({
33526         // raw events
33527         /**
33528          * @event click
33529          * When a Brick is click
33530          * @param {Roo.bootstrap.Brick} this
33531          * @param {Roo.EventObject} e
33532          */
33533         "click" : true
33534     });
33535 };
33536
33537 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33538     
33539     /**
33540      * @cfg {String} title
33541      */   
33542     title : '',
33543     /**
33544      * @cfg {String} html
33545      */   
33546     html : '',
33547     /**
33548      * @cfg {String} bgimage
33549      */   
33550     bgimage : '',
33551     /**
33552      * @cfg {String} cls
33553      */   
33554     cls : '',
33555     /**
33556      * @cfg {String} href
33557      */   
33558     href : '',
33559     /**
33560      * @cfg {String} video
33561      */   
33562     video : '',
33563     /**
33564      * @cfg {Boolean} square
33565      */   
33566     square : true,
33567     
33568     getAutoCreate : function()
33569     {
33570         var cls = 'roo-brick';
33571         
33572         if(this.href.length){
33573             cls += ' roo-brick-link';
33574         }
33575         
33576         if(this.bgimage.length){
33577             cls += ' roo-brick-image';
33578         }
33579         
33580         if(!this.html.length && !this.bgimage.length){
33581             cls += ' roo-brick-center-title';
33582         }
33583         
33584         if(!this.html.length && this.bgimage.length){
33585             cls += ' roo-brick-bottom-title';
33586         }
33587         
33588         if(this.cls){
33589             cls += ' ' + this.cls;
33590         }
33591         
33592         var cfg = {
33593             tag: (this.href.length) ? 'a' : 'div',
33594             cls: cls,
33595             cn: [
33596                 {
33597                     tag: 'div',
33598                     cls: 'roo-brick-paragraph',
33599                     cn: []
33600                 }
33601             ]
33602         };
33603         
33604         if(this.href.length){
33605             cfg.href = this.href;
33606         }
33607         
33608         var cn = cfg.cn[0].cn;
33609         
33610         if(this.title.length){
33611             cn.push({
33612                 tag: 'h4',
33613                 cls: 'roo-brick-title',
33614                 html: this.title
33615             });
33616         }
33617         
33618         if(this.html.length){
33619             cn.push({
33620                 tag: 'p',
33621                 cls: 'roo-brick-text',
33622                 html: this.html
33623             });
33624         } else {
33625             cn.cls += ' hide';
33626         }
33627         
33628         if(this.bgimage.length){
33629             cfg.cn.push({
33630                 tag: 'img',
33631                 cls: 'roo-brick-image-view',
33632                 src: this.bgimage
33633             });
33634         }
33635         
33636         return cfg;
33637     },
33638     
33639     initEvents: function() 
33640     {
33641         if(this.title.length || this.html.length){
33642             this.el.on('mouseenter'  ,this.enter, this);
33643             this.el.on('mouseleave', this.leave, this);
33644         }
33645         
33646         Roo.EventManager.onWindowResize(this.resize, this); 
33647         
33648         if(this.bgimage.length){
33649             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33650             this.imageEl.on('load', this.onImageLoad, this);
33651             return;
33652         }
33653         
33654         this.resize();
33655     },
33656     
33657     onImageLoad : function()
33658     {
33659         this.resize();
33660     },
33661     
33662     resize : function()
33663     {
33664         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33665         
33666         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33667         
33668         if(this.bgimage.length){
33669             var image = this.el.select('.roo-brick-image-view', true).first();
33670             
33671             image.setWidth(paragraph.getWidth());
33672             
33673             if(this.square){
33674                 image.setHeight(paragraph.getWidth());
33675             }
33676             
33677             this.el.setHeight(image.getHeight());
33678             paragraph.setHeight(image.getHeight());
33679             
33680         }
33681         
33682     },
33683     
33684     enter: function(e, el)
33685     {
33686         e.preventDefault();
33687         
33688         if(this.bgimage.length){
33689             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33690             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33691         }
33692     },
33693     
33694     leave: function(e, el)
33695     {
33696         e.preventDefault();
33697         
33698         if(this.bgimage.length){
33699             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33700             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33701         }
33702     }
33703     
33704 });
33705
33706  
33707
33708  /*
33709  * - LGPL
33710  *
33711  * Number field 
33712  */
33713
33714 /**
33715  * @class Roo.bootstrap.NumberField
33716  * @extends Roo.bootstrap.Input
33717  * Bootstrap NumberField class
33718  * 
33719  * 
33720  * 
33721  * 
33722  * @constructor
33723  * Create a new NumberField
33724  * @param {Object} config The config object
33725  */
33726
33727 Roo.bootstrap.NumberField = function(config){
33728     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33729 };
33730
33731 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33732     
33733     /**
33734      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33735      */
33736     allowDecimals : true,
33737     /**
33738      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33739      */
33740     decimalSeparator : ".",
33741     /**
33742      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33743      */
33744     decimalPrecision : 2,
33745     /**
33746      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33747      */
33748     allowNegative : true,
33749     
33750     /**
33751      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33752      */
33753     allowZero: true,
33754     /**
33755      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33756      */
33757     minValue : Number.NEGATIVE_INFINITY,
33758     /**
33759      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33760      */
33761     maxValue : Number.MAX_VALUE,
33762     /**
33763      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33764      */
33765     minText : "The minimum value for this field is {0}",
33766     /**
33767      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33768      */
33769     maxText : "The maximum value for this field is {0}",
33770     /**
33771      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33772      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33773      */
33774     nanText : "{0} is not a valid number",
33775     /**
33776      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33777      */
33778     thousandsDelimiter : false,
33779     /**
33780      * @cfg {String} valueAlign alignment of value
33781      */
33782     valueAlign : "left",
33783
33784     getAutoCreate : function()
33785     {
33786         var hiddenInput = {
33787             tag: 'input',
33788             type: 'hidden',
33789             id: Roo.id(),
33790             cls: 'hidden-number-input'
33791         };
33792         
33793         if (this.name) {
33794             hiddenInput.name = this.name;
33795         }
33796         
33797         this.name = '';
33798         
33799         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33800         
33801         this.name = hiddenInput.name;
33802         
33803         if(cfg.cn.length > 0) {
33804             cfg.cn.push(hiddenInput);
33805         }
33806         
33807         return cfg;
33808     },
33809
33810     // private
33811     initEvents : function()
33812     {   
33813         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33814         
33815         var allowed = "0123456789";
33816         
33817         if(this.allowDecimals){
33818             allowed += this.decimalSeparator;
33819         }
33820         
33821         if(this.allowNegative){
33822             allowed += "-";
33823         }
33824         
33825         if(this.thousandsDelimiter) {
33826             allowed += ",";
33827         }
33828         
33829         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33830         
33831         var keyPress = function(e){
33832             
33833             var k = e.getKey();
33834             
33835             var c = e.getCharCode();
33836             
33837             if(
33838                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33839                     allowed.indexOf(String.fromCharCode(c)) === -1
33840             ){
33841                 e.stopEvent();
33842                 return;
33843             }
33844             
33845             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33846                 return;
33847             }
33848             
33849             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33850                 e.stopEvent();
33851             }
33852         };
33853         
33854         this.el.on("keypress", keyPress, this);
33855     },
33856     
33857     validateValue : function(value)
33858     {
33859         
33860         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33861             return false;
33862         }
33863         
33864         var num = this.parseValue(value);
33865         
33866         if(isNaN(num)){
33867             this.markInvalid(String.format(this.nanText, value));
33868             return false;
33869         }
33870         
33871         if(num < this.minValue){
33872             this.markInvalid(String.format(this.minText, this.minValue));
33873             return false;
33874         }
33875         
33876         if(num > this.maxValue){
33877             this.markInvalid(String.format(this.maxText, this.maxValue));
33878             return false;
33879         }
33880         
33881         return true;
33882     },
33883
33884     getValue : function()
33885     {
33886         var v = this.hiddenEl().getValue();
33887         
33888         return this.fixPrecision(this.parseValue(v));
33889     },
33890
33891     parseValue : function(value)
33892     {
33893         if(this.thousandsDelimiter) {
33894             value += "";
33895             r = new RegExp(",", "g");
33896             value = value.replace(r, "");
33897         }
33898         
33899         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33900         return isNaN(value) ? '' : value;
33901     },
33902
33903     fixPrecision : function(value)
33904     {
33905         if(this.thousandsDelimiter) {
33906             value += "";
33907             r = new RegExp(",", "g");
33908             value = value.replace(r, "");
33909         }
33910         
33911         var nan = isNaN(value);
33912         
33913         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33914             return nan ? '' : value;
33915         }
33916         return parseFloat(value).toFixed(this.decimalPrecision);
33917     },
33918
33919     setValue : function(v)
33920     {
33921         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33922         
33923         this.value = v;
33924         
33925         if(this.rendered){
33926             
33927             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33928             
33929             this.inputEl().dom.value = (v == '') ? '' :
33930                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33931             
33932             if(!this.allowZero && v === '0') {
33933                 this.hiddenEl().dom.value = '';
33934                 this.inputEl().dom.value = '';
33935             }
33936             
33937             this.validate();
33938         }
33939     },
33940
33941     decimalPrecisionFcn : function(v)
33942     {
33943         return Math.floor(v);
33944     },
33945
33946     beforeBlur : function()
33947     {
33948         var v = this.parseValue(this.getRawValue());
33949         
33950         if(v || v === 0 || v === ''){
33951             this.setValue(v);
33952         }
33953     },
33954     
33955     hiddenEl : function()
33956     {
33957         return this.el.select('input.hidden-number-input',true).first();
33958     }
33959     
33960 });
33961
33962  
33963
33964 /*
33965 * Licence: LGPL
33966 */
33967
33968 /**
33969  * @class Roo.bootstrap.DocumentSlider
33970  * @extends Roo.bootstrap.Component
33971  * Bootstrap DocumentSlider class
33972  * 
33973  * @constructor
33974  * Create a new DocumentViewer
33975  * @param {Object} config The config object
33976  */
33977
33978 Roo.bootstrap.DocumentSlider = function(config){
33979     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33980     
33981     this.files = [];
33982     
33983     this.addEvents({
33984         /**
33985          * @event initial
33986          * Fire after initEvent
33987          * @param {Roo.bootstrap.DocumentSlider} this
33988          */
33989         "initial" : true,
33990         /**
33991          * @event update
33992          * Fire after update
33993          * @param {Roo.bootstrap.DocumentSlider} this
33994          */
33995         "update" : true,
33996         /**
33997          * @event click
33998          * Fire after click
33999          * @param {Roo.bootstrap.DocumentSlider} this
34000          */
34001         "click" : true
34002     });
34003 };
34004
34005 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
34006     
34007     files : false,
34008     
34009     indicator : 0,
34010     
34011     getAutoCreate : function()
34012     {
34013         var cfg = {
34014             tag : 'div',
34015             cls : 'roo-document-slider',
34016             cn : [
34017                 {
34018                     tag : 'div',
34019                     cls : 'roo-document-slider-header',
34020                     cn : [
34021                         {
34022                             tag : 'div',
34023                             cls : 'roo-document-slider-header-title'
34024                         }
34025                     ]
34026                 },
34027                 {
34028                     tag : 'div',
34029                     cls : 'roo-document-slider-body',
34030                     cn : [
34031                         {
34032                             tag : 'div',
34033                             cls : 'roo-document-slider-prev',
34034                             cn : [
34035                                 {
34036                                     tag : 'i',
34037                                     cls : 'fa fa-chevron-left'
34038                                 }
34039                             ]
34040                         },
34041                         {
34042                             tag : 'div',
34043                             cls : 'roo-document-slider-thumb',
34044                             cn : [
34045                                 {
34046                                     tag : 'img',
34047                                     cls : 'roo-document-slider-image'
34048                                 }
34049                             ]
34050                         },
34051                         {
34052                             tag : 'div',
34053                             cls : 'roo-document-slider-next',
34054                             cn : [
34055                                 {
34056                                     tag : 'i',
34057                                     cls : 'fa fa-chevron-right'
34058                                 }
34059                             ]
34060                         }
34061                     ]
34062                 }
34063             ]
34064         };
34065         
34066         return cfg;
34067     },
34068     
34069     initEvents : function()
34070     {
34071         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
34072         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
34073         
34074         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
34075         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
34076         
34077         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
34078         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
34079         
34080         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
34081         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
34082         
34083         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
34084         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
34085         
34086         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
34087         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34088         
34089         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
34090         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34091         
34092         this.thumbEl.on('click', this.onClick, this);
34093         
34094         this.prevIndicator.on('click', this.prev, this);
34095         
34096         this.nextIndicator.on('click', this.next, this);
34097         
34098     },
34099     
34100     initial : function()
34101     {
34102         if(this.files.length){
34103             this.indicator = 1;
34104             this.update()
34105         }
34106         
34107         this.fireEvent('initial', this);
34108     },
34109     
34110     update : function()
34111     {
34112         this.imageEl.attr('src', this.files[this.indicator - 1]);
34113         
34114         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
34115         
34116         this.prevIndicator.show();
34117         
34118         if(this.indicator == 1){
34119             this.prevIndicator.hide();
34120         }
34121         
34122         this.nextIndicator.show();
34123         
34124         if(this.indicator == this.files.length){
34125             this.nextIndicator.hide();
34126         }
34127         
34128         this.thumbEl.scrollTo('top');
34129         
34130         this.fireEvent('update', this);
34131     },
34132     
34133     onClick : function(e)
34134     {
34135         e.preventDefault();
34136         
34137         this.fireEvent('click', this);
34138     },
34139     
34140     prev : function(e)
34141     {
34142         e.preventDefault();
34143         
34144         this.indicator = Math.max(1, this.indicator - 1);
34145         
34146         this.update();
34147     },
34148     
34149     next : function(e)
34150     {
34151         e.preventDefault();
34152         
34153         this.indicator = Math.min(this.files.length, this.indicator + 1);
34154         
34155         this.update();
34156     }
34157 });
34158 /*
34159  * - LGPL
34160  *
34161  * RadioSet
34162  *
34163  *
34164  */
34165
34166 /**
34167  * @class Roo.bootstrap.RadioSet
34168  * @extends Roo.bootstrap.Input
34169  * Bootstrap RadioSet class
34170  * @cfg {String} indicatorpos (left|right) default left
34171  * @cfg {Boolean} inline (true|false) inline the element (default true)
34172  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
34173  * @constructor
34174  * Create a new RadioSet
34175  * @param {Object} config The config object
34176  */
34177
34178 Roo.bootstrap.RadioSet = function(config){
34179     
34180     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
34181     
34182     this.radioes = [];
34183     
34184     Roo.bootstrap.RadioSet.register(this);
34185     
34186     this.addEvents({
34187         /**
34188         * @event check
34189         * Fires when the element is checked or unchecked.
34190         * @param {Roo.bootstrap.RadioSet} this This radio
34191         * @param {Roo.bootstrap.Radio} item The checked item
34192         */
34193        check : true,
34194        /**
34195         * @event click
34196         * Fires when the element is click.
34197         * @param {Roo.bootstrap.RadioSet} this This radio set
34198         * @param {Roo.bootstrap.Radio} item The checked item
34199         * @param {Roo.EventObject} e The event object
34200         */
34201        click : true
34202     });
34203     
34204 };
34205
34206 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
34207
34208     radioes : false,
34209     
34210     inline : true,
34211     
34212     weight : '',
34213     
34214     indicatorpos : 'left',
34215     
34216     getAutoCreate : function()
34217     {
34218         var label = {
34219             tag : 'label',
34220             cls : 'roo-radio-set-label',
34221             cn : [
34222                 {
34223                     tag : 'span',
34224                     html : this.fieldLabel
34225                 }
34226             ]
34227         };
34228         if (Roo.bootstrap.version == 3) {
34229             
34230             
34231             if(this.indicatorpos == 'left'){
34232                 label.cn.unshift({
34233                     tag : 'i',
34234                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34235                     tooltip : 'This field is required'
34236                 });
34237             } else {
34238                 label.cn.push({
34239                     tag : 'i',
34240                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34241                     tooltip : 'This field is required'
34242                 });
34243             }
34244         }
34245         var items = {
34246             tag : 'div',
34247             cls : 'roo-radio-set-items'
34248         };
34249         
34250         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34251         
34252         if (align === 'left' && this.fieldLabel.length) {
34253             
34254             items = {
34255                 cls : "roo-radio-set-right", 
34256                 cn: [
34257                     items
34258                 ]
34259             };
34260             
34261             if(this.labelWidth > 12){
34262                 label.style = "width: " + this.labelWidth + 'px';
34263             }
34264             
34265             if(this.labelWidth < 13 && this.labelmd == 0){
34266                 this.labelmd = this.labelWidth;
34267             }
34268             
34269             if(this.labellg > 0){
34270                 label.cls += ' col-lg-' + this.labellg;
34271                 items.cls += ' col-lg-' + (12 - this.labellg);
34272             }
34273             
34274             if(this.labelmd > 0){
34275                 label.cls += ' col-md-' + this.labelmd;
34276                 items.cls += ' col-md-' + (12 - this.labelmd);
34277             }
34278             
34279             if(this.labelsm > 0){
34280                 label.cls += ' col-sm-' + this.labelsm;
34281                 items.cls += ' col-sm-' + (12 - this.labelsm);
34282             }
34283             
34284             if(this.labelxs > 0){
34285                 label.cls += ' col-xs-' + this.labelxs;
34286                 items.cls += ' col-xs-' + (12 - this.labelxs);
34287             }
34288         }
34289         
34290         var cfg = {
34291             tag : 'div',
34292             cls : 'roo-radio-set',
34293             cn : [
34294                 {
34295                     tag : 'input',
34296                     cls : 'roo-radio-set-input',
34297                     type : 'hidden',
34298                     name : this.name,
34299                     value : this.value ? this.value :  ''
34300                 },
34301                 label,
34302                 items
34303             ]
34304         };
34305         
34306         if(this.weight.length){
34307             cfg.cls += ' roo-radio-' + this.weight;
34308         }
34309         
34310         if(this.inline) {
34311             cfg.cls += ' roo-radio-set-inline';
34312         }
34313         
34314         var settings=this;
34315         ['xs','sm','md','lg'].map(function(size){
34316             if (settings[size]) {
34317                 cfg.cls += ' col-' + size + '-' + settings[size];
34318             }
34319         });
34320         
34321         return cfg;
34322         
34323     },
34324
34325     initEvents : function()
34326     {
34327         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34328         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34329         
34330         if(!this.fieldLabel.length){
34331             this.labelEl.hide();
34332         }
34333         
34334         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34335         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34336         
34337         this.indicator = this.indicatorEl();
34338         
34339         if(this.indicator){
34340             this.indicator.addClass('invisible');
34341         }
34342         
34343         this.originalValue = this.getValue();
34344         
34345     },
34346     
34347     inputEl: function ()
34348     {
34349         return this.el.select('.roo-radio-set-input', true).first();
34350     },
34351     
34352     getChildContainer : function()
34353     {
34354         return this.itemsEl;
34355     },
34356     
34357     register : function(item)
34358     {
34359         this.radioes.push(item);
34360         
34361     },
34362     
34363     validate : function()
34364     {   
34365         if(this.getVisibilityEl().hasClass('hidden')){
34366             return true;
34367         }
34368         
34369         var valid = false;
34370         
34371         Roo.each(this.radioes, function(i){
34372             if(!i.checked){
34373                 return;
34374             }
34375             
34376             valid = true;
34377             return false;
34378         });
34379         
34380         if(this.allowBlank) {
34381             return true;
34382         }
34383         
34384         if(this.disabled || valid){
34385             this.markValid();
34386             return true;
34387         }
34388         
34389         this.markInvalid();
34390         return false;
34391         
34392     },
34393     
34394     markValid : function()
34395     {
34396         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34397             this.indicatorEl().removeClass('visible');
34398             this.indicatorEl().addClass('invisible');
34399         }
34400         
34401         
34402         if (Roo.bootstrap.version == 3) {
34403             this.el.removeClass([this.invalidClass, this.validClass]);
34404             this.el.addClass(this.validClass);
34405         } else {
34406             this.el.removeClass(['is-invalid','is-valid']);
34407             this.el.addClass(['is-valid']);
34408         }
34409         this.fireEvent('valid', this);
34410     },
34411     
34412     markInvalid : function(msg)
34413     {
34414         if(this.allowBlank || this.disabled){
34415             return;
34416         }
34417         
34418         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34419             this.indicatorEl().removeClass('invisible');
34420             this.indicatorEl().addClass('visible');
34421         }
34422         if (Roo.bootstrap.version == 3) {
34423             this.el.removeClass([this.invalidClass, this.validClass]);
34424             this.el.addClass(this.invalidClass);
34425         } else {
34426             this.el.removeClass(['is-invalid','is-valid']);
34427             this.el.addClass(['is-invalid']);
34428         }
34429         
34430         this.fireEvent('invalid', this, msg);
34431         
34432     },
34433     
34434     setValue : function(v, suppressEvent)
34435     {   
34436         if(this.value === v){
34437             return;
34438         }
34439         
34440         this.value = v;
34441         
34442         if(this.rendered){
34443             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34444         }
34445         
34446         Roo.each(this.radioes, function(i){
34447             i.checked = false;
34448             i.el.removeClass('checked');
34449         });
34450         
34451         Roo.each(this.radioes, function(i){
34452             
34453             if(i.value === v || i.value.toString() === v.toString()){
34454                 i.checked = true;
34455                 i.el.addClass('checked');
34456                 
34457                 if(suppressEvent !== true){
34458                     this.fireEvent('check', this, i);
34459                 }
34460                 
34461                 return false;
34462             }
34463             
34464         }, this);
34465         
34466         this.validate();
34467     },
34468     
34469     clearInvalid : function(){
34470         
34471         if(!this.el || this.preventMark){
34472             return;
34473         }
34474         
34475         this.el.removeClass([this.invalidClass]);
34476         
34477         this.fireEvent('valid', this);
34478     }
34479     
34480 });
34481
34482 Roo.apply(Roo.bootstrap.RadioSet, {
34483     
34484     groups: {},
34485     
34486     register : function(set)
34487     {
34488         this.groups[set.name] = set;
34489     },
34490     
34491     get: function(name) 
34492     {
34493         if (typeof(this.groups[name]) == 'undefined') {
34494             return false;
34495         }
34496         
34497         return this.groups[name] ;
34498     }
34499     
34500 });
34501 /*
34502  * Based on:
34503  * Ext JS Library 1.1.1
34504  * Copyright(c) 2006-2007, Ext JS, LLC.
34505  *
34506  * Originally Released Under LGPL - original licence link has changed is not relivant.
34507  *
34508  * Fork - LGPL
34509  * <script type="text/javascript">
34510  */
34511
34512
34513 /**
34514  * @class Roo.bootstrap.SplitBar
34515  * @extends Roo.util.Observable
34516  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34517  * <br><br>
34518  * Usage:
34519  * <pre><code>
34520 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34521                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34522 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34523 split.minSize = 100;
34524 split.maxSize = 600;
34525 split.animate = true;
34526 split.on('moved', splitterMoved);
34527 </code></pre>
34528  * @constructor
34529  * Create a new SplitBar
34530  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34531  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34532  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34533  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34534                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34535                         position of the SplitBar).
34536  */
34537 Roo.bootstrap.SplitBar = function(cfg){
34538     
34539     /** @private */
34540     
34541     //{
34542     //  dragElement : elm
34543     //  resizingElement: el,
34544         // optional..
34545     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34546     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34547         // existingProxy ???
34548     //}
34549     
34550     this.el = Roo.get(cfg.dragElement, true);
34551     this.el.dom.unselectable = "on";
34552     /** @private */
34553     this.resizingEl = Roo.get(cfg.resizingElement, true);
34554
34555     /**
34556      * @private
34557      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34558      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34559      * @type Number
34560      */
34561     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34562     
34563     /**
34564      * The minimum size of the resizing element. (Defaults to 0)
34565      * @type Number
34566      */
34567     this.minSize = 0;
34568     
34569     /**
34570      * The maximum size of the resizing element. (Defaults to 2000)
34571      * @type Number
34572      */
34573     this.maxSize = 2000;
34574     
34575     /**
34576      * Whether to animate the transition to the new size
34577      * @type Boolean
34578      */
34579     this.animate = false;
34580     
34581     /**
34582      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34583      * @type Boolean
34584      */
34585     this.useShim = false;
34586     
34587     /** @private */
34588     this.shim = null;
34589     
34590     if(!cfg.existingProxy){
34591         /** @private */
34592         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34593     }else{
34594         this.proxy = Roo.get(cfg.existingProxy).dom;
34595     }
34596     /** @private */
34597     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34598     
34599     /** @private */
34600     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34601     
34602     /** @private */
34603     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34604     
34605     /** @private */
34606     this.dragSpecs = {};
34607     
34608     /**
34609      * @private The adapter to use to positon and resize elements
34610      */
34611     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34612     this.adapter.init(this);
34613     
34614     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34615         /** @private */
34616         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34617         this.el.addClass("roo-splitbar-h");
34618     }else{
34619         /** @private */
34620         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34621         this.el.addClass("roo-splitbar-v");
34622     }
34623     
34624     this.addEvents({
34625         /**
34626          * @event resize
34627          * Fires when the splitter is moved (alias for {@link #event-moved})
34628          * @param {Roo.bootstrap.SplitBar} this
34629          * @param {Number} newSize the new width or height
34630          */
34631         "resize" : true,
34632         /**
34633          * @event moved
34634          * Fires when the splitter is moved
34635          * @param {Roo.bootstrap.SplitBar} this
34636          * @param {Number} newSize the new width or height
34637          */
34638         "moved" : true,
34639         /**
34640          * @event beforeresize
34641          * Fires before the splitter is dragged
34642          * @param {Roo.bootstrap.SplitBar} this
34643          */
34644         "beforeresize" : true,
34645
34646         "beforeapply" : true
34647     });
34648
34649     Roo.util.Observable.call(this);
34650 };
34651
34652 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34653     onStartProxyDrag : function(x, y){
34654         this.fireEvent("beforeresize", this);
34655         if(!this.overlay){
34656             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34657             o.unselectable();
34658             o.enableDisplayMode("block");
34659             // all splitbars share the same overlay
34660             Roo.bootstrap.SplitBar.prototype.overlay = o;
34661         }
34662         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34663         this.overlay.show();
34664         Roo.get(this.proxy).setDisplayed("block");
34665         var size = this.adapter.getElementSize(this);
34666         this.activeMinSize = this.getMinimumSize();;
34667         this.activeMaxSize = this.getMaximumSize();;
34668         var c1 = size - this.activeMinSize;
34669         var c2 = Math.max(this.activeMaxSize - size, 0);
34670         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34671             this.dd.resetConstraints();
34672             this.dd.setXConstraint(
34673                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34674                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34675             );
34676             this.dd.setYConstraint(0, 0);
34677         }else{
34678             this.dd.resetConstraints();
34679             this.dd.setXConstraint(0, 0);
34680             this.dd.setYConstraint(
34681                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34682                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34683             );
34684          }
34685         this.dragSpecs.startSize = size;
34686         this.dragSpecs.startPoint = [x, y];
34687         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34688     },
34689     
34690     /** 
34691      * @private Called after the drag operation by the DDProxy
34692      */
34693     onEndProxyDrag : function(e){
34694         Roo.get(this.proxy).setDisplayed(false);
34695         var endPoint = Roo.lib.Event.getXY(e);
34696         if(this.overlay){
34697             this.overlay.hide();
34698         }
34699         var newSize;
34700         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34701             newSize = this.dragSpecs.startSize + 
34702                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34703                     endPoint[0] - this.dragSpecs.startPoint[0] :
34704                     this.dragSpecs.startPoint[0] - endPoint[0]
34705                 );
34706         }else{
34707             newSize = this.dragSpecs.startSize + 
34708                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34709                     endPoint[1] - this.dragSpecs.startPoint[1] :
34710                     this.dragSpecs.startPoint[1] - endPoint[1]
34711                 );
34712         }
34713         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34714         if(newSize != this.dragSpecs.startSize){
34715             if(this.fireEvent('beforeapply', this, newSize) !== false){
34716                 this.adapter.setElementSize(this, newSize);
34717                 this.fireEvent("moved", this, newSize);
34718                 this.fireEvent("resize", this, newSize);
34719             }
34720         }
34721     },
34722     
34723     /**
34724      * Get the adapter this SplitBar uses
34725      * @return The adapter object
34726      */
34727     getAdapter : function(){
34728         return this.adapter;
34729     },
34730     
34731     /**
34732      * Set the adapter this SplitBar uses
34733      * @param {Object} adapter A SplitBar adapter object
34734      */
34735     setAdapter : function(adapter){
34736         this.adapter = adapter;
34737         this.adapter.init(this);
34738     },
34739     
34740     /**
34741      * Gets the minimum size for the resizing element
34742      * @return {Number} The minimum size
34743      */
34744     getMinimumSize : function(){
34745         return this.minSize;
34746     },
34747     
34748     /**
34749      * Sets the minimum size for the resizing element
34750      * @param {Number} minSize The minimum size
34751      */
34752     setMinimumSize : function(minSize){
34753         this.minSize = minSize;
34754     },
34755     
34756     /**
34757      * Gets the maximum size for the resizing element
34758      * @return {Number} The maximum size
34759      */
34760     getMaximumSize : function(){
34761         return this.maxSize;
34762     },
34763     
34764     /**
34765      * Sets the maximum size for the resizing element
34766      * @param {Number} maxSize The maximum size
34767      */
34768     setMaximumSize : function(maxSize){
34769         this.maxSize = maxSize;
34770     },
34771     
34772     /**
34773      * Sets the initialize size for the resizing element
34774      * @param {Number} size The initial size
34775      */
34776     setCurrentSize : function(size){
34777         var oldAnimate = this.animate;
34778         this.animate = false;
34779         this.adapter.setElementSize(this, size);
34780         this.animate = oldAnimate;
34781     },
34782     
34783     /**
34784      * Destroy this splitbar. 
34785      * @param {Boolean} removeEl True to remove the element
34786      */
34787     destroy : function(removeEl){
34788         if(this.shim){
34789             this.shim.remove();
34790         }
34791         this.dd.unreg();
34792         this.proxy.parentNode.removeChild(this.proxy);
34793         if(removeEl){
34794             this.el.remove();
34795         }
34796     }
34797 });
34798
34799 /**
34800  * @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.
34801  */
34802 Roo.bootstrap.SplitBar.createProxy = function(dir){
34803     var proxy = new Roo.Element(document.createElement("div"));
34804     proxy.unselectable();
34805     var cls = 'roo-splitbar-proxy';
34806     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34807     document.body.appendChild(proxy.dom);
34808     return proxy.dom;
34809 };
34810
34811 /** 
34812  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34813  * Default Adapter. It assumes the splitter and resizing element are not positioned
34814  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34815  */
34816 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34817 };
34818
34819 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34820     // do nothing for now
34821     init : function(s){
34822     
34823     },
34824     /**
34825      * Called before drag operations to get the current size of the resizing element. 
34826      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34827      */
34828      getElementSize : function(s){
34829         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34830             return s.resizingEl.getWidth();
34831         }else{
34832             return s.resizingEl.getHeight();
34833         }
34834     },
34835     
34836     /**
34837      * Called after drag operations to set the size of the resizing element.
34838      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34839      * @param {Number} newSize The new size to set
34840      * @param {Function} onComplete A function to be invoked when resizing is complete
34841      */
34842     setElementSize : function(s, newSize, onComplete){
34843         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34844             if(!s.animate){
34845                 s.resizingEl.setWidth(newSize);
34846                 if(onComplete){
34847                     onComplete(s, newSize);
34848                 }
34849             }else{
34850                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34851             }
34852         }else{
34853             
34854             if(!s.animate){
34855                 s.resizingEl.setHeight(newSize);
34856                 if(onComplete){
34857                     onComplete(s, newSize);
34858                 }
34859             }else{
34860                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34861             }
34862         }
34863     }
34864 };
34865
34866 /** 
34867  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34868  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34869  * Adapter that  moves the splitter element to align with the resized sizing element. 
34870  * Used with an absolute positioned SplitBar.
34871  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34872  * document.body, make sure you assign an id to the body element.
34873  */
34874 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34875     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34876     this.container = Roo.get(container);
34877 };
34878
34879 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34880     init : function(s){
34881         this.basic.init(s);
34882     },
34883     
34884     getElementSize : function(s){
34885         return this.basic.getElementSize(s);
34886     },
34887     
34888     setElementSize : function(s, newSize, onComplete){
34889         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34890     },
34891     
34892     moveSplitter : function(s){
34893         var yes = Roo.bootstrap.SplitBar;
34894         switch(s.placement){
34895             case yes.LEFT:
34896                 s.el.setX(s.resizingEl.getRight());
34897                 break;
34898             case yes.RIGHT:
34899                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34900                 break;
34901             case yes.TOP:
34902                 s.el.setY(s.resizingEl.getBottom());
34903                 break;
34904             case yes.BOTTOM:
34905                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34906                 break;
34907         }
34908     }
34909 };
34910
34911 /**
34912  * Orientation constant - Create a vertical SplitBar
34913  * @static
34914  * @type Number
34915  */
34916 Roo.bootstrap.SplitBar.VERTICAL = 1;
34917
34918 /**
34919  * Orientation constant - Create a horizontal SplitBar
34920  * @static
34921  * @type Number
34922  */
34923 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34924
34925 /**
34926  * Placement constant - The resizing element is to the left of the splitter element
34927  * @static
34928  * @type Number
34929  */
34930 Roo.bootstrap.SplitBar.LEFT = 1;
34931
34932 /**
34933  * Placement constant - The resizing element is to the right of the splitter element
34934  * @static
34935  * @type Number
34936  */
34937 Roo.bootstrap.SplitBar.RIGHT = 2;
34938
34939 /**
34940  * Placement constant - The resizing element is positioned above the splitter element
34941  * @static
34942  * @type Number
34943  */
34944 Roo.bootstrap.SplitBar.TOP = 3;
34945
34946 /**
34947  * Placement constant - The resizing element is positioned under splitter element
34948  * @static
34949  * @type Number
34950  */
34951 Roo.bootstrap.SplitBar.BOTTOM = 4;
34952 Roo.namespace("Roo.bootstrap.layout");/*
34953  * Based on:
34954  * Ext JS Library 1.1.1
34955  * Copyright(c) 2006-2007, Ext JS, LLC.
34956  *
34957  * Originally Released Under LGPL - original licence link has changed is not relivant.
34958  *
34959  * Fork - LGPL
34960  * <script type="text/javascript">
34961  */
34962
34963 /**
34964  * @class Roo.bootstrap.layout.Manager
34965  * @extends Roo.bootstrap.Component
34966  * Base class for layout managers.
34967  */
34968 Roo.bootstrap.layout.Manager = function(config)
34969 {
34970     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34971
34972
34973
34974
34975
34976     /** false to disable window resize monitoring @type Boolean */
34977     this.monitorWindowResize = true;
34978     this.regions = {};
34979     this.addEvents({
34980         /**
34981          * @event layout
34982          * Fires when a layout is performed.
34983          * @param {Roo.LayoutManager} this
34984          */
34985         "layout" : true,
34986         /**
34987          * @event regionresized
34988          * Fires when the user resizes a region.
34989          * @param {Roo.LayoutRegion} region The resized region
34990          * @param {Number} newSize The new size (width for east/west, height for north/south)
34991          */
34992         "regionresized" : true,
34993         /**
34994          * @event regioncollapsed
34995          * Fires when a region is collapsed.
34996          * @param {Roo.LayoutRegion} region The collapsed region
34997          */
34998         "regioncollapsed" : true,
34999         /**
35000          * @event regionexpanded
35001          * Fires when a region is expanded.
35002          * @param {Roo.LayoutRegion} region The expanded region
35003          */
35004         "regionexpanded" : true
35005     });
35006     this.updating = false;
35007
35008     if (config.el) {
35009         this.el = Roo.get(config.el);
35010         this.initEvents();
35011     }
35012
35013 };
35014
35015 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
35016
35017
35018     regions : null,
35019
35020     monitorWindowResize : true,
35021
35022
35023     updating : false,
35024
35025
35026     onRender : function(ct, position)
35027     {
35028         if(!this.el){
35029             this.el = Roo.get(ct);
35030             this.initEvents();
35031         }
35032         //this.fireEvent('render',this);
35033     },
35034
35035
35036     initEvents: function()
35037     {
35038
35039
35040         // ie scrollbar fix
35041         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
35042             document.body.scroll = "no";
35043         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
35044             this.el.position('relative');
35045         }
35046         this.id = this.el.id;
35047         this.el.addClass("roo-layout-container");
35048         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
35049         if(this.el.dom != document.body ) {
35050             this.el.on('resize', this.layout,this);
35051             this.el.on('show', this.layout,this);
35052         }
35053
35054     },
35055
35056     /**
35057      * Returns true if this layout is currently being updated
35058      * @return {Boolean}
35059      */
35060     isUpdating : function(){
35061         return this.updating;
35062     },
35063
35064     /**
35065      * Suspend the LayoutManager from doing auto-layouts while
35066      * making multiple add or remove calls
35067      */
35068     beginUpdate : function(){
35069         this.updating = true;
35070     },
35071
35072     /**
35073      * Restore auto-layouts and optionally disable the manager from performing a layout
35074      * @param {Boolean} noLayout true to disable a layout update
35075      */
35076     endUpdate : function(noLayout){
35077         this.updating = false;
35078         if(!noLayout){
35079             this.layout();
35080         }
35081     },
35082
35083     layout: function(){
35084         // abstract...
35085     },
35086
35087     onRegionResized : function(region, newSize){
35088         this.fireEvent("regionresized", region, newSize);
35089         this.layout();
35090     },
35091
35092     onRegionCollapsed : function(region){
35093         this.fireEvent("regioncollapsed", region);
35094     },
35095
35096     onRegionExpanded : function(region){
35097         this.fireEvent("regionexpanded", region);
35098     },
35099
35100     /**
35101      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
35102      * performs box-model adjustments.
35103      * @return {Object} The size as an object {width: (the width), height: (the height)}
35104      */
35105     getViewSize : function()
35106     {
35107         var size;
35108         if(this.el.dom != document.body){
35109             size = this.el.getSize();
35110         }else{
35111             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
35112         }
35113         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
35114         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35115         return size;
35116     },
35117
35118     /**
35119      * Returns the Element this layout is bound to.
35120      * @return {Roo.Element}
35121      */
35122     getEl : function(){
35123         return this.el;
35124     },
35125
35126     /**
35127      * Returns the specified region.
35128      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
35129      * @return {Roo.LayoutRegion}
35130      */
35131     getRegion : function(target){
35132         return this.regions[target.toLowerCase()];
35133     },
35134
35135     onWindowResize : function(){
35136         if(this.monitorWindowResize){
35137             this.layout();
35138         }
35139     }
35140 });
35141 /*
35142  * Based on:
35143  * Ext JS Library 1.1.1
35144  * Copyright(c) 2006-2007, Ext JS, LLC.
35145  *
35146  * Originally Released Under LGPL - original licence link has changed is not relivant.
35147  *
35148  * Fork - LGPL
35149  * <script type="text/javascript">
35150  */
35151 /**
35152  * @class Roo.bootstrap.layout.Border
35153  * @extends Roo.bootstrap.layout.Manager
35154  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
35155  * please see: examples/bootstrap/nested.html<br><br>
35156  
35157 <b>The container the layout is rendered into can be either the body element or any other element.
35158 If it is not the body element, the container needs to either be an absolute positioned element,
35159 or you will need to add "position:relative" to the css of the container.  You will also need to specify
35160 the container size if it is not the body element.</b>
35161
35162 * @constructor
35163 * Create a new Border
35164 * @param {Object} config Configuration options
35165  */
35166 Roo.bootstrap.layout.Border = function(config){
35167     config = config || {};
35168     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
35169     
35170     
35171     
35172     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35173         if(config[region]){
35174             config[region].region = region;
35175             this.addRegion(config[region]);
35176         }
35177     },this);
35178     
35179 };
35180
35181 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
35182
35183 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
35184     
35185     parent : false, // this might point to a 'nest' or a ???
35186     
35187     /**
35188      * Creates and adds a new region if it doesn't already exist.
35189      * @param {String} target The target region key (north, south, east, west or center).
35190      * @param {Object} config The regions config object
35191      * @return {BorderLayoutRegion} The new region
35192      */
35193     addRegion : function(config)
35194     {
35195         if(!this.regions[config.region]){
35196             var r = this.factory(config);
35197             this.bindRegion(r);
35198         }
35199         return this.regions[config.region];
35200     },
35201
35202     // private (kinda)
35203     bindRegion : function(r){
35204         this.regions[r.config.region] = r;
35205         
35206         r.on("visibilitychange",    this.layout, this);
35207         r.on("paneladded",          this.layout, this);
35208         r.on("panelremoved",        this.layout, this);
35209         r.on("invalidated",         this.layout, this);
35210         r.on("resized",             this.onRegionResized, this);
35211         r.on("collapsed",           this.onRegionCollapsed, this);
35212         r.on("expanded",            this.onRegionExpanded, this);
35213     },
35214
35215     /**
35216      * Performs a layout update.
35217      */
35218     layout : function()
35219     {
35220         if(this.updating) {
35221             return;
35222         }
35223         
35224         // render all the rebions if they have not been done alreayd?
35225         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35226             if(this.regions[region] && !this.regions[region].bodyEl){
35227                 this.regions[region].onRender(this.el)
35228             }
35229         },this);
35230         
35231         var size = this.getViewSize();
35232         var w = size.width;
35233         var h = size.height;
35234         var centerW = w;
35235         var centerH = h;
35236         var centerY = 0;
35237         var centerX = 0;
35238         //var x = 0, y = 0;
35239
35240         var rs = this.regions;
35241         var north = rs["north"];
35242         var south = rs["south"]; 
35243         var west = rs["west"];
35244         var east = rs["east"];
35245         var center = rs["center"];
35246         //if(this.hideOnLayout){ // not supported anymore
35247             //c.el.setStyle("display", "none");
35248         //}
35249         if(north && north.isVisible()){
35250             var b = north.getBox();
35251             var m = north.getMargins();
35252             b.width = w - (m.left+m.right);
35253             b.x = m.left;
35254             b.y = m.top;
35255             centerY = b.height + b.y + m.bottom;
35256             centerH -= centerY;
35257             north.updateBox(this.safeBox(b));
35258         }
35259         if(south && south.isVisible()){
35260             var b = south.getBox();
35261             var m = south.getMargins();
35262             b.width = w - (m.left+m.right);
35263             b.x = m.left;
35264             var totalHeight = (b.height + m.top + m.bottom);
35265             b.y = h - totalHeight + m.top;
35266             centerH -= totalHeight;
35267             south.updateBox(this.safeBox(b));
35268         }
35269         if(west && west.isVisible()){
35270             var b = west.getBox();
35271             var m = west.getMargins();
35272             b.height = centerH - (m.top+m.bottom);
35273             b.x = m.left;
35274             b.y = centerY + m.top;
35275             var totalWidth = (b.width + m.left + m.right);
35276             centerX += totalWidth;
35277             centerW -= totalWidth;
35278             west.updateBox(this.safeBox(b));
35279         }
35280         if(east && east.isVisible()){
35281             var b = east.getBox();
35282             var m = east.getMargins();
35283             b.height = centerH - (m.top+m.bottom);
35284             var totalWidth = (b.width + m.left + m.right);
35285             b.x = w - totalWidth + m.left;
35286             b.y = centerY + m.top;
35287             centerW -= totalWidth;
35288             east.updateBox(this.safeBox(b));
35289         }
35290         if(center){
35291             var m = center.getMargins();
35292             var centerBox = {
35293                 x: centerX + m.left,
35294                 y: centerY + m.top,
35295                 width: centerW - (m.left+m.right),
35296                 height: centerH - (m.top+m.bottom)
35297             };
35298             //if(this.hideOnLayout){
35299                 //center.el.setStyle("display", "block");
35300             //}
35301             center.updateBox(this.safeBox(centerBox));
35302         }
35303         this.el.repaint();
35304         this.fireEvent("layout", this);
35305     },
35306
35307     // private
35308     safeBox : function(box){
35309         box.width = Math.max(0, box.width);
35310         box.height = Math.max(0, box.height);
35311         return box;
35312     },
35313
35314     /**
35315      * Adds a ContentPanel (or subclass) to this layout.
35316      * @param {String} target The target region key (north, south, east, west or center).
35317      * @param {Roo.ContentPanel} panel The panel to add
35318      * @return {Roo.ContentPanel} The added panel
35319      */
35320     add : function(target, panel){
35321          
35322         target = target.toLowerCase();
35323         return this.regions[target].add(panel);
35324     },
35325
35326     /**
35327      * Remove a ContentPanel (or subclass) to this layout.
35328      * @param {String} target The target region key (north, south, east, west or center).
35329      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35330      * @return {Roo.ContentPanel} The removed panel
35331      */
35332     remove : function(target, panel){
35333         target = target.toLowerCase();
35334         return this.regions[target].remove(panel);
35335     },
35336
35337     /**
35338      * Searches all regions for a panel with the specified id
35339      * @param {String} panelId
35340      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35341      */
35342     findPanel : function(panelId){
35343         var rs = this.regions;
35344         for(var target in rs){
35345             if(typeof rs[target] != "function"){
35346                 var p = rs[target].getPanel(panelId);
35347                 if(p){
35348                     return p;
35349                 }
35350             }
35351         }
35352         return null;
35353     },
35354
35355     /**
35356      * Searches all regions for a panel with the specified id and activates (shows) it.
35357      * @param {String/ContentPanel} panelId The panels id or the panel itself
35358      * @return {Roo.ContentPanel} The shown panel or null
35359      */
35360     showPanel : function(panelId) {
35361       var rs = this.regions;
35362       for(var target in rs){
35363          var r = rs[target];
35364          if(typeof r != "function"){
35365             if(r.hasPanel(panelId)){
35366                return r.showPanel(panelId);
35367             }
35368          }
35369       }
35370       return null;
35371    },
35372
35373    /**
35374      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35375      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35376      */
35377    /*
35378     restoreState : function(provider){
35379         if(!provider){
35380             provider = Roo.state.Manager;
35381         }
35382         var sm = new Roo.LayoutStateManager();
35383         sm.init(this, provider);
35384     },
35385 */
35386  
35387  
35388     /**
35389      * Adds a xtype elements to the layout.
35390      * <pre><code>
35391
35392 layout.addxtype({
35393        xtype : 'ContentPanel',
35394        region: 'west',
35395        items: [ .... ]
35396    }
35397 );
35398
35399 layout.addxtype({
35400         xtype : 'NestedLayoutPanel',
35401         region: 'west',
35402         layout: {
35403            center: { },
35404            west: { }   
35405         },
35406         items : [ ... list of content panels or nested layout panels.. ]
35407    }
35408 );
35409 </code></pre>
35410      * @param {Object} cfg Xtype definition of item to add.
35411      */
35412     addxtype : function(cfg)
35413     {
35414         // basically accepts a pannel...
35415         // can accept a layout region..!?!?
35416         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35417         
35418         
35419         // theory?  children can only be panels??
35420         
35421         //if (!cfg.xtype.match(/Panel$/)) {
35422         //    return false;
35423         //}
35424         var ret = false;
35425         
35426         if (typeof(cfg.region) == 'undefined') {
35427             Roo.log("Failed to add Panel, region was not set");
35428             Roo.log(cfg);
35429             return false;
35430         }
35431         var region = cfg.region;
35432         delete cfg.region;
35433         
35434           
35435         var xitems = [];
35436         if (cfg.items) {
35437             xitems = cfg.items;
35438             delete cfg.items;
35439         }
35440         var nb = false;
35441         
35442         if ( region == 'center') {
35443             Roo.log("Center: " + cfg.title);
35444         }
35445         
35446         
35447         switch(cfg.xtype) 
35448         {
35449             case 'Content':  // ContentPanel (el, cfg)
35450             case 'Scroll':  // ContentPanel (el, cfg)
35451             case 'View': 
35452                 cfg.autoCreate = cfg.autoCreate || true;
35453                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35454                 //} else {
35455                 //    var el = this.el.createChild();
35456                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35457                 //}
35458                 
35459                 this.add(region, ret);
35460                 break;
35461             
35462             /*
35463             case 'TreePanel': // our new panel!
35464                 cfg.el = this.el.createChild();
35465                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35466                 this.add(region, ret);
35467                 break;
35468             */
35469             
35470             case 'Nest': 
35471                 // create a new Layout (which is  a Border Layout...
35472                 
35473                 var clayout = cfg.layout;
35474                 clayout.el  = this.el.createChild();
35475                 clayout.items   = clayout.items  || [];
35476                 
35477                 delete cfg.layout;
35478                 
35479                 // replace this exitems with the clayout ones..
35480                 xitems = clayout.items;
35481                  
35482                 // force background off if it's in center...
35483                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35484                     cfg.background = false;
35485                 }
35486                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35487                 
35488                 
35489                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35490                 //console.log('adding nested layout panel '  + cfg.toSource());
35491                 this.add(region, ret);
35492                 nb = {}; /// find first...
35493                 break;
35494             
35495             case 'Grid':
35496                 
35497                 // needs grid and region
35498                 
35499                 //var el = this.getRegion(region).el.createChild();
35500                 /*
35501                  *var el = this.el.createChild();
35502                 // create the grid first...
35503                 cfg.grid.container = el;
35504                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35505                 */
35506                 
35507                 if (region == 'center' && this.active ) {
35508                     cfg.background = false;
35509                 }
35510                 
35511                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35512                 
35513                 this.add(region, ret);
35514                 /*
35515                 if (cfg.background) {
35516                     // render grid on panel activation (if panel background)
35517                     ret.on('activate', function(gp) {
35518                         if (!gp.grid.rendered) {
35519                     //        gp.grid.render(el);
35520                         }
35521                     });
35522                 } else {
35523                   //  cfg.grid.render(el);
35524                 }
35525                 */
35526                 break;
35527            
35528            
35529             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35530                 // it was the old xcomponent building that caused this before.
35531                 // espeically if border is the top element in the tree.
35532                 ret = this;
35533                 break; 
35534                 
35535                     
35536                 
35537                 
35538                 
35539             default:
35540                 /*
35541                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35542                     
35543                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35544                     this.add(region, ret);
35545                 } else {
35546                 */
35547                     Roo.log(cfg);
35548                     throw "Can not add '" + cfg.xtype + "' to Border";
35549                     return null;
35550              
35551                                 
35552              
35553         }
35554         this.beginUpdate();
35555         // add children..
35556         var region = '';
35557         var abn = {};
35558         Roo.each(xitems, function(i)  {
35559             region = nb && i.region ? i.region : false;
35560             
35561             var add = ret.addxtype(i);
35562            
35563             if (region) {
35564                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35565                 if (!i.background) {
35566                     abn[region] = nb[region] ;
35567                 }
35568             }
35569             
35570         });
35571         this.endUpdate();
35572
35573         // make the last non-background panel active..
35574         //if (nb) { Roo.log(abn); }
35575         if (nb) {
35576             
35577             for(var r in abn) {
35578                 region = this.getRegion(r);
35579                 if (region) {
35580                     // tried using nb[r], but it does not work..
35581                      
35582                     region.showPanel(abn[r]);
35583                    
35584                 }
35585             }
35586         }
35587         return ret;
35588         
35589     },
35590     
35591     
35592 // private
35593     factory : function(cfg)
35594     {
35595         
35596         var validRegions = Roo.bootstrap.layout.Border.regions;
35597
35598         var target = cfg.region;
35599         cfg.mgr = this;
35600         
35601         var r = Roo.bootstrap.layout;
35602         Roo.log(target);
35603         switch(target){
35604             case "north":
35605                 return new r.North(cfg);
35606             case "south":
35607                 return new r.South(cfg);
35608             case "east":
35609                 return new r.East(cfg);
35610             case "west":
35611                 return new r.West(cfg);
35612             case "center":
35613                 return new r.Center(cfg);
35614         }
35615         throw 'Layout region "'+target+'" not supported.';
35616     }
35617     
35618     
35619 });
35620  /*
35621  * Based on:
35622  * Ext JS Library 1.1.1
35623  * Copyright(c) 2006-2007, Ext JS, LLC.
35624  *
35625  * Originally Released Under LGPL - original licence link has changed is not relivant.
35626  *
35627  * Fork - LGPL
35628  * <script type="text/javascript">
35629  */
35630  
35631 /**
35632  * @class Roo.bootstrap.layout.Basic
35633  * @extends Roo.util.Observable
35634  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35635  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35636  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35637  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35638  * @cfg {string}   region  the region that it inhabits..
35639  * @cfg {bool}   skipConfig skip config?
35640  * 
35641
35642  */
35643 Roo.bootstrap.layout.Basic = function(config){
35644     
35645     this.mgr = config.mgr;
35646     
35647     this.position = config.region;
35648     
35649     var skipConfig = config.skipConfig;
35650     
35651     this.events = {
35652         /**
35653          * @scope Roo.BasicLayoutRegion
35654          */
35655         
35656         /**
35657          * @event beforeremove
35658          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35659          * @param {Roo.LayoutRegion} this
35660          * @param {Roo.ContentPanel} panel The panel
35661          * @param {Object} e The cancel event object
35662          */
35663         "beforeremove" : true,
35664         /**
35665          * @event invalidated
35666          * Fires when the layout for this region is changed.
35667          * @param {Roo.LayoutRegion} this
35668          */
35669         "invalidated" : true,
35670         /**
35671          * @event visibilitychange
35672          * Fires when this region is shown or hidden 
35673          * @param {Roo.LayoutRegion} this
35674          * @param {Boolean} visibility true or false
35675          */
35676         "visibilitychange" : true,
35677         /**
35678          * @event paneladded
35679          * Fires when a panel is added. 
35680          * @param {Roo.LayoutRegion} this
35681          * @param {Roo.ContentPanel} panel The panel
35682          */
35683         "paneladded" : true,
35684         /**
35685          * @event panelremoved
35686          * Fires when a panel is removed. 
35687          * @param {Roo.LayoutRegion} this
35688          * @param {Roo.ContentPanel} panel The panel
35689          */
35690         "panelremoved" : true,
35691         /**
35692          * @event beforecollapse
35693          * Fires when this region before collapse.
35694          * @param {Roo.LayoutRegion} this
35695          */
35696         "beforecollapse" : true,
35697         /**
35698          * @event collapsed
35699          * Fires when this region is collapsed.
35700          * @param {Roo.LayoutRegion} this
35701          */
35702         "collapsed" : true,
35703         /**
35704          * @event expanded
35705          * Fires when this region is expanded.
35706          * @param {Roo.LayoutRegion} this
35707          */
35708         "expanded" : true,
35709         /**
35710          * @event slideshow
35711          * Fires when this region is slid into view.
35712          * @param {Roo.LayoutRegion} this
35713          */
35714         "slideshow" : true,
35715         /**
35716          * @event slidehide
35717          * Fires when this region slides out of view. 
35718          * @param {Roo.LayoutRegion} this
35719          */
35720         "slidehide" : true,
35721         /**
35722          * @event panelactivated
35723          * Fires when a panel is activated. 
35724          * @param {Roo.LayoutRegion} this
35725          * @param {Roo.ContentPanel} panel The activated panel
35726          */
35727         "panelactivated" : true,
35728         /**
35729          * @event resized
35730          * Fires when the user resizes this region. 
35731          * @param {Roo.LayoutRegion} this
35732          * @param {Number} newSize The new size (width for east/west, height for north/south)
35733          */
35734         "resized" : true
35735     };
35736     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35737     this.panels = new Roo.util.MixedCollection();
35738     this.panels.getKey = this.getPanelId.createDelegate(this);
35739     this.box = null;
35740     this.activePanel = null;
35741     // ensure listeners are added...
35742     
35743     if (config.listeners || config.events) {
35744         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35745             listeners : config.listeners || {},
35746             events : config.events || {}
35747         });
35748     }
35749     
35750     if(skipConfig !== true){
35751         this.applyConfig(config);
35752     }
35753 };
35754
35755 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35756 {
35757     getPanelId : function(p){
35758         return p.getId();
35759     },
35760     
35761     applyConfig : function(config){
35762         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35763         this.config = config;
35764         
35765     },
35766     
35767     /**
35768      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35769      * the width, for horizontal (north, south) the height.
35770      * @param {Number} newSize The new width or height
35771      */
35772     resizeTo : function(newSize){
35773         var el = this.el ? this.el :
35774                  (this.activePanel ? this.activePanel.getEl() : null);
35775         if(el){
35776             switch(this.position){
35777                 case "east":
35778                 case "west":
35779                     el.setWidth(newSize);
35780                     this.fireEvent("resized", this, newSize);
35781                 break;
35782                 case "north":
35783                 case "south":
35784                     el.setHeight(newSize);
35785                     this.fireEvent("resized", this, newSize);
35786                 break;                
35787             }
35788         }
35789     },
35790     
35791     getBox : function(){
35792         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35793     },
35794     
35795     getMargins : function(){
35796         return this.margins;
35797     },
35798     
35799     updateBox : function(box){
35800         this.box = box;
35801         var el = this.activePanel.getEl();
35802         el.dom.style.left = box.x + "px";
35803         el.dom.style.top = box.y + "px";
35804         this.activePanel.setSize(box.width, box.height);
35805     },
35806     
35807     /**
35808      * Returns the container element for this region.
35809      * @return {Roo.Element}
35810      */
35811     getEl : function(){
35812         return this.activePanel;
35813     },
35814     
35815     /**
35816      * Returns true if this region is currently visible.
35817      * @return {Boolean}
35818      */
35819     isVisible : function(){
35820         return this.activePanel ? true : false;
35821     },
35822     
35823     setActivePanel : function(panel){
35824         panel = this.getPanel(panel);
35825         if(this.activePanel && this.activePanel != panel){
35826             this.activePanel.setActiveState(false);
35827             this.activePanel.getEl().setLeftTop(-10000,-10000);
35828         }
35829         this.activePanel = panel;
35830         panel.setActiveState(true);
35831         if(this.box){
35832             panel.setSize(this.box.width, this.box.height);
35833         }
35834         this.fireEvent("panelactivated", this, panel);
35835         this.fireEvent("invalidated");
35836     },
35837     
35838     /**
35839      * Show the specified panel.
35840      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35841      * @return {Roo.ContentPanel} The shown panel or null
35842      */
35843     showPanel : function(panel){
35844         panel = this.getPanel(panel);
35845         if(panel){
35846             this.setActivePanel(panel);
35847         }
35848         return panel;
35849     },
35850     
35851     /**
35852      * Get the active panel for this region.
35853      * @return {Roo.ContentPanel} The active panel or null
35854      */
35855     getActivePanel : function(){
35856         return this.activePanel;
35857     },
35858     
35859     /**
35860      * Add the passed ContentPanel(s)
35861      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35862      * @return {Roo.ContentPanel} The panel added (if only one was added)
35863      */
35864     add : function(panel){
35865         if(arguments.length > 1){
35866             for(var i = 0, len = arguments.length; i < len; i++) {
35867                 this.add(arguments[i]);
35868             }
35869             return null;
35870         }
35871         if(this.hasPanel(panel)){
35872             this.showPanel(panel);
35873             return panel;
35874         }
35875         var el = panel.getEl();
35876         if(el.dom.parentNode != this.mgr.el.dom){
35877             this.mgr.el.dom.appendChild(el.dom);
35878         }
35879         if(panel.setRegion){
35880             panel.setRegion(this);
35881         }
35882         this.panels.add(panel);
35883         el.setStyle("position", "absolute");
35884         if(!panel.background){
35885             this.setActivePanel(panel);
35886             if(this.config.initialSize && this.panels.getCount()==1){
35887                 this.resizeTo(this.config.initialSize);
35888             }
35889         }
35890         this.fireEvent("paneladded", this, panel);
35891         return panel;
35892     },
35893     
35894     /**
35895      * Returns true if the panel is in this region.
35896      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35897      * @return {Boolean}
35898      */
35899     hasPanel : function(panel){
35900         if(typeof panel == "object"){ // must be panel obj
35901             panel = panel.getId();
35902         }
35903         return this.getPanel(panel) ? true : false;
35904     },
35905     
35906     /**
35907      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35908      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35909      * @param {Boolean} preservePanel Overrides the config preservePanel option
35910      * @return {Roo.ContentPanel} The panel that was removed
35911      */
35912     remove : function(panel, preservePanel){
35913         panel = this.getPanel(panel);
35914         if(!panel){
35915             return null;
35916         }
35917         var e = {};
35918         this.fireEvent("beforeremove", this, panel, e);
35919         if(e.cancel === true){
35920             return null;
35921         }
35922         var panelId = panel.getId();
35923         this.panels.removeKey(panelId);
35924         return panel;
35925     },
35926     
35927     /**
35928      * Returns the panel specified or null if it's not in this region.
35929      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35930      * @return {Roo.ContentPanel}
35931      */
35932     getPanel : function(id){
35933         if(typeof id == "object"){ // must be panel obj
35934             return id;
35935         }
35936         return this.panels.get(id);
35937     },
35938     
35939     /**
35940      * Returns this regions position (north/south/east/west/center).
35941      * @return {String} 
35942      */
35943     getPosition: function(){
35944         return this.position;    
35945     }
35946 });/*
35947  * Based on:
35948  * Ext JS Library 1.1.1
35949  * Copyright(c) 2006-2007, Ext JS, LLC.
35950  *
35951  * Originally Released Under LGPL - original licence link has changed is not relivant.
35952  *
35953  * Fork - LGPL
35954  * <script type="text/javascript">
35955  */
35956  
35957 /**
35958  * @class Roo.bootstrap.layout.Region
35959  * @extends Roo.bootstrap.layout.Basic
35960  * This class represents a region in a layout manager.
35961  
35962  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35963  * @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})
35964  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35965  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35966  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35967  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35968  * @cfg {String}    title           The title for the region (overrides panel titles)
35969  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35970  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35971  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35972  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35973  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35974  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35975  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35976  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35977  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35978  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35979
35980  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35981  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35982  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35983  * @cfg {Number}    width           For East/West panels
35984  * @cfg {Number}    height          For North/South panels
35985  * @cfg {Boolean}   split           To show the splitter
35986  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35987  * 
35988  * @cfg {string}   cls             Extra CSS classes to add to region
35989  * 
35990  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35991  * @cfg {string}   region  the region that it inhabits..
35992  *
35993
35994  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35995  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35996
35997  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35998  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35999  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
36000  */
36001 Roo.bootstrap.layout.Region = function(config)
36002 {
36003     this.applyConfig(config);
36004
36005     var mgr = config.mgr;
36006     var pos = config.region;
36007     config.skipConfig = true;
36008     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
36009     
36010     if (mgr.el) {
36011         this.onRender(mgr.el);   
36012     }
36013      
36014     this.visible = true;
36015     this.collapsed = false;
36016     this.unrendered_panels = [];
36017 };
36018
36019 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
36020
36021     position: '', // set by wrapper (eg. north/south etc..)
36022     unrendered_panels : null,  // unrendered panels.
36023     
36024     tabPosition : false,
36025     
36026     mgr: false, // points to 'Border'
36027     
36028     
36029     createBody : function(){
36030         /** This region's body element 
36031         * @type Roo.Element */
36032         this.bodyEl = this.el.createChild({
36033                 tag: "div",
36034                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
36035         });
36036     },
36037
36038     onRender: function(ctr, pos)
36039     {
36040         var dh = Roo.DomHelper;
36041         /** This region's container element 
36042         * @type Roo.Element */
36043         this.el = dh.append(ctr.dom, {
36044                 tag: "div",
36045                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
36046             }, true);
36047         /** This region's title element 
36048         * @type Roo.Element */
36049     
36050         this.titleEl = dh.append(this.el.dom,  {
36051                 tag: "div",
36052                 unselectable: "on",
36053                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
36054                 children:[
36055                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
36056                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
36057                 ]
36058             }, true);
36059         
36060         this.titleEl.enableDisplayMode();
36061         /** This region's title text element 
36062         * @type HTMLElement */
36063         this.titleTextEl = this.titleEl.dom.firstChild;
36064         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
36065         /*
36066         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
36067         this.closeBtn.enableDisplayMode();
36068         this.closeBtn.on("click", this.closeClicked, this);
36069         this.closeBtn.hide();
36070     */
36071         this.createBody(this.config);
36072         if(this.config.hideWhenEmpty){
36073             this.hide();
36074             this.on("paneladded", this.validateVisibility, this);
36075             this.on("panelremoved", this.validateVisibility, this);
36076         }
36077         if(this.autoScroll){
36078             this.bodyEl.setStyle("overflow", "auto");
36079         }else{
36080             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
36081         }
36082         //if(c.titlebar !== false){
36083             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
36084                 this.titleEl.hide();
36085             }else{
36086                 this.titleEl.show();
36087                 if(this.config.title){
36088                     this.titleTextEl.innerHTML = this.config.title;
36089                 }
36090             }
36091         //}
36092         if(this.config.collapsed){
36093             this.collapse(true);
36094         }
36095         if(this.config.hidden){
36096             this.hide();
36097         }
36098         
36099         if (this.unrendered_panels && this.unrendered_panels.length) {
36100             for (var i =0;i< this.unrendered_panels.length; i++) {
36101                 this.add(this.unrendered_panels[i]);
36102             }
36103             this.unrendered_panels = null;
36104             
36105         }
36106         
36107     },
36108     
36109     applyConfig : function(c)
36110     {
36111         /*
36112          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
36113             var dh = Roo.DomHelper;
36114             if(c.titlebar !== false){
36115                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
36116                 this.collapseBtn.on("click", this.collapse, this);
36117                 this.collapseBtn.enableDisplayMode();
36118                 /*
36119                 if(c.showPin === true || this.showPin){
36120                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
36121                     this.stickBtn.enableDisplayMode();
36122                     this.stickBtn.on("click", this.expand, this);
36123                     this.stickBtn.hide();
36124                 }
36125                 
36126             }
36127             */
36128             /** This region's collapsed element
36129             * @type Roo.Element */
36130             /*
36131              *
36132             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
36133                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
36134             ]}, true);
36135             
36136             if(c.floatable !== false){
36137                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
36138                this.collapsedEl.on("click", this.collapseClick, this);
36139             }
36140
36141             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
36142                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
36143                    id: "message", unselectable: "on", style:{"float":"left"}});
36144                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
36145              }
36146             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
36147             this.expandBtn.on("click", this.expand, this);
36148             
36149         }
36150         
36151         if(this.collapseBtn){
36152             this.collapseBtn.setVisible(c.collapsible == true);
36153         }
36154         
36155         this.cmargins = c.cmargins || this.cmargins ||
36156                          (this.position == "west" || this.position == "east" ?
36157                              {top: 0, left: 2, right:2, bottom: 0} :
36158                              {top: 2, left: 0, right:0, bottom: 2});
36159         */
36160         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36161         
36162         
36163         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
36164         
36165         this.autoScroll = c.autoScroll || false;
36166         
36167         
36168        
36169         
36170         this.duration = c.duration || .30;
36171         this.slideDuration = c.slideDuration || .45;
36172         this.config = c;
36173        
36174     },
36175     /**
36176      * Returns true if this region is currently visible.
36177      * @return {Boolean}
36178      */
36179     isVisible : function(){
36180         return this.visible;
36181     },
36182
36183     /**
36184      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
36185      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
36186      */
36187     //setCollapsedTitle : function(title){
36188     //    title = title || "&#160;";
36189      //   if(this.collapsedTitleTextEl){
36190       //      this.collapsedTitleTextEl.innerHTML = title;
36191        // }
36192     //},
36193
36194     getBox : function(){
36195         var b;
36196       //  if(!this.collapsed){
36197             b = this.el.getBox(false, true);
36198        // }else{
36199           //  b = this.collapsedEl.getBox(false, true);
36200         //}
36201         return b;
36202     },
36203
36204     getMargins : function(){
36205         return this.margins;
36206         //return this.collapsed ? this.cmargins : this.margins;
36207     },
36208 /*
36209     highlight : function(){
36210         this.el.addClass("x-layout-panel-dragover");
36211     },
36212
36213     unhighlight : function(){
36214         this.el.removeClass("x-layout-panel-dragover");
36215     },
36216 */
36217     updateBox : function(box)
36218     {
36219         if (!this.bodyEl) {
36220             return; // not rendered yet..
36221         }
36222         
36223         this.box = box;
36224         if(!this.collapsed){
36225             this.el.dom.style.left = box.x + "px";
36226             this.el.dom.style.top = box.y + "px";
36227             this.updateBody(box.width, box.height);
36228         }else{
36229             this.collapsedEl.dom.style.left = box.x + "px";
36230             this.collapsedEl.dom.style.top = box.y + "px";
36231             this.collapsedEl.setSize(box.width, box.height);
36232         }
36233         if(this.tabs){
36234             this.tabs.autoSizeTabs();
36235         }
36236     },
36237
36238     updateBody : function(w, h)
36239     {
36240         if(w !== null){
36241             this.el.setWidth(w);
36242             w -= this.el.getBorderWidth("rl");
36243             if(this.config.adjustments){
36244                 w += this.config.adjustments[0];
36245             }
36246         }
36247         if(h !== null && h > 0){
36248             this.el.setHeight(h);
36249             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36250             h -= this.el.getBorderWidth("tb");
36251             if(this.config.adjustments){
36252                 h += this.config.adjustments[1];
36253             }
36254             this.bodyEl.setHeight(h);
36255             if(this.tabs){
36256                 h = this.tabs.syncHeight(h);
36257             }
36258         }
36259         if(this.panelSize){
36260             w = w !== null ? w : this.panelSize.width;
36261             h = h !== null ? h : this.panelSize.height;
36262         }
36263         if(this.activePanel){
36264             var el = this.activePanel.getEl();
36265             w = w !== null ? w : el.getWidth();
36266             h = h !== null ? h : el.getHeight();
36267             this.panelSize = {width: w, height: h};
36268             this.activePanel.setSize(w, h);
36269         }
36270         if(Roo.isIE && this.tabs){
36271             this.tabs.el.repaint();
36272         }
36273     },
36274
36275     /**
36276      * Returns the container element for this region.
36277      * @return {Roo.Element}
36278      */
36279     getEl : function(){
36280         return this.el;
36281     },
36282
36283     /**
36284      * Hides this region.
36285      */
36286     hide : function(){
36287         //if(!this.collapsed){
36288             this.el.dom.style.left = "-2000px";
36289             this.el.hide();
36290         //}else{
36291          //   this.collapsedEl.dom.style.left = "-2000px";
36292          //   this.collapsedEl.hide();
36293        // }
36294         this.visible = false;
36295         this.fireEvent("visibilitychange", this, false);
36296     },
36297
36298     /**
36299      * Shows this region if it was previously hidden.
36300      */
36301     show : function(){
36302         //if(!this.collapsed){
36303             this.el.show();
36304         //}else{
36305         //    this.collapsedEl.show();
36306        // }
36307         this.visible = true;
36308         this.fireEvent("visibilitychange", this, true);
36309     },
36310 /*
36311     closeClicked : function(){
36312         if(this.activePanel){
36313             this.remove(this.activePanel);
36314         }
36315     },
36316
36317     collapseClick : function(e){
36318         if(this.isSlid){
36319            e.stopPropagation();
36320            this.slideIn();
36321         }else{
36322            e.stopPropagation();
36323            this.slideOut();
36324         }
36325     },
36326 */
36327     /**
36328      * Collapses this region.
36329      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36330      */
36331     /*
36332     collapse : function(skipAnim, skipCheck = false){
36333         if(this.collapsed) {
36334             return;
36335         }
36336         
36337         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36338             
36339             this.collapsed = true;
36340             if(this.split){
36341                 this.split.el.hide();
36342             }
36343             if(this.config.animate && skipAnim !== true){
36344                 this.fireEvent("invalidated", this);
36345                 this.animateCollapse();
36346             }else{
36347                 this.el.setLocation(-20000,-20000);
36348                 this.el.hide();
36349                 this.collapsedEl.show();
36350                 this.fireEvent("collapsed", this);
36351                 this.fireEvent("invalidated", this);
36352             }
36353         }
36354         
36355     },
36356 */
36357     animateCollapse : function(){
36358         // overridden
36359     },
36360
36361     /**
36362      * Expands this region if it was previously collapsed.
36363      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36364      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36365      */
36366     /*
36367     expand : function(e, skipAnim){
36368         if(e) {
36369             e.stopPropagation();
36370         }
36371         if(!this.collapsed || this.el.hasActiveFx()) {
36372             return;
36373         }
36374         if(this.isSlid){
36375             this.afterSlideIn();
36376             skipAnim = true;
36377         }
36378         this.collapsed = false;
36379         if(this.config.animate && skipAnim !== true){
36380             this.animateExpand();
36381         }else{
36382             this.el.show();
36383             if(this.split){
36384                 this.split.el.show();
36385             }
36386             this.collapsedEl.setLocation(-2000,-2000);
36387             this.collapsedEl.hide();
36388             this.fireEvent("invalidated", this);
36389             this.fireEvent("expanded", this);
36390         }
36391     },
36392 */
36393     animateExpand : function(){
36394         // overridden
36395     },
36396
36397     initTabs : function()
36398     {
36399         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36400         
36401         var ts = new Roo.bootstrap.panel.Tabs({
36402             el: this.bodyEl.dom,
36403             region : this,
36404             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
36405             disableTooltips: this.config.disableTabTips,
36406             toolbar : this.config.toolbar
36407         });
36408         
36409         if(this.config.hideTabs){
36410             ts.stripWrap.setDisplayed(false);
36411         }
36412         this.tabs = ts;
36413         ts.resizeTabs = this.config.resizeTabs === true;
36414         ts.minTabWidth = this.config.minTabWidth || 40;
36415         ts.maxTabWidth = this.config.maxTabWidth || 250;
36416         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36417         ts.monitorResize = false;
36418         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36419         ts.bodyEl.addClass('roo-layout-tabs-body');
36420         this.panels.each(this.initPanelAsTab, this);
36421     },
36422
36423     initPanelAsTab : function(panel){
36424         var ti = this.tabs.addTab(
36425             panel.getEl().id,
36426             panel.getTitle(),
36427             null,
36428             this.config.closeOnTab && panel.isClosable(),
36429             panel.tpl
36430         );
36431         if(panel.tabTip !== undefined){
36432             ti.setTooltip(panel.tabTip);
36433         }
36434         ti.on("activate", function(){
36435               this.setActivePanel(panel);
36436         }, this);
36437         
36438         if(this.config.closeOnTab){
36439             ti.on("beforeclose", function(t, e){
36440                 e.cancel = true;
36441                 this.remove(panel);
36442             }, this);
36443         }
36444         
36445         panel.tabItem = ti;
36446         
36447         return ti;
36448     },
36449
36450     updatePanelTitle : function(panel, title)
36451     {
36452         if(this.activePanel == panel){
36453             this.updateTitle(title);
36454         }
36455         if(this.tabs){
36456             var ti = this.tabs.getTab(panel.getEl().id);
36457             ti.setText(title);
36458             if(panel.tabTip !== undefined){
36459                 ti.setTooltip(panel.tabTip);
36460             }
36461         }
36462     },
36463
36464     updateTitle : function(title){
36465         if(this.titleTextEl && !this.config.title){
36466             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36467         }
36468     },
36469
36470     setActivePanel : function(panel)
36471     {
36472         panel = this.getPanel(panel);
36473         if(this.activePanel && this.activePanel != panel){
36474             if(this.activePanel.setActiveState(false) === false){
36475                 return;
36476             }
36477         }
36478         this.activePanel = panel;
36479         panel.setActiveState(true);
36480         if(this.panelSize){
36481             panel.setSize(this.panelSize.width, this.panelSize.height);
36482         }
36483         if(this.closeBtn){
36484             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36485         }
36486         this.updateTitle(panel.getTitle());
36487         if(this.tabs){
36488             this.fireEvent("invalidated", this);
36489         }
36490         this.fireEvent("panelactivated", this, panel);
36491     },
36492
36493     /**
36494      * Shows the specified panel.
36495      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36496      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36497      */
36498     showPanel : function(panel)
36499     {
36500         panel = this.getPanel(panel);
36501         if(panel){
36502             if(this.tabs){
36503                 var tab = this.tabs.getTab(panel.getEl().id);
36504                 if(tab.isHidden()){
36505                     this.tabs.unhideTab(tab.id);
36506                 }
36507                 tab.activate();
36508             }else{
36509                 this.setActivePanel(panel);
36510             }
36511         }
36512         return panel;
36513     },
36514
36515     /**
36516      * Get the active panel for this region.
36517      * @return {Roo.ContentPanel} The active panel or null
36518      */
36519     getActivePanel : function(){
36520         return this.activePanel;
36521     },
36522
36523     validateVisibility : function(){
36524         if(this.panels.getCount() < 1){
36525             this.updateTitle("&#160;");
36526             this.closeBtn.hide();
36527             this.hide();
36528         }else{
36529             if(!this.isVisible()){
36530                 this.show();
36531             }
36532         }
36533     },
36534
36535     /**
36536      * Adds the passed ContentPanel(s) to this region.
36537      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36538      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36539      */
36540     add : function(panel)
36541     {
36542         if(arguments.length > 1){
36543             for(var i = 0, len = arguments.length; i < len; i++) {
36544                 this.add(arguments[i]);
36545             }
36546             return null;
36547         }
36548         
36549         // if we have not been rendered yet, then we can not really do much of this..
36550         if (!this.bodyEl) {
36551             this.unrendered_panels.push(panel);
36552             return panel;
36553         }
36554         
36555         
36556         
36557         
36558         if(this.hasPanel(panel)){
36559             this.showPanel(panel);
36560             return panel;
36561         }
36562         panel.setRegion(this);
36563         this.panels.add(panel);
36564        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36565             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36566             // and hide them... ???
36567             this.bodyEl.dom.appendChild(panel.getEl().dom);
36568             if(panel.background !== true){
36569                 this.setActivePanel(panel);
36570             }
36571             this.fireEvent("paneladded", this, panel);
36572             return panel;
36573         }
36574         */
36575         if(!this.tabs){
36576             this.initTabs();
36577         }else{
36578             this.initPanelAsTab(panel);
36579         }
36580         
36581         
36582         if(panel.background !== true){
36583             this.tabs.activate(panel.getEl().id);
36584         }
36585         this.fireEvent("paneladded", this, panel);
36586         return panel;
36587     },
36588
36589     /**
36590      * Hides the tab for the specified panel.
36591      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36592      */
36593     hidePanel : function(panel){
36594         if(this.tabs && (panel = this.getPanel(panel))){
36595             this.tabs.hideTab(panel.getEl().id);
36596         }
36597     },
36598
36599     /**
36600      * Unhides the tab for a previously hidden panel.
36601      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36602      */
36603     unhidePanel : function(panel){
36604         if(this.tabs && (panel = this.getPanel(panel))){
36605             this.tabs.unhideTab(panel.getEl().id);
36606         }
36607     },
36608
36609     clearPanels : function(){
36610         while(this.panels.getCount() > 0){
36611              this.remove(this.panels.first());
36612         }
36613     },
36614
36615     /**
36616      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36617      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36618      * @param {Boolean} preservePanel Overrides the config preservePanel option
36619      * @return {Roo.ContentPanel} The panel that was removed
36620      */
36621     remove : function(panel, preservePanel)
36622     {
36623         panel = this.getPanel(panel);
36624         if(!panel){
36625             return null;
36626         }
36627         var e = {};
36628         this.fireEvent("beforeremove", this, panel, e);
36629         if(e.cancel === true){
36630             return null;
36631         }
36632         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36633         var panelId = panel.getId();
36634         this.panels.removeKey(panelId);
36635         if(preservePanel){
36636             document.body.appendChild(panel.getEl().dom);
36637         }
36638         if(this.tabs){
36639             this.tabs.removeTab(panel.getEl().id);
36640         }else if (!preservePanel){
36641             this.bodyEl.dom.removeChild(panel.getEl().dom);
36642         }
36643         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36644             var p = this.panels.first();
36645             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36646             tempEl.appendChild(p.getEl().dom);
36647             this.bodyEl.update("");
36648             this.bodyEl.dom.appendChild(p.getEl().dom);
36649             tempEl = null;
36650             this.updateTitle(p.getTitle());
36651             this.tabs = null;
36652             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36653             this.setActivePanel(p);
36654         }
36655         panel.setRegion(null);
36656         if(this.activePanel == panel){
36657             this.activePanel = null;
36658         }
36659         if(this.config.autoDestroy !== false && preservePanel !== true){
36660             try{panel.destroy();}catch(e){}
36661         }
36662         this.fireEvent("panelremoved", this, panel);
36663         return panel;
36664     },
36665
36666     /**
36667      * Returns the TabPanel component used by this region
36668      * @return {Roo.TabPanel}
36669      */
36670     getTabs : function(){
36671         return this.tabs;
36672     },
36673
36674     createTool : function(parentEl, className){
36675         var btn = Roo.DomHelper.append(parentEl, {
36676             tag: "div",
36677             cls: "x-layout-tools-button",
36678             children: [ {
36679                 tag: "div",
36680                 cls: "roo-layout-tools-button-inner " + className,
36681                 html: "&#160;"
36682             }]
36683         }, true);
36684         btn.addClassOnOver("roo-layout-tools-button-over");
36685         return btn;
36686     }
36687 });/*
36688  * Based on:
36689  * Ext JS Library 1.1.1
36690  * Copyright(c) 2006-2007, Ext JS, LLC.
36691  *
36692  * Originally Released Under LGPL - original licence link has changed is not relivant.
36693  *
36694  * Fork - LGPL
36695  * <script type="text/javascript">
36696  */
36697  
36698
36699
36700 /**
36701  * @class Roo.SplitLayoutRegion
36702  * @extends Roo.LayoutRegion
36703  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36704  */
36705 Roo.bootstrap.layout.Split = function(config){
36706     this.cursor = config.cursor;
36707     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36708 };
36709
36710 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36711 {
36712     splitTip : "Drag to resize.",
36713     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36714     useSplitTips : false,
36715
36716     applyConfig : function(config){
36717         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36718     },
36719     
36720     onRender : function(ctr,pos) {
36721         
36722         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36723         if(!this.config.split){
36724             return;
36725         }
36726         if(!this.split){
36727             
36728             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36729                             tag: "div",
36730                             id: this.el.id + "-split",
36731                             cls: "roo-layout-split roo-layout-split-"+this.position,
36732                             html: "&#160;"
36733             });
36734             /** The SplitBar for this region 
36735             * @type Roo.SplitBar */
36736             // does not exist yet...
36737             Roo.log([this.position, this.orientation]);
36738             
36739             this.split = new Roo.bootstrap.SplitBar({
36740                 dragElement : splitEl,
36741                 resizingElement: this.el,
36742                 orientation : this.orientation
36743             });
36744             
36745             this.split.on("moved", this.onSplitMove, this);
36746             this.split.useShim = this.config.useShim === true;
36747             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36748             if(this.useSplitTips){
36749                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36750             }
36751             //if(config.collapsible){
36752             //    this.split.el.on("dblclick", this.collapse,  this);
36753             //}
36754         }
36755         if(typeof this.config.minSize != "undefined"){
36756             this.split.minSize = this.config.minSize;
36757         }
36758         if(typeof this.config.maxSize != "undefined"){
36759             this.split.maxSize = this.config.maxSize;
36760         }
36761         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36762             this.hideSplitter();
36763         }
36764         
36765     },
36766
36767     getHMaxSize : function(){
36768          var cmax = this.config.maxSize || 10000;
36769          var center = this.mgr.getRegion("center");
36770          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36771     },
36772
36773     getVMaxSize : function(){
36774          var cmax = this.config.maxSize || 10000;
36775          var center = this.mgr.getRegion("center");
36776          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36777     },
36778
36779     onSplitMove : function(split, newSize){
36780         this.fireEvent("resized", this, newSize);
36781     },
36782     
36783     /** 
36784      * Returns the {@link Roo.SplitBar} for this region.
36785      * @return {Roo.SplitBar}
36786      */
36787     getSplitBar : function(){
36788         return this.split;
36789     },
36790     
36791     hide : function(){
36792         this.hideSplitter();
36793         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36794     },
36795
36796     hideSplitter : function(){
36797         if(this.split){
36798             this.split.el.setLocation(-2000,-2000);
36799             this.split.el.hide();
36800         }
36801     },
36802
36803     show : function(){
36804         if(this.split){
36805             this.split.el.show();
36806         }
36807         Roo.bootstrap.layout.Split.superclass.show.call(this);
36808     },
36809     
36810     beforeSlide: function(){
36811         if(Roo.isGecko){// firefox overflow auto bug workaround
36812             this.bodyEl.clip();
36813             if(this.tabs) {
36814                 this.tabs.bodyEl.clip();
36815             }
36816             if(this.activePanel){
36817                 this.activePanel.getEl().clip();
36818                 
36819                 if(this.activePanel.beforeSlide){
36820                     this.activePanel.beforeSlide();
36821                 }
36822             }
36823         }
36824     },
36825     
36826     afterSlide : function(){
36827         if(Roo.isGecko){// firefox overflow auto bug workaround
36828             this.bodyEl.unclip();
36829             if(this.tabs) {
36830                 this.tabs.bodyEl.unclip();
36831             }
36832             if(this.activePanel){
36833                 this.activePanel.getEl().unclip();
36834                 if(this.activePanel.afterSlide){
36835                     this.activePanel.afterSlide();
36836                 }
36837             }
36838         }
36839     },
36840
36841     initAutoHide : function(){
36842         if(this.autoHide !== false){
36843             if(!this.autoHideHd){
36844                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36845                 this.autoHideHd = {
36846                     "mouseout": function(e){
36847                         if(!e.within(this.el, true)){
36848                             st.delay(500);
36849                         }
36850                     },
36851                     "mouseover" : function(e){
36852                         st.cancel();
36853                     },
36854                     scope : this
36855                 };
36856             }
36857             this.el.on(this.autoHideHd);
36858         }
36859     },
36860
36861     clearAutoHide : function(){
36862         if(this.autoHide !== false){
36863             this.el.un("mouseout", this.autoHideHd.mouseout);
36864             this.el.un("mouseover", this.autoHideHd.mouseover);
36865         }
36866     },
36867
36868     clearMonitor : function(){
36869         Roo.get(document).un("click", this.slideInIf, this);
36870     },
36871
36872     // these names are backwards but not changed for compat
36873     slideOut : function(){
36874         if(this.isSlid || this.el.hasActiveFx()){
36875             return;
36876         }
36877         this.isSlid = true;
36878         if(this.collapseBtn){
36879             this.collapseBtn.hide();
36880         }
36881         this.closeBtnState = this.closeBtn.getStyle('display');
36882         this.closeBtn.hide();
36883         if(this.stickBtn){
36884             this.stickBtn.show();
36885         }
36886         this.el.show();
36887         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36888         this.beforeSlide();
36889         this.el.setStyle("z-index", 10001);
36890         this.el.slideIn(this.getSlideAnchor(), {
36891             callback: function(){
36892                 this.afterSlide();
36893                 this.initAutoHide();
36894                 Roo.get(document).on("click", this.slideInIf, this);
36895                 this.fireEvent("slideshow", this);
36896             },
36897             scope: this,
36898             block: true
36899         });
36900     },
36901
36902     afterSlideIn : function(){
36903         this.clearAutoHide();
36904         this.isSlid = false;
36905         this.clearMonitor();
36906         this.el.setStyle("z-index", "");
36907         if(this.collapseBtn){
36908             this.collapseBtn.show();
36909         }
36910         this.closeBtn.setStyle('display', this.closeBtnState);
36911         if(this.stickBtn){
36912             this.stickBtn.hide();
36913         }
36914         this.fireEvent("slidehide", this);
36915     },
36916
36917     slideIn : function(cb){
36918         if(!this.isSlid || this.el.hasActiveFx()){
36919             Roo.callback(cb);
36920             return;
36921         }
36922         this.isSlid = false;
36923         this.beforeSlide();
36924         this.el.slideOut(this.getSlideAnchor(), {
36925             callback: function(){
36926                 this.el.setLeftTop(-10000, -10000);
36927                 this.afterSlide();
36928                 this.afterSlideIn();
36929                 Roo.callback(cb);
36930             },
36931             scope: this,
36932             block: true
36933         });
36934     },
36935     
36936     slideInIf : function(e){
36937         if(!e.within(this.el)){
36938             this.slideIn();
36939         }
36940     },
36941
36942     animateCollapse : function(){
36943         this.beforeSlide();
36944         this.el.setStyle("z-index", 20000);
36945         var anchor = this.getSlideAnchor();
36946         this.el.slideOut(anchor, {
36947             callback : function(){
36948                 this.el.setStyle("z-index", "");
36949                 this.collapsedEl.slideIn(anchor, {duration:.3});
36950                 this.afterSlide();
36951                 this.el.setLocation(-10000,-10000);
36952                 this.el.hide();
36953                 this.fireEvent("collapsed", this);
36954             },
36955             scope: this,
36956             block: true
36957         });
36958     },
36959
36960     animateExpand : function(){
36961         this.beforeSlide();
36962         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36963         this.el.setStyle("z-index", 20000);
36964         this.collapsedEl.hide({
36965             duration:.1
36966         });
36967         this.el.slideIn(this.getSlideAnchor(), {
36968             callback : function(){
36969                 this.el.setStyle("z-index", "");
36970                 this.afterSlide();
36971                 if(this.split){
36972                     this.split.el.show();
36973                 }
36974                 this.fireEvent("invalidated", this);
36975                 this.fireEvent("expanded", this);
36976             },
36977             scope: this,
36978             block: true
36979         });
36980     },
36981
36982     anchors : {
36983         "west" : "left",
36984         "east" : "right",
36985         "north" : "top",
36986         "south" : "bottom"
36987     },
36988
36989     sanchors : {
36990         "west" : "l",
36991         "east" : "r",
36992         "north" : "t",
36993         "south" : "b"
36994     },
36995
36996     canchors : {
36997         "west" : "tl-tr",
36998         "east" : "tr-tl",
36999         "north" : "tl-bl",
37000         "south" : "bl-tl"
37001     },
37002
37003     getAnchor : function(){
37004         return this.anchors[this.position];
37005     },
37006
37007     getCollapseAnchor : function(){
37008         return this.canchors[this.position];
37009     },
37010
37011     getSlideAnchor : function(){
37012         return this.sanchors[this.position];
37013     },
37014
37015     getAlignAdj : function(){
37016         var cm = this.cmargins;
37017         switch(this.position){
37018             case "west":
37019                 return [0, 0];
37020             break;
37021             case "east":
37022                 return [0, 0];
37023             break;
37024             case "north":
37025                 return [0, 0];
37026             break;
37027             case "south":
37028                 return [0, 0];
37029             break;
37030         }
37031     },
37032
37033     getExpandAdj : function(){
37034         var c = this.collapsedEl, cm = this.cmargins;
37035         switch(this.position){
37036             case "west":
37037                 return [-(cm.right+c.getWidth()+cm.left), 0];
37038             break;
37039             case "east":
37040                 return [cm.right+c.getWidth()+cm.left, 0];
37041             break;
37042             case "north":
37043                 return [0, -(cm.top+cm.bottom+c.getHeight())];
37044             break;
37045             case "south":
37046                 return [0, cm.top+cm.bottom+c.getHeight()];
37047             break;
37048         }
37049     }
37050 });/*
37051  * Based on:
37052  * Ext JS Library 1.1.1
37053  * Copyright(c) 2006-2007, Ext JS, LLC.
37054  *
37055  * Originally Released Under LGPL - original licence link has changed is not relivant.
37056  *
37057  * Fork - LGPL
37058  * <script type="text/javascript">
37059  */
37060 /*
37061  * These classes are private internal classes
37062  */
37063 Roo.bootstrap.layout.Center = function(config){
37064     config.region = "center";
37065     Roo.bootstrap.layout.Region.call(this, config);
37066     this.visible = true;
37067     this.minWidth = config.minWidth || 20;
37068     this.minHeight = config.minHeight || 20;
37069 };
37070
37071 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
37072     hide : function(){
37073         // center panel can't be hidden
37074     },
37075     
37076     show : function(){
37077         // center panel can't be hidden
37078     },
37079     
37080     getMinWidth: function(){
37081         return this.minWidth;
37082     },
37083     
37084     getMinHeight: function(){
37085         return this.minHeight;
37086     }
37087 });
37088
37089
37090
37091
37092  
37093
37094
37095
37096
37097
37098
37099 Roo.bootstrap.layout.North = function(config)
37100 {
37101     config.region = 'north';
37102     config.cursor = 'n-resize';
37103     
37104     Roo.bootstrap.layout.Split.call(this, config);
37105     
37106     
37107     if(this.split){
37108         this.split.placement = Roo.bootstrap.SplitBar.TOP;
37109         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37110         this.split.el.addClass("roo-layout-split-v");
37111     }
37112     var size = config.initialSize || config.height;
37113     if(typeof size != "undefined"){
37114         this.el.setHeight(size);
37115     }
37116 };
37117 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
37118 {
37119     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37120     
37121     
37122     
37123     getBox : function(){
37124         if(this.collapsed){
37125             return this.collapsedEl.getBox();
37126         }
37127         var box = this.el.getBox();
37128         if(this.split){
37129             box.height += this.split.el.getHeight();
37130         }
37131         return box;
37132     },
37133     
37134     updateBox : function(box){
37135         if(this.split && !this.collapsed){
37136             box.height -= this.split.el.getHeight();
37137             this.split.el.setLeft(box.x);
37138             this.split.el.setTop(box.y+box.height);
37139             this.split.el.setWidth(box.width);
37140         }
37141         if(this.collapsed){
37142             this.updateBody(box.width, null);
37143         }
37144         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37145     }
37146 });
37147
37148
37149
37150
37151
37152 Roo.bootstrap.layout.South = function(config){
37153     config.region = 'south';
37154     config.cursor = 's-resize';
37155     Roo.bootstrap.layout.Split.call(this, config);
37156     if(this.split){
37157         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
37158         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37159         this.split.el.addClass("roo-layout-split-v");
37160     }
37161     var size = config.initialSize || config.height;
37162     if(typeof size != "undefined"){
37163         this.el.setHeight(size);
37164     }
37165 };
37166
37167 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
37168     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37169     getBox : function(){
37170         if(this.collapsed){
37171             return this.collapsedEl.getBox();
37172         }
37173         var box = this.el.getBox();
37174         if(this.split){
37175             var sh = this.split.el.getHeight();
37176             box.height += sh;
37177             box.y -= sh;
37178         }
37179         return box;
37180     },
37181     
37182     updateBox : function(box){
37183         if(this.split && !this.collapsed){
37184             var sh = this.split.el.getHeight();
37185             box.height -= sh;
37186             box.y += sh;
37187             this.split.el.setLeft(box.x);
37188             this.split.el.setTop(box.y-sh);
37189             this.split.el.setWidth(box.width);
37190         }
37191         if(this.collapsed){
37192             this.updateBody(box.width, null);
37193         }
37194         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37195     }
37196 });
37197
37198 Roo.bootstrap.layout.East = function(config){
37199     config.region = "east";
37200     config.cursor = "e-resize";
37201     Roo.bootstrap.layout.Split.call(this, config);
37202     if(this.split){
37203         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
37204         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37205         this.split.el.addClass("roo-layout-split-h");
37206     }
37207     var size = config.initialSize || config.width;
37208     if(typeof size != "undefined"){
37209         this.el.setWidth(size);
37210     }
37211 };
37212 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
37213     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37214     getBox : function(){
37215         if(this.collapsed){
37216             return this.collapsedEl.getBox();
37217         }
37218         var box = this.el.getBox();
37219         if(this.split){
37220             var sw = this.split.el.getWidth();
37221             box.width += sw;
37222             box.x -= sw;
37223         }
37224         return box;
37225     },
37226
37227     updateBox : function(box){
37228         if(this.split && !this.collapsed){
37229             var sw = this.split.el.getWidth();
37230             box.width -= sw;
37231             this.split.el.setLeft(box.x);
37232             this.split.el.setTop(box.y);
37233             this.split.el.setHeight(box.height);
37234             box.x += sw;
37235         }
37236         if(this.collapsed){
37237             this.updateBody(null, box.height);
37238         }
37239         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37240     }
37241 });
37242
37243 Roo.bootstrap.layout.West = function(config){
37244     config.region = "west";
37245     config.cursor = "w-resize";
37246     
37247     Roo.bootstrap.layout.Split.call(this, config);
37248     if(this.split){
37249         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37250         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37251         this.split.el.addClass("roo-layout-split-h");
37252     }
37253     
37254 };
37255 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37256     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37257     
37258     onRender: function(ctr, pos)
37259     {
37260         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37261         var size = this.config.initialSize || this.config.width;
37262         if(typeof size != "undefined"){
37263             this.el.setWidth(size);
37264         }
37265     },
37266     
37267     getBox : function(){
37268         if(this.collapsed){
37269             return this.collapsedEl.getBox();
37270         }
37271         var box = this.el.getBox();
37272         if(this.split){
37273             box.width += this.split.el.getWidth();
37274         }
37275         return box;
37276     },
37277     
37278     updateBox : function(box){
37279         if(this.split && !this.collapsed){
37280             var sw = this.split.el.getWidth();
37281             box.width -= sw;
37282             this.split.el.setLeft(box.x+box.width);
37283             this.split.el.setTop(box.y);
37284             this.split.el.setHeight(box.height);
37285         }
37286         if(this.collapsed){
37287             this.updateBody(null, box.height);
37288         }
37289         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37290     }
37291 });Roo.namespace("Roo.bootstrap.panel");/*
37292  * Based on:
37293  * Ext JS Library 1.1.1
37294  * Copyright(c) 2006-2007, Ext JS, LLC.
37295  *
37296  * Originally Released Under LGPL - original licence link has changed is not relivant.
37297  *
37298  * Fork - LGPL
37299  * <script type="text/javascript">
37300  */
37301 /**
37302  * @class Roo.ContentPanel
37303  * @extends Roo.util.Observable
37304  * A basic ContentPanel element.
37305  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
37306  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
37307  * @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
37308  * @cfg {Boolean}   closable      True if the panel can be closed/removed
37309  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
37310  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37311  * @cfg {Toolbar}   toolbar       A toolbar for this panel
37312  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
37313  * @cfg {String} title          The title for this panel
37314  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37315  * @cfg {String} url            Calls {@link #setUrl} with this value
37316  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37317  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37318  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37319  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37320  * @cfg {Boolean} badges render the badges
37321
37322  * @constructor
37323  * Create a new ContentPanel.
37324  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37325  * @param {String/Object} config A string to set only the title or a config object
37326  * @param {String} content (optional) Set the HTML content for this panel
37327  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37328  */
37329 Roo.bootstrap.panel.Content = function( config){
37330     
37331     this.tpl = config.tpl || false;
37332     
37333     var el = config.el;
37334     var content = config.content;
37335
37336     if(config.autoCreate){ // xtype is available if this is called from factory
37337         el = Roo.id();
37338     }
37339     this.el = Roo.get(el);
37340     if(!this.el && config && config.autoCreate){
37341         if(typeof config.autoCreate == "object"){
37342             if(!config.autoCreate.id){
37343                 config.autoCreate.id = config.id||el;
37344             }
37345             this.el = Roo.DomHelper.append(document.body,
37346                         config.autoCreate, true);
37347         }else{
37348             var elcfg =  {   tag: "div",
37349                             cls: "roo-layout-inactive-content",
37350                             id: config.id||el
37351                             };
37352             if (config.html) {
37353                 elcfg.html = config.html;
37354                 
37355             }
37356                         
37357             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37358         }
37359     } 
37360     this.closable = false;
37361     this.loaded = false;
37362     this.active = false;
37363    
37364       
37365     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37366         
37367         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37368         
37369         this.wrapEl = this.el; //this.el.wrap();
37370         var ti = [];
37371         if (config.toolbar.items) {
37372             ti = config.toolbar.items ;
37373             delete config.toolbar.items ;
37374         }
37375         
37376         var nitems = [];
37377         this.toolbar.render(this.wrapEl, 'before');
37378         for(var i =0;i < ti.length;i++) {
37379           //  Roo.log(['add child', items[i]]);
37380             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37381         }
37382         this.toolbar.items = nitems;
37383         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37384         delete config.toolbar;
37385         
37386     }
37387     /*
37388     // xtype created footer. - not sure if will work as we normally have to render first..
37389     if (this.footer && !this.footer.el && this.footer.xtype) {
37390         if (!this.wrapEl) {
37391             this.wrapEl = this.el.wrap();
37392         }
37393     
37394         this.footer.container = this.wrapEl.createChild();
37395          
37396         this.footer = Roo.factory(this.footer, Roo);
37397         
37398     }
37399     */
37400     
37401      if(typeof config == "string"){
37402         this.title = config;
37403     }else{
37404         Roo.apply(this, config);
37405     }
37406     
37407     if(this.resizeEl){
37408         this.resizeEl = Roo.get(this.resizeEl, true);
37409     }else{
37410         this.resizeEl = this.el;
37411     }
37412     // handle view.xtype
37413     
37414  
37415     
37416     
37417     this.addEvents({
37418         /**
37419          * @event activate
37420          * Fires when this panel is activated. 
37421          * @param {Roo.ContentPanel} this
37422          */
37423         "activate" : true,
37424         /**
37425          * @event deactivate
37426          * Fires when this panel is activated. 
37427          * @param {Roo.ContentPanel} this
37428          */
37429         "deactivate" : true,
37430
37431         /**
37432          * @event resize
37433          * Fires when this panel is resized if fitToFrame is true.
37434          * @param {Roo.ContentPanel} this
37435          * @param {Number} width The width after any component adjustments
37436          * @param {Number} height The height after any component adjustments
37437          */
37438         "resize" : true,
37439         
37440          /**
37441          * @event render
37442          * Fires when this tab is created
37443          * @param {Roo.ContentPanel} this
37444          */
37445         "render" : true
37446         
37447         
37448         
37449     });
37450     
37451
37452     
37453     
37454     if(this.autoScroll){
37455         this.resizeEl.setStyle("overflow", "auto");
37456     } else {
37457         // fix randome scrolling
37458         //this.el.on('scroll', function() {
37459         //    Roo.log('fix random scolling');
37460         //    this.scrollTo('top',0); 
37461         //});
37462     }
37463     content = content || this.content;
37464     if(content){
37465         this.setContent(content);
37466     }
37467     if(config && config.url){
37468         this.setUrl(this.url, this.params, this.loadOnce);
37469     }
37470     
37471     
37472     
37473     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37474     
37475     if (this.view && typeof(this.view.xtype) != 'undefined') {
37476         this.view.el = this.el.appendChild(document.createElement("div"));
37477         this.view = Roo.factory(this.view); 
37478         this.view.render  &&  this.view.render(false, '');  
37479     }
37480     
37481     
37482     this.fireEvent('render', this);
37483 };
37484
37485 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37486     
37487     tabTip : '',
37488     
37489     setRegion : function(region){
37490         this.region = region;
37491         this.setActiveClass(region && !this.background);
37492     },
37493     
37494     
37495     setActiveClass: function(state)
37496     {
37497         if(state){
37498            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37499            this.el.setStyle('position','relative');
37500         }else{
37501            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37502            this.el.setStyle('position', 'absolute');
37503         } 
37504     },
37505     
37506     /**
37507      * Returns the toolbar for this Panel if one was configured. 
37508      * @return {Roo.Toolbar} 
37509      */
37510     getToolbar : function(){
37511         return this.toolbar;
37512     },
37513     
37514     setActiveState : function(active)
37515     {
37516         this.active = active;
37517         this.setActiveClass(active);
37518         if(!active){
37519             if(this.fireEvent("deactivate", this) === false){
37520                 return false;
37521             }
37522             return true;
37523         }
37524         this.fireEvent("activate", this);
37525         return true;
37526     },
37527     /**
37528      * Updates this panel's element
37529      * @param {String} content The new content
37530      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37531     */
37532     setContent : function(content, loadScripts){
37533         this.el.update(content, loadScripts);
37534     },
37535
37536     ignoreResize : function(w, h){
37537         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37538             return true;
37539         }else{
37540             this.lastSize = {width: w, height: h};
37541             return false;
37542         }
37543     },
37544     /**
37545      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37546      * @return {Roo.UpdateManager} The UpdateManager
37547      */
37548     getUpdateManager : function(){
37549         return this.el.getUpdateManager();
37550     },
37551      /**
37552      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37553      * @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:
37554 <pre><code>
37555 panel.load({
37556     url: "your-url.php",
37557     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37558     callback: yourFunction,
37559     scope: yourObject, //(optional scope)
37560     discardUrl: false,
37561     nocache: false,
37562     text: "Loading...",
37563     timeout: 30,
37564     scripts: false
37565 });
37566 </code></pre>
37567      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37568      * 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.
37569      * @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}
37570      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37571      * @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.
37572      * @return {Roo.ContentPanel} this
37573      */
37574     load : function(){
37575         var um = this.el.getUpdateManager();
37576         um.update.apply(um, arguments);
37577         return this;
37578     },
37579
37580
37581     /**
37582      * 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.
37583      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37584      * @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)
37585      * @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)
37586      * @return {Roo.UpdateManager} The UpdateManager
37587      */
37588     setUrl : function(url, params, loadOnce){
37589         if(this.refreshDelegate){
37590             this.removeListener("activate", this.refreshDelegate);
37591         }
37592         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37593         this.on("activate", this.refreshDelegate);
37594         return this.el.getUpdateManager();
37595     },
37596     
37597     _handleRefresh : function(url, params, loadOnce){
37598         if(!loadOnce || !this.loaded){
37599             var updater = this.el.getUpdateManager();
37600             updater.update(url, params, this._setLoaded.createDelegate(this));
37601         }
37602     },
37603     
37604     _setLoaded : function(){
37605         this.loaded = true;
37606     }, 
37607     
37608     /**
37609      * Returns this panel's id
37610      * @return {String} 
37611      */
37612     getId : function(){
37613         return this.el.id;
37614     },
37615     
37616     /** 
37617      * Returns this panel's element - used by regiosn to add.
37618      * @return {Roo.Element} 
37619      */
37620     getEl : function(){
37621         return this.wrapEl || this.el;
37622     },
37623     
37624    
37625     
37626     adjustForComponents : function(width, height)
37627     {
37628         //Roo.log('adjustForComponents ');
37629         if(this.resizeEl != this.el){
37630             width -= this.el.getFrameWidth('lr');
37631             height -= this.el.getFrameWidth('tb');
37632         }
37633         if(this.toolbar){
37634             var te = this.toolbar.getEl();
37635             te.setWidth(width);
37636             height -= te.getHeight();
37637         }
37638         if(this.footer){
37639             var te = this.footer.getEl();
37640             te.setWidth(width);
37641             height -= te.getHeight();
37642         }
37643         
37644         
37645         if(this.adjustments){
37646             width += this.adjustments[0];
37647             height += this.adjustments[1];
37648         }
37649         return {"width": width, "height": height};
37650     },
37651     
37652     setSize : function(width, height){
37653         if(this.fitToFrame && !this.ignoreResize(width, height)){
37654             if(this.fitContainer && this.resizeEl != this.el){
37655                 this.el.setSize(width, height);
37656             }
37657             var size = this.adjustForComponents(width, height);
37658             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37659             this.fireEvent('resize', this, size.width, size.height);
37660         }
37661     },
37662     
37663     /**
37664      * Returns this panel's title
37665      * @return {String} 
37666      */
37667     getTitle : function(){
37668         
37669         if (typeof(this.title) != 'object') {
37670             return this.title;
37671         }
37672         
37673         var t = '';
37674         for (var k in this.title) {
37675             if (!this.title.hasOwnProperty(k)) {
37676                 continue;
37677             }
37678             
37679             if (k.indexOf('-') >= 0) {
37680                 var s = k.split('-');
37681                 for (var i = 0; i<s.length; i++) {
37682                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37683                 }
37684             } else {
37685                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37686             }
37687         }
37688         return t;
37689     },
37690     
37691     /**
37692      * Set this panel's title
37693      * @param {String} title
37694      */
37695     setTitle : function(title){
37696         this.title = title;
37697         if(this.region){
37698             this.region.updatePanelTitle(this, title);
37699         }
37700     },
37701     
37702     /**
37703      * Returns true is this panel was configured to be closable
37704      * @return {Boolean} 
37705      */
37706     isClosable : function(){
37707         return this.closable;
37708     },
37709     
37710     beforeSlide : function(){
37711         this.el.clip();
37712         this.resizeEl.clip();
37713     },
37714     
37715     afterSlide : function(){
37716         this.el.unclip();
37717         this.resizeEl.unclip();
37718     },
37719     
37720     /**
37721      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37722      *   Will fail silently if the {@link #setUrl} method has not been called.
37723      *   This does not activate the panel, just updates its content.
37724      */
37725     refresh : function(){
37726         if(this.refreshDelegate){
37727            this.loaded = false;
37728            this.refreshDelegate();
37729         }
37730     },
37731     
37732     /**
37733      * Destroys this panel
37734      */
37735     destroy : function(){
37736         this.el.removeAllListeners();
37737         var tempEl = document.createElement("span");
37738         tempEl.appendChild(this.el.dom);
37739         tempEl.innerHTML = "";
37740         this.el.remove();
37741         this.el = null;
37742     },
37743     
37744     /**
37745      * form - if the content panel contains a form - this is a reference to it.
37746      * @type {Roo.form.Form}
37747      */
37748     form : false,
37749     /**
37750      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37751      *    This contains a reference to it.
37752      * @type {Roo.View}
37753      */
37754     view : false,
37755     
37756       /**
37757      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37758      * <pre><code>
37759
37760 layout.addxtype({
37761        xtype : 'Form',
37762        items: [ .... ]
37763    }
37764 );
37765
37766 </code></pre>
37767      * @param {Object} cfg Xtype definition of item to add.
37768      */
37769     
37770     
37771     getChildContainer: function () {
37772         return this.getEl();
37773     }
37774     
37775     
37776     /*
37777         var  ret = new Roo.factory(cfg);
37778         return ret;
37779         
37780         
37781         // add form..
37782         if (cfg.xtype.match(/^Form$/)) {
37783             
37784             var el;
37785             //if (this.footer) {
37786             //    el = this.footer.container.insertSibling(false, 'before');
37787             //} else {
37788                 el = this.el.createChild();
37789             //}
37790
37791             this.form = new  Roo.form.Form(cfg);
37792             
37793             
37794             if ( this.form.allItems.length) {
37795                 this.form.render(el.dom);
37796             }
37797             return this.form;
37798         }
37799         // should only have one of theses..
37800         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37801             // views.. should not be just added - used named prop 'view''
37802             
37803             cfg.el = this.el.appendChild(document.createElement("div"));
37804             // factory?
37805             
37806             var ret = new Roo.factory(cfg);
37807              
37808              ret.render && ret.render(false, ''); // render blank..
37809             this.view = ret;
37810             return ret;
37811         }
37812         return false;
37813     }
37814     \*/
37815 });
37816  
37817 /**
37818  * @class Roo.bootstrap.panel.Grid
37819  * @extends Roo.bootstrap.panel.Content
37820  * @constructor
37821  * Create a new GridPanel.
37822  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37823  * @param {Object} config A the config object
37824   
37825  */
37826
37827
37828
37829 Roo.bootstrap.panel.Grid = function(config)
37830 {
37831     
37832       
37833     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37834         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37835
37836     config.el = this.wrapper;
37837     //this.el = this.wrapper;
37838     
37839       if (config.container) {
37840         // ctor'ed from a Border/panel.grid
37841         
37842         
37843         this.wrapper.setStyle("overflow", "hidden");
37844         this.wrapper.addClass('roo-grid-container');
37845
37846     }
37847     
37848     
37849     if(config.toolbar){
37850         var tool_el = this.wrapper.createChild();    
37851         this.toolbar = Roo.factory(config.toolbar);
37852         var ti = [];
37853         if (config.toolbar.items) {
37854             ti = config.toolbar.items ;
37855             delete config.toolbar.items ;
37856         }
37857         
37858         var nitems = [];
37859         this.toolbar.render(tool_el);
37860         for(var i =0;i < ti.length;i++) {
37861           //  Roo.log(['add child', items[i]]);
37862             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37863         }
37864         this.toolbar.items = nitems;
37865         
37866         delete config.toolbar;
37867     }
37868     
37869     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37870     config.grid.scrollBody = true;;
37871     config.grid.monitorWindowResize = false; // turn off autosizing
37872     config.grid.autoHeight = false;
37873     config.grid.autoWidth = false;
37874     
37875     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37876     
37877     if (config.background) {
37878         // render grid on panel activation (if panel background)
37879         this.on('activate', function(gp) {
37880             if (!gp.grid.rendered) {
37881                 gp.grid.render(this.wrapper);
37882                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37883             }
37884         });
37885             
37886     } else {
37887         this.grid.render(this.wrapper);
37888         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37889
37890     }
37891     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37892     // ??? needed ??? config.el = this.wrapper;
37893     
37894     
37895     
37896   
37897     // xtype created footer. - not sure if will work as we normally have to render first..
37898     if (this.footer && !this.footer.el && this.footer.xtype) {
37899         
37900         var ctr = this.grid.getView().getFooterPanel(true);
37901         this.footer.dataSource = this.grid.dataSource;
37902         this.footer = Roo.factory(this.footer, Roo);
37903         this.footer.render(ctr);
37904         
37905     }
37906     
37907     
37908     
37909     
37910      
37911 };
37912
37913 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37914     getId : function(){
37915         return this.grid.id;
37916     },
37917     
37918     /**
37919      * Returns the grid for this panel
37920      * @return {Roo.bootstrap.Table} 
37921      */
37922     getGrid : function(){
37923         return this.grid;    
37924     },
37925     
37926     setSize : function(width, height){
37927         if(!this.ignoreResize(width, height)){
37928             var grid = this.grid;
37929             var size = this.adjustForComponents(width, height);
37930             var gridel = grid.getGridEl();
37931             gridel.setSize(size.width, size.height);
37932             /*
37933             var thd = grid.getGridEl().select('thead',true).first();
37934             var tbd = grid.getGridEl().select('tbody', true).first();
37935             if (tbd) {
37936                 tbd.setSize(width, height - thd.getHeight());
37937             }
37938             */
37939             grid.autoSize();
37940         }
37941     },
37942      
37943     
37944     
37945     beforeSlide : function(){
37946         this.grid.getView().scroller.clip();
37947     },
37948     
37949     afterSlide : function(){
37950         this.grid.getView().scroller.unclip();
37951     },
37952     
37953     destroy : function(){
37954         this.grid.destroy();
37955         delete this.grid;
37956         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37957     }
37958 });
37959
37960 /**
37961  * @class Roo.bootstrap.panel.Nest
37962  * @extends Roo.bootstrap.panel.Content
37963  * @constructor
37964  * Create a new Panel, that can contain a layout.Border.
37965  * 
37966  * 
37967  * @param {Roo.BorderLayout} layout The layout for this panel
37968  * @param {String/Object} config A string to set only the title or a config object
37969  */
37970 Roo.bootstrap.panel.Nest = function(config)
37971 {
37972     // construct with only one argument..
37973     /* FIXME - implement nicer consturctors
37974     if (layout.layout) {
37975         config = layout;
37976         layout = config.layout;
37977         delete config.layout;
37978     }
37979     if (layout.xtype && !layout.getEl) {
37980         // then layout needs constructing..
37981         layout = Roo.factory(layout, Roo);
37982     }
37983     */
37984     
37985     config.el =  config.layout.getEl();
37986     
37987     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37988     
37989     config.layout.monitorWindowResize = false; // turn off autosizing
37990     this.layout = config.layout;
37991     this.layout.getEl().addClass("roo-layout-nested-layout");
37992     this.layout.parent = this;
37993     
37994     
37995     
37996     
37997 };
37998
37999 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
38000
38001     setSize : function(width, height){
38002         if(!this.ignoreResize(width, height)){
38003             var size = this.adjustForComponents(width, height);
38004             var el = this.layout.getEl();
38005             if (size.height < 1) {
38006                 el.setWidth(size.width);   
38007             } else {
38008                 el.setSize(size.width, size.height);
38009             }
38010             var touch = el.dom.offsetWidth;
38011             this.layout.layout();
38012             // ie requires a double layout on the first pass
38013             if(Roo.isIE && !this.initialized){
38014                 this.initialized = true;
38015                 this.layout.layout();
38016             }
38017         }
38018     },
38019     
38020     // activate all subpanels if not currently active..
38021     
38022     setActiveState : function(active){
38023         this.active = active;
38024         this.setActiveClass(active);
38025         
38026         if(!active){
38027             this.fireEvent("deactivate", this);
38028             return;
38029         }
38030         
38031         this.fireEvent("activate", this);
38032         // not sure if this should happen before or after..
38033         if (!this.layout) {
38034             return; // should not happen..
38035         }
38036         var reg = false;
38037         for (var r in this.layout.regions) {
38038             reg = this.layout.getRegion(r);
38039             if (reg.getActivePanel()) {
38040                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
38041                 reg.setActivePanel(reg.getActivePanel());
38042                 continue;
38043             }
38044             if (!reg.panels.length) {
38045                 continue;
38046             }
38047             reg.showPanel(reg.getPanel(0));
38048         }
38049         
38050         
38051         
38052         
38053     },
38054     
38055     /**
38056      * Returns the nested BorderLayout for this panel
38057      * @return {Roo.BorderLayout} 
38058      */
38059     getLayout : function(){
38060         return this.layout;
38061     },
38062     
38063      /**
38064      * Adds a xtype elements to the layout of the nested panel
38065      * <pre><code>
38066
38067 panel.addxtype({
38068        xtype : 'ContentPanel',
38069        region: 'west',
38070        items: [ .... ]
38071    }
38072 );
38073
38074 panel.addxtype({
38075         xtype : 'NestedLayoutPanel',
38076         region: 'west',
38077         layout: {
38078            center: { },
38079            west: { }   
38080         },
38081         items : [ ... list of content panels or nested layout panels.. ]
38082    }
38083 );
38084 </code></pre>
38085      * @param {Object} cfg Xtype definition of item to add.
38086      */
38087     addxtype : function(cfg) {
38088         return this.layout.addxtype(cfg);
38089     
38090     }
38091 });/*
38092  * Based on:
38093  * Ext JS Library 1.1.1
38094  * Copyright(c) 2006-2007, Ext JS, LLC.
38095  *
38096  * Originally Released Under LGPL - original licence link has changed is not relivant.
38097  *
38098  * Fork - LGPL
38099  * <script type="text/javascript">
38100  */
38101 /**
38102  * @class Roo.TabPanel
38103  * @extends Roo.util.Observable
38104  * A lightweight tab container.
38105  * <br><br>
38106  * Usage:
38107  * <pre><code>
38108 // basic tabs 1, built from existing content
38109 var tabs = new Roo.TabPanel("tabs1");
38110 tabs.addTab("script", "View Script");
38111 tabs.addTab("markup", "View Markup");
38112 tabs.activate("script");
38113
38114 // more advanced tabs, built from javascript
38115 var jtabs = new Roo.TabPanel("jtabs");
38116 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
38117
38118 // set up the UpdateManager
38119 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
38120 var updater = tab2.getUpdateManager();
38121 updater.setDefaultUrl("ajax1.htm");
38122 tab2.on('activate', updater.refresh, updater, true);
38123
38124 // Use setUrl for Ajax loading
38125 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
38126 tab3.setUrl("ajax2.htm", null, true);
38127
38128 // Disabled tab
38129 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
38130 tab4.disable();
38131
38132 jtabs.activate("jtabs-1");
38133  * </code></pre>
38134  * @constructor
38135  * Create a new TabPanel.
38136  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
38137  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
38138  */
38139 Roo.bootstrap.panel.Tabs = function(config){
38140     /**
38141     * The container element for this TabPanel.
38142     * @type Roo.Element
38143     */
38144     this.el = Roo.get(config.el);
38145     delete config.el;
38146     if(config){
38147         if(typeof config == "boolean"){
38148             this.tabPosition = config ? "bottom" : "top";
38149         }else{
38150             Roo.apply(this, config);
38151         }
38152     }
38153     
38154     if(this.tabPosition == "bottom"){
38155         // if tabs are at the bottom = create the body first.
38156         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38157         this.el.addClass("roo-tabs-bottom");
38158     }
38159     // next create the tabs holders
38160     
38161     if (this.tabPosition == "west"){
38162         
38163         var reg = this.region; // fake it..
38164         while (reg) {
38165             if (!reg.mgr.parent) {
38166                 break;
38167             }
38168             reg = reg.mgr.parent.region;
38169         }
38170         Roo.log("got nest?");
38171         Roo.log(reg);
38172         if (reg.mgr.getRegion('west')) {
38173             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
38174             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
38175             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38176             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38177             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38178         
38179             
38180         }
38181         
38182         
38183     } else {
38184      
38185         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
38186         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38187         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38188         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38189     }
38190     
38191     
38192     if(Roo.isIE){
38193         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
38194     }
38195     
38196     // finally - if tabs are at the top, then create the body last..
38197     if(this.tabPosition != "bottom"){
38198         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
38199          * @type Roo.Element
38200          */
38201         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38202         this.el.addClass("roo-tabs-top");
38203     }
38204     this.items = [];
38205
38206     this.bodyEl.setStyle("position", "relative");
38207
38208     this.active = null;
38209     this.activateDelegate = this.activate.createDelegate(this);
38210
38211     this.addEvents({
38212         /**
38213          * @event tabchange
38214          * Fires when the active tab changes
38215          * @param {Roo.TabPanel} this
38216          * @param {Roo.TabPanelItem} activePanel The new active tab
38217          */
38218         "tabchange": true,
38219         /**
38220          * @event beforetabchange
38221          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
38222          * @param {Roo.TabPanel} this
38223          * @param {Object} e Set cancel to true on this object to cancel the tab change
38224          * @param {Roo.TabPanelItem} tab The tab being changed to
38225          */
38226         "beforetabchange" : true
38227     });
38228
38229     Roo.EventManager.onWindowResize(this.onResize, this);
38230     this.cpad = this.el.getPadding("lr");
38231     this.hiddenCount = 0;
38232
38233
38234     // toolbar on the tabbar support...
38235     if (this.toolbar) {
38236         alert("no toolbar support yet");
38237         this.toolbar  = false;
38238         /*
38239         var tcfg = this.toolbar;
38240         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
38241         this.toolbar = new Roo.Toolbar(tcfg);
38242         if (Roo.isSafari) {
38243             var tbl = tcfg.container.child('table', true);
38244             tbl.setAttribute('width', '100%');
38245         }
38246         */
38247         
38248     }
38249    
38250
38251
38252     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
38253 };
38254
38255 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
38256     /*
38257      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
38258      */
38259     tabPosition : "top",
38260     /*
38261      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
38262      */
38263     currentTabWidth : 0,
38264     /*
38265      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
38266      */
38267     minTabWidth : 40,
38268     /*
38269      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
38270      */
38271     maxTabWidth : 250,
38272     /*
38273      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
38274      */
38275     preferredTabWidth : 175,
38276     /*
38277      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
38278      */
38279     resizeTabs : false,
38280     /*
38281      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38282      */
38283     monitorResize : true,
38284     /*
38285      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
38286      */
38287     toolbar : false,  // set by caller..
38288     
38289     region : false, /// set by caller
38290     
38291     disableTooltips : true, // not used yet...
38292
38293     /**
38294      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38295      * @param {String} id The id of the div to use <b>or create</b>
38296      * @param {String} text The text for the tab
38297      * @param {String} content (optional) Content to put in the TabPanelItem body
38298      * @param {Boolean} closable (optional) True to create a close icon on the tab
38299      * @return {Roo.TabPanelItem} The created TabPanelItem
38300      */
38301     addTab : function(id, text, content, closable, tpl)
38302     {
38303         var item = new Roo.bootstrap.panel.TabItem({
38304             panel: this,
38305             id : id,
38306             text : text,
38307             closable : closable,
38308             tpl : tpl
38309         });
38310         this.addTabItem(item);
38311         if(content){
38312             item.setContent(content);
38313         }
38314         return item;
38315     },
38316
38317     /**
38318      * Returns the {@link Roo.TabPanelItem} with the specified id/index
38319      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38320      * @return {Roo.TabPanelItem}
38321      */
38322     getTab : function(id){
38323         return this.items[id];
38324     },
38325
38326     /**
38327      * Hides the {@link Roo.TabPanelItem} with the specified id/index
38328      * @param {String/Number} id The id or index of the TabPanelItem to hide.
38329      */
38330     hideTab : function(id){
38331         var t = this.items[id];
38332         if(!t.isHidden()){
38333            t.setHidden(true);
38334            this.hiddenCount++;
38335            this.autoSizeTabs();
38336         }
38337     },
38338
38339     /**
38340      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38341      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38342      */
38343     unhideTab : function(id){
38344         var t = this.items[id];
38345         if(t.isHidden()){
38346            t.setHidden(false);
38347            this.hiddenCount--;
38348            this.autoSizeTabs();
38349         }
38350     },
38351
38352     /**
38353      * Adds an existing {@link Roo.TabPanelItem}.
38354      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38355      */
38356     addTabItem : function(item)
38357     {
38358         this.items[item.id] = item;
38359         this.items.push(item);
38360         this.autoSizeTabs();
38361       //  if(this.resizeTabs){
38362     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38363   //         this.autoSizeTabs();
38364 //        }else{
38365 //            item.autoSize();
38366        // }
38367     },
38368
38369     /**
38370      * Removes a {@link Roo.TabPanelItem}.
38371      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38372      */
38373     removeTab : function(id){
38374         var items = this.items;
38375         var tab = items[id];
38376         if(!tab) { return; }
38377         var index = items.indexOf(tab);
38378         if(this.active == tab && items.length > 1){
38379             var newTab = this.getNextAvailable(index);
38380             if(newTab) {
38381                 newTab.activate();
38382             }
38383         }
38384         this.stripEl.dom.removeChild(tab.pnode.dom);
38385         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38386             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38387         }
38388         items.splice(index, 1);
38389         delete this.items[tab.id];
38390         tab.fireEvent("close", tab);
38391         tab.purgeListeners();
38392         this.autoSizeTabs();
38393     },
38394
38395     getNextAvailable : function(start){
38396         var items = this.items;
38397         var index = start;
38398         // look for a next tab that will slide over to
38399         // replace the one being removed
38400         while(index < items.length){
38401             var item = items[++index];
38402             if(item && !item.isHidden()){
38403                 return item;
38404             }
38405         }
38406         // if one isn't found select the previous tab (on the left)
38407         index = start;
38408         while(index >= 0){
38409             var item = items[--index];
38410             if(item && !item.isHidden()){
38411                 return item;
38412             }
38413         }
38414         return null;
38415     },
38416
38417     /**
38418      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38419      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38420      */
38421     disableTab : function(id){
38422         var tab = this.items[id];
38423         if(tab && this.active != tab){
38424             tab.disable();
38425         }
38426     },
38427
38428     /**
38429      * Enables a {@link Roo.TabPanelItem} that is disabled.
38430      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38431      */
38432     enableTab : function(id){
38433         var tab = this.items[id];
38434         tab.enable();
38435     },
38436
38437     /**
38438      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38439      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38440      * @return {Roo.TabPanelItem} The TabPanelItem.
38441      */
38442     activate : function(id)
38443     {
38444         //Roo.log('activite:'  + id);
38445         
38446         var tab = this.items[id];
38447         if(!tab){
38448             return null;
38449         }
38450         if(tab == this.active || tab.disabled){
38451             return tab;
38452         }
38453         var e = {};
38454         this.fireEvent("beforetabchange", this, e, tab);
38455         if(e.cancel !== true && !tab.disabled){
38456             if(this.active){
38457                 this.active.hide();
38458             }
38459             this.active = this.items[id];
38460             this.active.show();
38461             this.fireEvent("tabchange", this, this.active);
38462         }
38463         return tab;
38464     },
38465
38466     /**
38467      * Gets the active {@link Roo.TabPanelItem}.
38468      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38469      */
38470     getActiveTab : function(){
38471         return this.active;
38472     },
38473
38474     /**
38475      * Updates the tab body element to fit the height of the container element
38476      * for overflow scrolling
38477      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38478      */
38479     syncHeight : function(targetHeight){
38480         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38481         var bm = this.bodyEl.getMargins();
38482         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38483         this.bodyEl.setHeight(newHeight);
38484         return newHeight;
38485     },
38486
38487     onResize : function(){
38488         if(this.monitorResize){
38489             this.autoSizeTabs();
38490         }
38491     },
38492
38493     /**
38494      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38495      */
38496     beginUpdate : function(){
38497         this.updating = true;
38498     },
38499
38500     /**
38501      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38502      */
38503     endUpdate : function(){
38504         this.updating = false;
38505         this.autoSizeTabs();
38506     },
38507
38508     /**
38509      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38510      */
38511     autoSizeTabs : function()
38512     {
38513         var count = this.items.length;
38514         var vcount = count - this.hiddenCount;
38515         
38516         if (vcount < 2) {
38517             this.stripEl.hide();
38518         } else {
38519             this.stripEl.show();
38520         }
38521         
38522         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38523             return;
38524         }
38525         
38526         
38527         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38528         var availWidth = Math.floor(w / vcount);
38529         var b = this.stripBody;
38530         if(b.getWidth() > w){
38531             var tabs = this.items;
38532             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38533             if(availWidth < this.minTabWidth){
38534                 /*if(!this.sleft){    // incomplete scrolling code
38535                     this.createScrollButtons();
38536                 }
38537                 this.showScroll();
38538                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38539             }
38540         }else{
38541             if(this.currentTabWidth < this.preferredTabWidth){
38542                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38543             }
38544         }
38545     },
38546
38547     /**
38548      * Returns the number of tabs in this TabPanel.
38549      * @return {Number}
38550      */
38551      getCount : function(){
38552          return this.items.length;
38553      },
38554
38555     /**
38556      * Resizes all the tabs to the passed width
38557      * @param {Number} The new width
38558      */
38559     setTabWidth : function(width){
38560         this.currentTabWidth = width;
38561         for(var i = 0, len = this.items.length; i < len; i++) {
38562                 if(!this.items[i].isHidden()) {
38563                 this.items[i].setWidth(width);
38564             }
38565         }
38566     },
38567
38568     /**
38569      * Destroys this TabPanel
38570      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38571      */
38572     destroy : function(removeEl){
38573         Roo.EventManager.removeResizeListener(this.onResize, this);
38574         for(var i = 0, len = this.items.length; i < len; i++){
38575             this.items[i].purgeListeners();
38576         }
38577         if(removeEl === true){
38578             this.el.update("");
38579             this.el.remove();
38580         }
38581     },
38582     
38583     createStrip : function(container)
38584     {
38585         var strip = document.createElement("nav");
38586         strip.className = Roo.bootstrap.version == 4 ?
38587             "navbar-light bg-light" : 
38588             "navbar navbar-default"; //"x-tabs-wrap";
38589         container.appendChild(strip);
38590         return strip;
38591     },
38592     
38593     createStripList : function(strip)
38594     {
38595         // div wrapper for retard IE
38596         // returns the "tr" element.
38597         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38598         //'<div class="x-tabs-strip-wrap">'+
38599           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38600           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38601         return strip.firstChild; //.firstChild.firstChild.firstChild;
38602     },
38603     createBody : function(container)
38604     {
38605         var body = document.createElement("div");
38606         Roo.id(body, "tab-body");
38607         //Roo.fly(body).addClass("x-tabs-body");
38608         Roo.fly(body).addClass("tab-content");
38609         container.appendChild(body);
38610         return body;
38611     },
38612     createItemBody :function(bodyEl, id){
38613         var body = Roo.getDom(id);
38614         if(!body){
38615             body = document.createElement("div");
38616             body.id = id;
38617         }
38618         //Roo.fly(body).addClass("x-tabs-item-body");
38619         Roo.fly(body).addClass("tab-pane");
38620          bodyEl.insertBefore(body, bodyEl.firstChild);
38621         return body;
38622     },
38623     /** @private */
38624     createStripElements :  function(stripEl, text, closable, tpl)
38625     {
38626         var td = document.createElement("li"); // was td..
38627         td.className = 'nav-item';
38628         
38629         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38630         
38631         
38632         stripEl.appendChild(td);
38633         /*if(closable){
38634             td.className = "x-tabs-closable";
38635             if(!this.closeTpl){
38636                 this.closeTpl = new Roo.Template(
38637                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38638                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38639                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38640                 );
38641             }
38642             var el = this.closeTpl.overwrite(td, {"text": text});
38643             var close = el.getElementsByTagName("div")[0];
38644             var inner = el.getElementsByTagName("em")[0];
38645             return {"el": el, "close": close, "inner": inner};
38646         } else {
38647         */
38648         // not sure what this is..
38649 //            if(!this.tabTpl){
38650                 //this.tabTpl = new Roo.Template(
38651                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38652                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38653                 //);
38654 //                this.tabTpl = new Roo.Template(
38655 //                   '<a href="#">' +
38656 //                   '<span unselectable="on"' +
38657 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38658 //                            ' >{text}</span></a>'
38659 //                );
38660 //                
38661 //            }
38662
38663
38664             var template = tpl || this.tabTpl || false;
38665             
38666             if(!template){
38667                 template =  new Roo.Template(
38668                         Roo.bootstrap.version == 4 ? 
38669                             (
38670                                 '<a class="nav-link" href="#" unselectable="on"' +
38671                                      (this.disableTooltips ? '' : ' title="{text}"') +
38672                                      ' >{text}</a>'
38673                             ) : (
38674                                 '<a class="nav-link" href="#">' +
38675                                 '<span unselectable="on"' +
38676                                          (this.disableTooltips ? '' : ' title="{text}"') +
38677                                     ' >{text}</span></a>'
38678                             )
38679                 );
38680             }
38681             
38682             switch (typeof(template)) {
38683                 case 'object' :
38684                     break;
38685                 case 'string' :
38686                     template = new Roo.Template(template);
38687                     break;
38688                 default :
38689                     break;
38690             }
38691             
38692             var el = template.overwrite(td, {"text": text});
38693             
38694             var inner = el.getElementsByTagName("span")[0];
38695             
38696             return {"el": el, "inner": inner};
38697             
38698     }
38699         
38700     
38701 });
38702
38703 /**
38704  * @class Roo.TabPanelItem
38705  * @extends Roo.util.Observable
38706  * Represents an individual item (tab plus body) in a TabPanel.
38707  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38708  * @param {String} id The id of this TabPanelItem
38709  * @param {String} text The text for the tab of this TabPanelItem
38710  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38711  */
38712 Roo.bootstrap.panel.TabItem = function(config){
38713     /**
38714      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38715      * @type Roo.TabPanel
38716      */
38717     this.tabPanel = config.panel;
38718     /**
38719      * The id for this TabPanelItem
38720      * @type String
38721      */
38722     this.id = config.id;
38723     /** @private */
38724     this.disabled = false;
38725     /** @private */
38726     this.text = config.text;
38727     /** @private */
38728     this.loaded = false;
38729     this.closable = config.closable;
38730
38731     /**
38732      * The body element for this TabPanelItem.
38733      * @type Roo.Element
38734      */
38735     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38736     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38737     this.bodyEl.setStyle("display", "block");
38738     this.bodyEl.setStyle("zoom", "1");
38739     //this.hideAction();
38740
38741     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38742     /** @private */
38743     this.el = Roo.get(els.el);
38744     this.inner = Roo.get(els.inner, true);
38745      this.textEl = Roo.bootstrap.version == 4 ?
38746         this.el : Roo.get(this.el.dom.firstChild, true);
38747
38748     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
38749     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
38750
38751     
38752 //    this.el.on("mousedown", this.onTabMouseDown, this);
38753     this.el.on("click", this.onTabClick, this);
38754     /** @private */
38755     if(config.closable){
38756         var c = Roo.get(els.close, true);
38757         c.dom.title = this.closeText;
38758         c.addClassOnOver("close-over");
38759         c.on("click", this.closeClick, this);
38760      }
38761
38762     this.addEvents({
38763          /**
38764          * @event activate
38765          * Fires when this tab becomes the active tab.
38766          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38767          * @param {Roo.TabPanelItem} this
38768          */
38769         "activate": true,
38770         /**
38771          * @event beforeclose
38772          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38773          * @param {Roo.TabPanelItem} this
38774          * @param {Object} e Set cancel to true on this object to cancel the close.
38775          */
38776         "beforeclose": true,
38777         /**
38778          * @event close
38779          * Fires when this tab is closed.
38780          * @param {Roo.TabPanelItem} this
38781          */
38782          "close": true,
38783         /**
38784          * @event deactivate
38785          * Fires when this tab is no longer the active tab.
38786          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38787          * @param {Roo.TabPanelItem} this
38788          */
38789          "deactivate" : true
38790     });
38791     this.hidden = false;
38792
38793     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38794 };
38795
38796 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38797            {
38798     purgeListeners : function(){
38799        Roo.util.Observable.prototype.purgeListeners.call(this);
38800        this.el.removeAllListeners();
38801     },
38802     /**
38803      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38804      */
38805     show : function(){
38806         this.status_node.addClass("active");
38807         this.showAction();
38808         if(Roo.isOpera){
38809             this.tabPanel.stripWrap.repaint();
38810         }
38811         this.fireEvent("activate", this.tabPanel, this);
38812     },
38813
38814     /**
38815      * Returns true if this tab is the active tab.
38816      * @return {Boolean}
38817      */
38818     isActive : function(){
38819         return this.tabPanel.getActiveTab() == this;
38820     },
38821
38822     /**
38823      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38824      */
38825     hide : function(){
38826         this.status_node.removeClass("active");
38827         this.hideAction();
38828         this.fireEvent("deactivate", this.tabPanel, this);
38829     },
38830
38831     hideAction : function(){
38832         this.bodyEl.hide();
38833         this.bodyEl.setStyle("position", "absolute");
38834         this.bodyEl.setLeft("-20000px");
38835         this.bodyEl.setTop("-20000px");
38836     },
38837
38838     showAction : function(){
38839         this.bodyEl.setStyle("position", "relative");
38840         this.bodyEl.setTop("");
38841         this.bodyEl.setLeft("");
38842         this.bodyEl.show();
38843     },
38844
38845     /**
38846      * Set the tooltip for the tab.
38847      * @param {String} tooltip The tab's tooltip
38848      */
38849     setTooltip : function(text){
38850         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38851             this.textEl.dom.qtip = text;
38852             this.textEl.dom.removeAttribute('title');
38853         }else{
38854             this.textEl.dom.title = text;
38855         }
38856     },
38857
38858     onTabClick : function(e){
38859         e.preventDefault();
38860         this.tabPanel.activate(this.id);
38861     },
38862
38863     onTabMouseDown : function(e){
38864         e.preventDefault();
38865         this.tabPanel.activate(this.id);
38866     },
38867 /*
38868     getWidth : function(){
38869         return this.inner.getWidth();
38870     },
38871
38872     setWidth : function(width){
38873         var iwidth = width - this.linode.getPadding("lr");
38874         this.inner.setWidth(iwidth);
38875         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38876         this.linode.setWidth(width);
38877     },
38878 */
38879     /**
38880      * Show or hide the tab
38881      * @param {Boolean} hidden True to hide or false to show.
38882      */
38883     setHidden : function(hidden){
38884         this.hidden = hidden;
38885         this.linode.setStyle("display", hidden ? "none" : "");
38886     },
38887
38888     /**
38889      * Returns true if this tab is "hidden"
38890      * @return {Boolean}
38891      */
38892     isHidden : function(){
38893         return this.hidden;
38894     },
38895
38896     /**
38897      * Returns the text for this tab
38898      * @return {String}
38899      */
38900     getText : function(){
38901         return this.text;
38902     },
38903     /*
38904     autoSize : function(){
38905         //this.el.beginMeasure();
38906         this.textEl.setWidth(1);
38907         /*
38908          *  #2804 [new] Tabs in Roojs
38909          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38910          */
38911         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38912         //this.el.endMeasure();
38913     //},
38914
38915     /**
38916      * Sets the text for the tab (Note: this also sets the tooltip text)
38917      * @param {String} text The tab's text and tooltip
38918      */
38919     setText : function(text){
38920         this.text = text;
38921         this.textEl.update(text);
38922         this.setTooltip(text);
38923         //if(!this.tabPanel.resizeTabs){
38924         //    this.autoSize();
38925         //}
38926     },
38927     /**
38928      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38929      */
38930     activate : function(){
38931         this.tabPanel.activate(this.id);
38932     },
38933
38934     /**
38935      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38936      */
38937     disable : function(){
38938         if(this.tabPanel.active != this){
38939             this.disabled = true;
38940             this.status_node.addClass("disabled");
38941         }
38942     },
38943
38944     /**
38945      * Enables this TabPanelItem if it was previously disabled.
38946      */
38947     enable : function(){
38948         this.disabled = false;
38949         this.status_node.removeClass("disabled");
38950     },
38951
38952     /**
38953      * Sets the content for this TabPanelItem.
38954      * @param {String} content The content
38955      * @param {Boolean} loadScripts true to look for and load scripts
38956      */
38957     setContent : function(content, loadScripts){
38958         this.bodyEl.update(content, loadScripts);
38959     },
38960
38961     /**
38962      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38963      * @return {Roo.UpdateManager} The UpdateManager
38964      */
38965     getUpdateManager : function(){
38966         return this.bodyEl.getUpdateManager();
38967     },
38968
38969     /**
38970      * Set a URL to be used to load the content for this TabPanelItem.
38971      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38972      * @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)
38973      * @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)
38974      * @return {Roo.UpdateManager} The UpdateManager
38975      */
38976     setUrl : function(url, params, loadOnce){
38977         if(this.refreshDelegate){
38978             this.un('activate', this.refreshDelegate);
38979         }
38980         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38981         this.on("activate", this.refreshDelegate);
38982         return this.bodyEl.getUpdateManager();
38983     },
38984
38985     /** @private */
38986     _handleRefresh : function(url, params, loadOnce){
38987         if(!loadOnce || !this.loaded){
38988             var updater = this.bodyEl.getUpdateManager();
38989             updater.update(url, params, this._setLoaded.createDelegate(this));
38990         }
38991     },
38992
38993     /**
38994      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38995      *   Will fail silently if the setUrl method has not been called.
38996      *   This does not activate the panel, just updates its content.
38997      */
38998     refresh : function(){
38999         if(this.refreshDelegate){
39000            this.loaded = false;
39001            this.refreshDelegate();
39002         }
39003     },
39004
39005     /** @private */
39006     _setLoaded : function(){
39007         this.loaded = true;
39008     },
39009
39010     /** @private */
39011     closeClick : function(e){
39012         var o = {};
39013         e.stopEvent();
39014         this.fireEvent("beforeclose", this, o);
39015         if(o.cancel !== true){
39016             this.tabPanel.removeTab(this.id);
39017         }
39018     },
39019     /**
39020      * The text displayed in the tooltip for the close icon.
39021      * @type String
39022      */
39023     closeText : "Close this tab"
39024 });
39025 /**
39026 *    This script refer to:
39027 *    Title: International Telephone Input
39028 *    Author: Jack O'Connor
39029 *    Code version:  v12.1.12
39030 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39031 **/
39032
39033 Roo.bootstrap.PhoneInputData = function() {
39034     var d = [
39035       [
39036         "Afghanistan (‫افغانستان‬‎)",
39037         "af",
39038         "93"
39039       ],
39040       [
39041         "Albania (Shqipëri)",
39042         "al",
39043         "355"
39044       ],
39045       [
39046         "Algeria (‫الجزائر‬‎)",
39047         "dz",
39048         "213"
39049       ],
39050       [
39051         "American Samoa",
39052         "as",
39053         "1684"
39054       ],
39055       [
39056         "Andorra",
39057         "ad",
39058         "376"
39059       ],
39060       [
39061         "Angola",
39062         "ao",
39063         "244"
39064       ],
39065       [
39066         "Anguilla",
39067         "ai",
39068         "1264"
39069       ],
39070       [
39071         "Antigua and Barbuda",
39072         "ag",
39073         "1268"
39074       ],
39075       [
39076         "Argentina",
39077         "ar",
39078         "54"
39079       ],
39080       [
39081         "Armenia (Հայաստան)",
39082         "am",
39083         "374"
39084       ],
39085       [
39086         "Aruba",
39087         "aw",
39088         "297"
39089       ],
39090       [
39091         "Australia",
39092         "au",
39093         "61",
39094         0
39095       ],
39096       [
39097         "Austria (Österreich)",
39098         "at",
39099         "43"
39100       ],
39101       [
39102         "Azerbaijan (Azərbaycan)",
39103         "az",
39104         "994"
39105       ],
39106       [
39107         "Bahamas",
39108         "bs",
39109         "1242"
39110       ],
39111       [
39112         "Bahrain (‫البحرين‬‎)",
39113         "bh",
39114         "973"
39115       ],
39116       [
39117         "Bangladesh (বাংলাদেশ)",
39118         "bd",
39119         "880"
39120       ],
39121       [
39122         "Barbados",
39123         "bb",
39124         "1246"
39125       ],
39126       [
39127         "Belarus (Беларусь)",
39128         "by",
39129         "375"
39130       ],
39131       [
39132         "Belgium (België)",
39133         "be",
39134         "32"
39135       ],
39136       [
39137         "Belize",
39138         "bz",
39139         "501"
39140       ],
39141       [
39142         "Benin (Bénin)",
39143         "bj",
39144         "229"
39145       ],
39146       [
39147         "Bermuda",
39148         "bm",
39149         "1441"
39150       ],
39151       [
39152         "Bhutan (འབྲུག)",
39153         "bt",
39154         "975"
39155       ],
39156       [
39157         "Bolivia",
39158         "bo",
39159         "591"
39160       ],
39161       [
39162         "Bosnia and Herzegovina (Босна и Херцеговина)",
39163         "ba",
39164         "387"
39165       ],
39166       [
39167         "Botswana",
39168         "bw",
39169         "267"
39170       ],
39171       [
39172         "Brazil (Brasil)",
39173         "br",
39174         "55"
39175       ],
39176       [
39177         "British Indian Ocean Territory",
39178         "io",
39179         "246"
39180       ],
39181       [
39182         "British Virgin Islands",
39183         "vg",
39184         "1284"
39185       ],
39186       [
39187         "Brunei",
39188         "bn",
39189         "673"
39190       ],
39191       [
39192         "Bulgaria (България)",
39193         "bg",
39194         "359"
39195       ],
39196       [
39197         "Burkina Faso",
39198         "bf",
39199         "226"
39200       ],
39201       [
39202         "Burundi (Uburundi)",
39203         "bi",
39204         "257"
39205       ],
39206       [
39207         "Cambodia (កម្ពុជា)",
39208         "kh",
39209         "855"
39210       ],
39211       [
39212         "Cameroon (Cameroun)",
39213         "cm",
39214         "237"
39215       ],
39216       [
39217         "Canada",
39218         "ca",
39219         "1",
39220         1,
39221         ["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"]
39222       ],
39223       [
39224         "Cape Verde (Kabu Verdi)",
39225         "cv",
39226         "238"
39227       ],
39228       [
39229         "Caribbean Netherlands",
39230         "bq",
39231         "599",
39232         1
39233       ],
39234       [
39235         "Cayman Islands",
39236         "ky",
39237         "1345"
39238       ],
39239       [
39240         "Central African Republic (République centrafricaine)",
39241         "cf",
39242         "236"
39243       ],
39244       [
39245         "Chad (Tchad)",
39246         "td",
39247         "235"
39248       ],
39249       [
39250         "Chile",
39251         "cl",
39252         "56"
39253       ],
39254       [
39255         "China (中国)",
39256         "cn",
39257         "86"
39258       ],
39259       [
39260         "Christmas Island",
39261         "cx",
39262         "61",
39263         2
39264       ],
39265       [
39266         "Cocos (Keeling) Islands",
39267         "cc",
39268         "61",
39269         1
39270       ],
39271       [
39272         "Colombia",
39273         "co",
39274         "57"
39275       ],
39276       [
39277         "Comoros (‫جزر القمر‬‎)",
39278         "km",
39279         "269"
39280       ],
39281       [
39282         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
39283         "cd",
39284         "243"
39285       ],
39286       [
39287         "Congo (Republic) (Congo-Brazzaville)",
39288         "cg",
39289         "242"
39290       ],
39291       [
39292         "Cook Islands",
39293         "ck",
39294         "682"
39295       ],
39296       [
39297         "Costa Rica",
39298         "cr",
39299         "506"
39300       ],
39301       [
39302         "Côte d’Ivoire",
39303         "ci",
39304         "225"
39305       ],
39306       [
39307         "Croatia (Hrvatska)",
39308         "hr",
39309         "385"
39310       ],
39311       [
39312         "Cuba",
39313         "cu",
39314         "53"
39315       ],
39316       [
39317         "Curaçao",
39318         "cw",
39319         "599",
39320         0
39321       ],
39322       [
39323         "Cyprus (Κύπρος)",
39324         "cy",
39325         "357"
39326       ],
39327       [
39328         "Czech Republic (Česká republika)",
39329         "cz",
39330         "420"
39331       ],
39332       [
39333         "Denmark (Danmark)",
39334         "dk",
39335         "45"
39336       ],
39337       [
39338         "Djibouti",
39339         "dj",
39340         "253"
39341       ],
39342       [
39343         "Dominica",
39344         "dm",
39345         "1767"
39346       ],
39347       [
39348         "Dominican Republic (República Dominicana)",
39349         "do",
39350         "1",
39351         2,
39352         ["809", "829", "849"]
39353       ],
39354       [
39355         "Ecuador",
39356         "ec",
39357         "593"
39358       ],
39359       [
39360         "Egypt (‫مصر‬‎)",
39361         "eg",
39362         "20"
39363       ],
39364       [
39365         "El Salvador",
39366         "sv",
39367         "503"
39368       ],
39369       [
39370         "Equatorial Guinea (Guinea Ecuatorial)",
39371         "gq",
39372         "240"
39373       ],
39374       [
39375         "Eritrea",
39376         "er",
39377         "291"
39378       ],
39379       [
39380         "Estonia (Eesti)",
39381         "ee",
39382         "372"
39383       ],
39384       [
39385         "Ethiopia",
39386         "et",
39387         "251"
39388       ],
39389       [
39390         "Falkland Islands (Islas Malvinas)",
39391         "fk",
39392         "500"
39393       ],
39394       [
39395         "Faroe Islands (Føroyar)",
39396         "fo",
39397         "298"
39398       ],
39399       [
39400         "Fiji",
39401         "fj",
39402         "679"
39403       ],
39404       [
39405         "Finland (Suomi)",
39406         "fi",
39407         "358",
39408         0
39409       ],
39410       [
39411         "France",
39412         "fr",
39413         "33"
39414       ],
39415       [
39416         "French Guiana (Guyane française)",
39417         "gf",
39418         "594"
39419       ],
39420       [
39421         "French Polynesia (Polynésie française)",
39422         "pf",
39423         "689"
39424       ],
39425       [
39426         "Gabon",
39427         "ga",
39428         "241"
39429       ],
39430       [
39431         "Gambia",
39432         "gm",
39433         "220"
39434       ],
39435       [
39436         "Georgia (საქართველო)",
39437         "ge",
39438         "995"
39439       ],
39440       [
39441         "Germany (Deutschland)",
39442         "de",
39443         "49"
39444       ],
39445       [
39446         "Ghana (Gaana)",
39447         "gh",
39448         "233"
39449       ],
39450       [
39451         "Gibraltar",
39452         "gi",
39453         "350"
39454       ],
39455       [
39456         "Greece (Ελλάδα)",
39457         "gr",
39458         "30"
39459       ],
39460       [
39461         "Greenland (Kalaallit Nunaat)",
39462         "gl",
39463         "299"
39464       ],
39465       [
39466         "Grenada",
39467         "gd",
39468         "1473"
39469       ],
39470       [
39471         "Guadeloupe",
39472         "gp",
39473         "590",
39474         0
39475       ],
39476       [
39477         "Guam",
39478         "gu",
39479         "1671"
39480       ],
39481       [
39482         "Guatemala",
39483         "gt",
39484         "502"
39485       ],
39486       [
39487         "Guernsey",
39488         "gg",
39489         "44",
39490         1
39491       ],
39492       [
39493         "Guinea (Guinée)",
39494         "gn",
39495         "224"
39496       ],
39497       [
39498         "Guinea-Bissau (Guiné Bissau)",
39499         "gw",
39500         "245"
39501       ],
39502       [
39503         "Guyana",
39504         "gy",
39505         "592"
39506       ],
39507       [
39508         "Haiti",
39509         "ht",
39510         "509"
39511       ],
39512       [
39513         "Honduras",
39514         "hn",
39515         "504"
39516       ],
39517       [
39518         "Hong Kong (香港)",
39519         "hk",
39520         "852"
39521       ],
39522       [
39523         "Hungary (Magyarország)",
39524         "hu",
39525         "36"
39526       ],
39527       [
39528         "Iceland (Ísland)",
39529         "is",
39530         "354"
39531       ],
39532       [
39533         "India (भारत)",
39534         "in",
39535         "91"
39536       ],
39537       [
39538         "Indonesia",
39539         "id",
39540         "62"
39541       ],
39542       [
39543         "Iran (‫ایران‬‎)",
39544         "ir",
39545         "98"
39546       ],
39547       [
39548         "Iraq (‫العراق‬‎)",
39549         "iq",
39550         "964"
39551       ],
39552       [
39553         "Ireland",
39554         "ie",
39555         "353"
39556       ],
39557       [
39558         "Isle of Man",
39559         "im",
39560         "44",
39561         2
39562       ],
39563       [
39564         "Israel (‫ישראל‬‎)",
39565         "il",
39566         "972"
39567       ],
39568       [
39569         "Italy (Italia)",
39570         "it",
39571         "39",
39572         0
39573       ],
39574       [
39575         "Jamaica",
39576         "jm",
39577         "1876"
39578       ],
39579       [
39580         "Japan (日本)",
39581         "jp",
39582         "81"
39583       ],
39584       [
39585         "Jersey",
39586         "je",
39587         "44",
39588         3
39589       ],
39590       [
39591         "Jordan (‫الأردن‬‎)",
39592         "jo",
39593         "962"
39594       ],
39595       [
39596         "Kazakhstan (Казахстан)",
39597         "kz",
39598         "7",
39599         1
39600       ],
39601       [
39602         "Kenya",
39603         "ke",
39604         "254"
39605       ],
39606       [
39607         "Kiribati",
39608         "ki",
39609         "686"
39610       ],
39611       [
39612         "Kosovo",
39613         "xk",
39614         "383"
39615       ],
39616       [
39617         "Kuwait (‫الكويت‬‎)",
39618         "kw",
39619         "965"
39620       ],
39621       [
39622         "Kyrgyzstan (Кыргызстан)",
39623         "kg",
39624         "996"
39625       ],
39626       [
39627         "Laos (ລາວ)",
39628         "la",
39629         "856"
39630       ],
39631       [
39632         "Latvia (Latvija)",
39633         "lv",
39634         "371"
39635       ],
39636       [
39637         "Lebanon (‫لبنان‬‎)",
39638         "lb",
39639         "961"
39640       ],
39641       [
39642         "Lesotho",
39643         "ls",
39644         "266"
39645       ],
39646       [
39647         "Liberia",
39648         "lr",
39649         "231"
39650       ],
39651       [
39652         "Libya (‫ليبيا‬‎)",
39653         "ly",
39654         "218"
39655       ],
39656       [
39657         "Liechtenstein",
39658         "li",
39659         "423"
39660       ],
39661       [
39662         "Lithuania (Lietuva)",
39663         "lt",
39664         "370"
39665       ],
39666       [
39667         "Luxembourg",
39668         "lu",
39669         "352"
39670       ],
39671       [
39672         "Macau (澳門)",
39673         "mo",
39674         "853"
39675       ],
39676       [
39677         "Macedonia (FYROM) (Македонија)",
39678         "mk",
39679         "389"
39680       ],
39681       [
39682         "Madagascar (Madagasikara)",
39683         "mg",
39684         "261"
39685       ],
39686       [
39687         "Malawi",
39688         "mw",
39689         "265"
39690       ],
39691       [
39692         "Malaysia",
39693         "my",
39694         "60"
39695       ],
39696       [
39697         "Maldives",
39698         "mv",
39699         "960"
39700       ],
39701       [
39702         "Mali",
39703         "ml",
39704         "223"
39705       ],
39706       [
39707         "Malta",
39708         "mt",
39709         "356"
39710       ],
39711       [
39712         "Marshall Islands",
39713         "mh",
39714         "692"
39715       ],
39716       [
39717         "Martinique",
39718         "mq",
39719         "596"
39720       ],
39721       [
39722         "Mauritania (‫موريتانيا‬‎)",
39723         "mr",
39724         "222"
39725       ],
39726       [
39727         "Mauritius (Moris)",
39728         "mu",
39729         "230"
39730       ],
39731       [
39732         "Mayotte",
39733         "yt",
39734         "262",
39735         1
39736       ],
39737       [
39738         "Mexico (México)",
39739         "mx",
39740         "52"
39741       ],
39742       [
39743         "Micronesia",
39744         "fm",
39745         "691"
39746       ],
39747       [
39748         "Moldova (Republica Moldova)",
39749         "md",
39750         "373"
39751       ],
39752       [
39753         "Monaco",
39754         "mc",
39755         "377"
39756       ],
39757       [
39758         "Mongolia (Монгол)",
39759         "mn",
39760         "976"
39761       ],
39762       [
39763         "Montenegro (Crna Gora)",
39764         "me",
39765         "382"
39766       ],
39767       [
39768         "Montserrat",
39769         "ms",
39770         "1664"
39771       ],
39772       [
39773         "Morocco (‫المغرب‬‎)",
39774         "ma",
39775         "212",
39776         0
39777       ],
39778       [
39779         "Mozambique (Moçambique)",
39780         "mz",
39781         "258"
39782       ],
39783       [
39784         "Myanmar (Burma) (မြန်မာ)",
39785         "mm",
39786         "95"
39787       ],
39788       [
39789         "Namibia (Namibië)",
39790         "na",
39791         "264"
39792       ],
39793       [
39794         "Nauru",
39795         "nr",
39796         "674"
39797       ],
39798       [
39799         "Nepal (नेपाल)",
39800         "np",
39801         "977"
39802       ],
39803       [
39804         "Netherlands (Nederland)",
39805         "nl",
39806         "31"
39807       ],
39808       [
39809         "New Caledonia (Nouvelle-Calédonie)",
39810         "nc",
39811         "687"
39812       ],
39813       [
39814         "New Zealand",
39815         "nz",
39816         "64"
39817       ],
39818       [
39819         "Nicaragua",
39820         "ni",
39821         "505"
39822       ],
39823       [
39824         "Niger (Nijar)",
39825         "ne",
39826         "227"
39827       ],
39828       [
39829         "Nigeria",
39830         "ng",
39831         "234"
39832       ],
39833       [
39834         "Niue",
39835         "nu",
39836         "683"
39837       ],
39838       [
39839         "Norfolk Island",
39840         "nf",
39841         "672"
39842       ],
39843       [
39844         "North Korea (조선 민주주의 인민 공화국)",
39845         "kp",
39846         "850"
39847       ],
39848       [
39849         "Northern Mariana Islands",
39850         "mp",
39851         "1670"
39852       ],
39853       [
39854         "Norway (Norge)",
39855         "no",
39856         "47",
39857         0
39858       ],
39859       [
39860         "Oman (‫عُمان‬‎)",
39861         "om",
39862         "968"
39863       ],
39864       [
39865         "Pakistan (‫پاکستان‬‎)",
39866         "pk",
39867         "92"
39868       ],
39869       [
39870         "Palau",
39871         "pw",
39872         "680"
39873       ],
39874       [
39875         "Palestine (‫فلسطين‬‎)",
39876         "ps",
39877         "970"
39878       ],
39879       [
39880         "Panama (Panamá)",
39881         "pa",
39882         "507"
39883       ],
39884       [
39885         "Papua New Guinea",
39886         "pg",
39887         "675"
39888       ],
39889       [
39890         "Paraguay",
39891         "py",
39892         "595"
39893       ],
39894       [
39895         "Peru (Perú)",
39896         "pe",
39897         "51"
39898       ],
39899       [
39900         "Philippines",
39901         "ph",
39902         "63"
39903       ],
39904       [
39905         "Poland (Polska)",
39906         "pl",
39907         "48"
39908       ],
39909       [
39910         "Portugal",
39911         "pt",
39912         "351"
39913       ],
39914       [
39915         "Puerto Rico",
39916         "pr",
39917         "1",
39918         3,
39919         ["787", "939"]
39920       ],
39921       [
39922         "Qatar (‫قطر‬‎)",
39923         "qa",
39924         "974"
39925       ],
39926       [
39927         "Réunion (La Réunion)",
39928         "re",
39929         "262",
39930         0
39931       ],
39932       [
39933         "Romania (România)",
39934         "ro",
39935         "40"
39936       ],
39937       [
39938         "Russia (Россия)",
39939         "ru",
39940         "7",
39941         0
39942       ],
39943       [
39944         "Rwanda",
39945         "rw",
39946         "250"
39947       ],
39948       [
39949         "Saint Barthélemy",
39950         "bl",
39951         "590",
39952         1
39953       ],
39954       [
39955         "Saint Helena",
39956         "sh",
39957         "290"
39958       ],
39959       [
39960         "Saint Kitts and Nevis",
39961         "kn",
39962         "1869"
39963       ],
39964       [
39965         "Saint Lucia",
39966         "lc",
39967         "1758"
39968       ],
39969       [
39970         "Saint Martin (Saint-Martin (partie française))",
39971         "mf",
39972         "590",
39973         2
39974       ],
39975       [
39976         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39977         "pm",
39978         "508"
39979       ],
39980       [
39981         "Saint Vincent and the Grenadines",
39982         "vc",
39983         "1784"
39984       ],
39985       [
39986         "Samoa",
39987         "ws",
39988         "685"
39989       ],
39990       [
39991         "San Marino",
39992         "sm",
39993         "378"
39994       ],
39995       [
39996         "São Tomé and Príncipe (São Tomé e Príncipe)",
39997         "st",
39998         "239"
39999       ],
40000       [
40001         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
40002         "sa",
40003         "966"
40004       ],
40005       [
40006         "Senegal (Sénégal)",
40007         "sn",
40008         "221"
40009       ],
40010       [
40011         "Serbia (Србија)",
40012         "rs",
40013         "381"
40014       ],
40015       [
40016         "Seychelles",
40017         "sc",
40018         "248"
40019       ],
40020       [
40021         "Sierra Leone",
40022         "sl",
40023         "232"
40024       ],
40025       [
40026         "Singapore",
40027         "sg",
40028         "65"
40029       ],
40030       [
40031         "Sint Maarten",
40032         "sx",
40033         "1721"
40034       ],
40035       [
40036         "Slovakia (Slovensko)",
40037         "sk",
40038         "421"
40039       ],
40040       [
40041         "Slovenia (Slovenija)",
40042         "si",
40043         "386"
40044       ],
40045       [
40046         "Solomon Islands",
40047         "sb",
40048         "677"
40049       ],
40050       [
40051         "Somalia (Soomaaliya)",
40052         "so",
40053         "252"
40054       ],
40055       [
40056         "South Africa",
40057         "za",
40058         "27"
40059       ],
40060       [
40061         "South Korea (대한민국)",
40062         "kr",
40063         "82"
40064       ],
40065       [
40066         "South Sudan (‫جنوب السودان‬‎)",
40067         "ss",
40068         "211"
40069       ],
40070       [
40071         "Spain (España)",
40072         "es",
40073         "34"
40074       ],
40075       [
40076         "Sri Lanka (ශ්‍රී ලංකාව)",
40077         "lk",
40078         "94"
40079       ],
40080       [
40081         "Sudan (‫السودان‬‎)",
40082         "sd",
40083         "249"
40084       ],
40085       [
40086         "Suriname",
40087         "sr",
40088         "597"
40089       ],
40090       [
40091         "Svalbard and Jan Mayen",
40092         "sj",
40093         "47",
40094         1
40095       ],
40096       [
40097         "Swaziland",
40098         "sz",
40099         "268"
40100       ],
40101       [
40102         "Sweden (Sverige)",
40103         "se",
40104         "46"
40105       ],
40106       [
40107         "Switzerland (Schweiz)",
40108         "ch",
40109         "41"
40110       ],
40111       [
40112         "Syria (‫سوريا‬‎)",
40113         "sy",
40114         "963"
40115       ],
40116       [
40117         "Taiwan (台灣)",
40118         "tw",
40119         "886"
40120       ],
40121       [
40122         "Tajikistan",
40123         "tj",
40124         "992"
40125       ],
40126       [
40127         "Tanzania",
40128         "tz",
40129         "255"
40130       ],
40131       [
40132         "Thailand (ไทย)",
40133         "th",
40134         "66"
40135       ],
40136       [
40137         "Timor-Leste",
40138         "tl",
40139         "670"
40140       ],
40141       [
40142         "Togo",
40143         "tg",
40144         "228"
40145       ],
40146       [
40147         "Tokelau",
40148         "tk",
40149         "690"
40150       ],
40151       [
40152         "Tonga",
40153         "to",
40154         "676"
40155       ],
40156       [
40157         "Trinidad and Tobago",
40158         "tt",
40159         "1868"
40160       ],
40161       [
40162         "Tunisia (‫تونس‬‎)",
40163         "tn",
40164         "216"
40165       ],
40166       [
40167         "Turkey (Türkiye)",
40168         "tr",
40169         "90"
40170       ],
40171       [
40172         "Turkmenistan",
40173         "tm",
40174         "993"
40175       ],
40176       [
40177         "Turks and Caicos Islands",
40178         "tc",
40179         "1649"
40180       ],
40181       [
40182         "Tuvalu",
40183         "tv",
40184         "688"
40185       ],
40186       [
40187         "U.S. Virgin Islands",
40188         "vi",
40189         "1340"
40190       ],
40191       [
40192         "Uganda",
40193         "ug",
40194         "256"
40195       ],
40196       [
40197         "Ukraine (Україна)",
40198         "ua",
40199         "380"
40200       ],
40201       [
40202         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
40203         "ae",
40204         "971"
40205       ],
40206       [
40207         "United Kingdom",
40208         "gb",
40209         "44",
40210         0
40211       ],
40212       [
40213         "United States",
40214         "us",
40215         "1",
40216         0
40217       ],
40218       [
40219         "Uruguay",
40220         "uy",
40221         "598"
40222       ],
40223       [
40224         "Uzbekistan (Oʻzbekiston)",
40225         "uz",
40226         "998"
40227       ],
40228       [
40229         "Vanuatu",
40230         "vu",
40231         "678"
40232       ],
40233       [
40234         "Vatican City (Città del Vaticano)",
40235         "va",
40236         "39",
40237         1
40238       ],
40239       [
40240         "Venezuela",
40241         "ve",
40242         "58"
40243       ],
40244       [
40245         "Vietnam (Việt Nam)",
40246         "vn",
40247         "84"
40248       ],
40249       [
40250         "Wallis and Futuna (Wallis-et-Futuna)",
40251         "wf",
40252         "681"
40253       ],
40254       [
40255         "Western Sahara (‫الصحراء الغربية‬‎)",
40256         "eh",
40257         "212",
40258         1
40259       ],
40260       [
40261         "Yemen (‫اليمن‬‎)",
40262         "ye",
40263         "967"
40264       ],
40265       [
40266         "Zambia",
40267         "zm",
40268         "260"
40269       ],
40270       [
40271         "Zimbabwe",
40272         "zw",
40273         "263"
40274       ],
40275       [
40276         "Åland Islands",
40277         "ax",
40278         "358",
40279         1
40280       ]
40281   ];
40282   
40283   return d;
40284 }/**
40285 *    This script refer to:
40286 *    Title: International Telephone Input
40287 *    Author: Jack O'Connor
40288 *    Code version:  v12.1.12
40289 *    Availability: https://github.com/jackocnr/intl-tel-input.git
40290 **/
40291
40292 /**
40293  * @class Roo.bootstrap.PhoneInput
40294  * @extends Roo.bootstrap.TriggerField
40295  * An input with International dial-code selection
40296  
40297  * @cfg {String} defaultDialCode default '+852'
40298  * @cfg {Array} preferedCountries default []
40299   
40300  * @constructor
40301  * Create a new PhoneInput.
40302  * @param {Object} config Configuration options
40303  */
40304
40305 Roo.bootstrap.PhoneInput = function(config) {
40306     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
40307 };
40308
40309 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
40310         
40311         listWidth: undefined,
40312         
40313         selectedClass: 'active',
40314         
40315         invalidClass : "has-warning",
40316         
40317         validClass: 'has-success',
40318         
40319         allowed: '0123456789',
40320         
40321         max_length: 15,
40322         
40323         /**
40324          * @cfg {String} defaultDialCode The default dial code when initializing the input
40325          */
40326         defaultDialCode: '+852',
40327         
40328         /**
40329          * @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
40330          */
40331         preferedCountries: false,
40332         
40333         getAutoCreate : function()
40334         {
40335             var data = Roo.bootstrap.PhoneInputData();
40336             var align = this.labelAlign || this.parentLabelAlign();
40337             var id = Roo.id();
40338             
40339             this.allCountries = [];
40340             this.dialCodeMapping = [];
40341             
40342             for (var i = 0; i < data.length; i++) {
40343               var c = data[i];
40344               this.allCountries[i] = {
40345                 name: c[0],
40346                 iso2: c[1],
40347                 dialCode: c[2],
40348                 priority: c[3] || 0,
40349                 areaCodes: c[4] || null
40350               };
40351               this.dialCodeMapping[c[2]] = {
40352                   name: c[0],
40353                   iso2: c[1],
40354                   priority: c[3] || 0,
40355                   areaCodes: c[4] || null
40356               };
40357             }
40358             
40359             var cfg = {
40360                 cls: 'form-group',
40361                 cn: []
40362             };
40363             
40364             var input =  {
40365                 tag: 'input',
40366                 id : id,
40367                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40368                 maxlength: this.max_length,
40369                 cls : 'form-control tel-input',
40370                 autocomplete: 'new-password'
40371             };
40372             
40373             var hiddenInput = {
40374                 tag: 'input',
40375                 type: 'hidden',
40376                 cls: 'hidden-tel-input'
40377             };
40378             
40379             if (this.name) {
40380                 hiddenInput.name = this.name;
40381             }
40382             
40383             if (this.disabled) {
40384                 input.disabled = true;
40385             }
40386             
40387             var flag_container = {
40388                 tag: 'div',
40389                 cls: 'flag-box',
40390                 cn: [
40391                     {
40392                         tag: 'div',
40393                         cls: 'flag'
40394                     },
40395                     {
40396                         tag: 'div',
40397                         cls: 'caret'
40398                     }
40399                 ]
40400             };
40401             
40402             var box = {
40403                 tag: 'div',
40404                 cls: this.hasFeedback ? 'has-feedback' : '',
40405                 cn: [
40406                     hiddenInput,
40407                     input,
40408                     {
40409                         tag: 'input',
40410                         cls: 'dial-code-holder',
40411                         disabled: true
40412                     }
40413                 ]
40414             };
40415             
40416             var container = {
40417                 cls: 'roo-select2-container input-group',
40418                 cn: [
40419                     flag_container,
40420                     box
40421                 ]
40422             };
40423             
40424             if (this.fieldLabel.length) {
40425                 var indicator = {
40426                     tag: 'i',
40427                     tooltip: 'This field is required'
40428                 };
40429                 
40430                 var label = {
40431                     tag: 'label',
40432                     'for':  id,
40433                     cls: 'control-label',
40434                     cn: []
40435                 };
40436                 
40437                 var label_text = {
40438                     tag: 'span',
40439                     html: this.fieldLabel
40440                 };
40441                 
40442                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40443                 label.cn = [
40444                     indicator,
40445                     label_text
40446                 ];
40447                 
40448                 if(this.indicatorpos == 'right') {
40449                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40450                     label.cn = [
40451                         label_text,
40452                         indicator
40453                     ];
40454                 }
40455                 
40456                 if(align == 'left') {
40457                     container = {
40458                         tag: 'div',
40459                         cn: [
40460                             container
40461                         ]
40462                     };
40463                     
40464                     if(this.labelWidth > 12){
40465                         label.style = "width: " + this.labelWidth + 'px';
40466                     }
40467                     if(this.labelWidth < 13 && this.labelmd == 0){
40468                         this.labelmd = this.labelWidth;
40469                     }
40470                     if(this.labellg > 0){
40471                         label.cls += ' col-lg-' + this.labellg;
40472                         input.cls += ' col-lg-' + (12 - this.labellg);
40473                     }
40474                     if(this.labelmd > 0){
40475                         label.cls += ' col-md-' + this.labelmd;
40476                         container.cls += ' col-md-' + (12 - this.labelmd);
40477                     }
40478                     if(this.labelsm > 0){
40479                         label.cls += ' col-sm-' + this.labelsm;
40480                         container.cls += ' col-sm-' + (12 - this.labelsm);
40481                     }
40482                     if(this.labelxs > 0){
40483                         label.cls += ' col-xs-' + this.labelxs;
40484                         container.cls += ' col-xs-' + (12 - this.labelxs);
40485                     }
40486                 }
40487             }
40488             
40489             cfg.cn = [
40490                 label,
40491                 container
40492             ];
40493             
40494             var settings = this;
40495             
40496             ['xs','sm','md','lg'].map(function(size){
40497                 if (settings[size]) {
40498                     cfg.cls += ' col-' + size + '-' + settings[size];
40499                 }
40500             });
40501             
40502             this.store = new Roo.data.Store({
40503                 proxy : new Roo.data.MemoryProxy({}),
40504                 reader : new Roo.data.JsonReader({
40505                     fields : [
40506                         {
40507                             'name' : 'name',
40508                             'type' : 'string'
40509                         },
40510                         {
40511                             'name' : 'iso2',
40512                             'type' : 'string'
40513                         },
40514                         {
40515                             'name' : 'dialCode',
40516                             'type' : 'string'
40517                         },
40518                         {
40519                             'name' : 'priority',
40520                             'type' : 'string'
40521                         },
40522                         {
40523                             'name' : 'areaCodes',
40524                             'type' : 'string'
40525                         }
40526                     ]
40527                 })
40528             });
40529             
40530             if(!this.preferedCountries) {
40531                 this.preferedCountries = [
40532                     'hk',
40533                     'gb',
40534                     'us'
40535                 ];
40536             }
40537             
40538             var p = this.preferedCountries.reverse();
40539             
40540             if(p) {
40541                 for (var i = 0; i < p.length; i++) {
40542                     for (var j = 0; j < this.allCountries.length; j++) {
40543                         if(this.allCountries[j].iso2 == p[i]) {
40544                             var t = this.allCountries[j];
40545                             this.allCountries.splice(j,1);
40546                             this.allCountries.unshift(t);
40547                         }
40548                     } 
40549                 }
40550             }
40551             
40552             this.store.proxy.data = {
40553                 success: true,
40554                 data: this.allCountries
40555             };
40556             
40557             return cfg;
40558         },
40559         
40560         initEvents : function()
40561         {
40562             this.createList();
40563             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40564             
40565             this.indicator = this.indicatorEl();
40566             this.flag = this.flagEl();
40567             this.dialCodeHolder = this.dialCodeHolderEl();
40568             
40569             this.trigger = this.el.select('div.flag-box',true).first();
40570             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40571             
40572             var _this = this;
40573             
40574             (function(){
40575                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40576                 _this.list.setWidth(lw);
40577             }).defer(100);
40578             
40579             this.list.on('mouseover', this.onViewOver, this);
40580             this.list.on('mousemove', this.onViewMove, this);
40581             this.inputEl().on("keyup", this.onKeyUp, this);
40582             this.inputEl().on("keypress", this.onKeyPress, this);
40583             
40584             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40585
40586             this.view = new Roo.View(this.list, this.tpl, {
40587                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40588             });
40589             
40590             this.view.on('click', this.onViewClick, this);
40591             this.setValue(this.defaultDialCode);
40592         },
40593         
40594         onTriggerClick : function(e)
40595         {
40596             Roo.log('trigger click');
40597             if(this.disabled){
40598                 return;
40599             }
40600             
40601             if(this.isExpanded()){
40602                 this.collapse();
40603                 this.hasFocus = false;
40604             }else {
40605                 this.store.load({});
40606                 this.hasFocus = true;
40607                 this.expand();
40608             }
40609         },
40610         
40611         isExpanded : function()
40612         {
40613             return this.list.isVisible();
40614         },
40615         
40616         collapse : function()
40617         {
40618             if(!this.isExpanded()){
40619                 return;
40620             }
40621             this.list.hide();
40622             Roo.get(document).un('mousedown', this.collapseIf, this);
40623             Roo.get(document).un('mousewheel', this.collapseIf, this);
40624             this.fireEvent('collapse', this);
40625             this.validate();
40626         },
40627         
40628         expand : function()
40629         {
40630             Roo.log('expand');
40631
40632             if(this.isExpanded() || !this.hasFocus){
40633                 return;
40634             }
40635             
40636             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40637             this.list.setWidth(lw);
40638             
40639             this.list.show();
40640             this.restrictHeight();
40641             
40642             Roo.get(document).on('mousedown', this.collapseIf, this);
40643             Roo.get(document).on('mousewheel', this.collapseIf, this);
40644             
40645             this.fireEvent('expand', this);
40646         },
40647         
40648         restrictHeight : function()
40649         {
40650             this.list.alignTo(this.inputEl(), this.listAlign);
40651             this.list.alignTo(this.inputEl(), this.listAlign);
40652         },
40653         
40654         onViewOver : function(e, t)
40655         {
40656             if(this.inKeyMode){
40657                 return;
40658             }
40659             var item = this.view.findItemFromChild(t);
40660             
40661             if(item){
40662                 var index = this.view.indexOf(item);
40663                 this.select(index, false);
40664             }
40665         },
40666
40667         // private
40668         onViewClick : function(view, doFocus, el, e)
40669         {
40670             var index = this.view.getSelectedIndexes()[0];
40671             
40672             var r = this.store.getAt(index);
40673             
40674             if(r){
40675                 this.onSelect(r, index);
40676             }
40677             if(doFocus !== false && !this.blockFocus){
40678                 this.inputEl().focus();
40679             }
40680         },
40681         
40682         onViewMove : function(e, t)
40683         {
40684             this.inKeyMode = false;
40685         },
40686         
40687         select : function(index, scrollIntoView)
40688         {
40689             this.selectedIndex = index;
40690             this.view.select(index);
40691             if(scrollIntoView !== false){
40692                 var el = this.view.getNode(index);
40693                 if(el){
40694                     this.list.scrollChildIntoView(el, false);
40695                 }
40696             }
40697         },
40698         
40699         createList : function()
40700         {
40701             this.list = Roo.get(document.body).createChild({
40702                 tag: 'ul',
40703                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40704                 style: 'display:none'
40705             });
40706             
40707             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40708         },
40709         
40710         collapseIf : function(e)
40711         {
40712             var in_combo  = e.within(this.el);
40713             var in_list =  e.within(this.list);
40714             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40715             
40716             if (in_combo || in_list || is_list) {
40717                 return;
40718             }
40719             this.collapse();
40720         },
40721         
40722         onSelect : function(record, index)
40723         {
40724             if(this.fireEvent('beforeselect', this, record, index) !== false){
40725                 
40726                 this.setFlagClass(record.data.iso2);
40727                 this.setDialCode(record.data.dialCode);
40728                 this.hasFocus = false;
40729                 this.collapse();
40730                 this.fireEvent('select', this, record, index);
40731             }
40732         },
40733         
40734         flagEl : function()
40735         {
40736             var flag = this.el.select('div.flag',true).first();
40737             if(!flag){
40738                 return false;
40739             }
40740             return flag;
40741         },
40742         
40743         dialCodeHolderEl : function()
40744         {
40745             var d = this.el.select('input.dial-code-holder',true).first();
40746             if(!d){
40747                 return false;
40748             }
40749             return d;
40750         },
40751         
40752         setDialCode : function(v)
40753         {
40754             this.dialCodeHolder.dom.value = '+'+v;
40755         },
40756         
40757         setFlagClass : function(n)
40758         {
40759             this.flag.dom.className = 'flag '+n;
40760         },
40761         
40762         getValue : function()
40763         {
40764             var v = this.inputEl().getValue();
40765             if(this.dialCodeHolder) {
40766                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40767             }
40768             return v;
40769         },
40770         
40771         setValue : function(v)
40772         {
40773             var d = this.getDialCode(v);
40774             
40775             //invalid dial code
40776             if(v.length == 0 || !d || d.length == 0) {
40777                 if(this.rendered){
40778                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40779                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40780                 }
40781                 return;
40782             }
40783             
40784             //valid dial code
40785             this.setFlagClass(this.dialCodeMapping[d].iso2);
40786             this.setDialCode(d);
40787             this.inputEl().dom.value = v.replace('+'+d,'');
40788             this.hiddenEl().dom.value = this.getValue();
40789             
40790             this.validate();
40791         },
40792         
40793         getDialCode : function(v)
40794         {
40795             v = v ||  '';
40796             
40797             if (v.length == 0) {
40798                 return this.dialCodeHolder.dom.value;
40799             }
40800             
40801             var dialCode = "";
40802             if (v.charAt(0) != "+") {
40803                 return false;
40804             }
40805             var numericChars = "";
40806             for (var i = 1; i < v.length; i++) {
40807               var c = v.charAt(i);
40808               if (!isNaN(c)) {
40809                 numericChars += c;
40810                 if (this.dialCodeMapping[numericChars]) {
40811                   dialCode = v.substr(1, i);
40812                 }
40813                 if (numericChars.length == 4) {
40814                   break;
40815                 }
40816               }
40817             }
40818             return dialCode;
40819         },
40820         
40821         reset : function()
40822         {
40823             this.setValue(this.defaultDialCode);
40824             this.validate();
40825         },
40826         
40827         hiddenEl : function()
40828         {
40829             return this.el.select('input.hidden-tel-input',true).first();
40830         },
40831         
40832         // after setting val
40833         onKeyUp : function(e){
40834             this.setValue(this.getValue());
40835         },
40836         
40837         onKeyPress : function(e){
40838             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40839                 e.stopEvent();
40840             }
40841         }
40842         
40843 });
40844 /**
40845  * @class Roo.bootstrap.MoneyField
40846  * @extends Roo.bootstrap.ComboBox
40847  * Bootstrap MoneyField class
40848  * 
40849  * @constructor
40850  * Create a new MoneyField.
40851  * @param {Object} config Configuration options
40852  */
40853
40854 Roo.bootstrap.MoneyField = function(config) {
40855     
40856     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40857     
40858 };
40859
40860 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40861     
40862     /**
40863      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40864      */
40865     allowDecimals : true,
40866     /**
40867      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40868      */
40869     decimalSeparator : ".",
40870     /**
40871      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40872      */
40873     decimalPrecision : 0,
40874     /**
40875      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40876      */
40877     allowNegative : true,
40878     /**
40879      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40880      */
40881     allowZero: true,
40882     /**
40883      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40884      */
40885     minValue : Number.NEGATIVE_INFINITY,
40886     /**
40887      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40888      */
40889     maxValue : Number.MAX_VALUE,
40890     /**
40891      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40892      */
40893     minText : "The minimum value for this field is {0}",
40894     /**
40895      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40896      */
40897     maxText : "The maximum value for this field is {0}",
40898     /**
40899      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40900      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40901      */
40902     nanText : "{0} is not a valid number",
40903     /**
40904      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40905      */
40906     castInt : true,
40907     /**
40908      * @cfg {String} defaults currency of the MoneyField
40909      * value should be in lkey
40910      */
40911     defaultCurrency : false,
40912     /**
40913      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40914      */
40915     thousandsDelimiter : false,
40916     /**
40917      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40918      */
40919     max_length: false,
40920     
40921     inputlg : 9,
40922     inputmd : 9,
40923     inputsm : 9,
40924     inputxs : 6,
40925     
40926     store : false,
40927     
40928     getAutoCreate : function()
40929     {
40930         var align = this.labelAlign || this.parentLabelAlign();
40931         
40932         var id = Roo.id();
40933
40934         var cfg = {
40935             cls: 'form-group',
40936             cn: []
40937         };
40938
40939         var input =  {
40940             tag: 'input',
40941             id : id,
40942             cls : 'form-control roo-money-amount-input',
40943             autocomplete: 'new-password'
40944         };
40945         
40946         var hiddenInput = {
40947             tag: 'input',
40948             type: 'hidden',
40949             id: Roo.id(),
40950             cls: 'hidden-number-input'
40951         };
40952         
40953         if(this.max_length) {
40954             input.maxlength = this.max_length; 
40955         }
40956         
40957         if (this.name) {
40958             hiddenInput.name = this.name;
40959         }
40960
40961         if (this.disabled) {
40962             input.disabled = true;
40963         }
40964
40965         var clg = 12 - this.inputlg;
40966         var cmd = 12 - this.inputmd;
40967         var csm = 12 - this.inputsm;
40968         var cxs = 12 - this.inputxs;
40969         
40970         var container = {
40971             tag : 'div',
40972             cls : 'row roo-money-field',
40973             cn : [
40974                 {
40975                     tag : 'div',
40976                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40977                     cn : [
40978                         {
40979                             tag : 'div',
40980                             cls: 'roo-select2-container input-group',
40981                             cn: [
40982                                 {
40983                                     tag : 'input',
40984                                     cls : 'form-control roo-money-currency-input',
40985                                     autocomplete: 'new-password',
40986                                     readOnly : 1,
40987                                     name : this.currencyName
40988                                 },
40989                                 {
40990                                     tag :'span',
40991                                     cls : 'input-group-addon',
40992                                     cn : [
40993                                         {
40994                                             tag: 'span',
40995                                             cls: 'caret'
40996                                         }
40997                                     ]
40998                                 }
40999                             ]
41000                         }
41001                     ]
41002                 },
41003                 {
41004                     tag : 'div',
41005                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
41006                     cn : [
41007                         {
41008                             tag: 'div',
41009                             cls: this.hasFeedback ? 'has-feedback' : '',
41010                             cn: [
41011                                 input
41012                             ]
41013                         }
41014                     ]
41015                 }
41016             ]
41017             
41018         };
41019         
41020         if (this.fieldLabel.length) {
41021             var indicator = {
41022                 tag: 'i',
41023                 tooltip: 'This field is required'
41024             };
41025
41026             var label = {
41027                 tag: 'label',
41028                 'for':  id,
41029                 cls: 'control-label',
41030                 cn: []
41031             };
41032
41033             var label_text = {
41034                 tag: 'span',
41035                 html: this.fieldLabel
41036             };
41037
41038             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
41039             label.cn = [
41040                 indicator,
41041                 label_text
41042             ];
41043
41044             if(this.indicatorpos == 'right') {
41045                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
41046                 label.cn = [
41047                     label_text,
41048                     indicator
41049                 ];
41050             }
41051
41052             if(align == 'left') {
41053                 container = {
41054                     tag: 'div',
41055                     cn: [
41056                         container
41057                     ]
41058                 };
41059
41060                 if(this.labelWidth > 12){
41061                     label.style = "width: " + this.labelWidth + 'px';
41062                 }
41063                 if(this.labelWidth < 13 && this.labelmd == 0){
41064                     this.labelmd = this.labelWidth;
41065                 }
41066                 if(this.labellg > 0){
41067                     label.cls += ' col-lg-' + this.labellg;
41068                     input.cls += ' col-lg-' + (12 - this.labellg);
41069                 }
41070                 if(this.labelmd > 0){
41071                     label.cls += ' col-md-' + this.labelmd;
41072                     container.cls += ' col-md-' + (12 - this.labelmd);
41073                 }
41074                 if(this.labelsm > 0){
41075                     label.cls += ' col-sm-' + this.labelsm;
41076                     container.cls += ' col-sm-' + (12 - this.labelsm);
41077                 }
41078                 if(this.labelxs > 0){
41079                     label.cls += ' col-xs-' + this.labelxs;
41080                     container.cls += ' col-xs-' + (12 - this.labelxs);
41081                 }
41082             }
41083         }
41084
41085         cfg.cn = [
41086             label,
41087             container,
41088             hiddenInput
41089         ];
41090         
41091         var settings = this;
41092
41093         ['xs','sm','md','lg'].map(function(size){
41094             if (settings[size]) {
41095                 cfg.cls += ' col-' + size + '-' + settings[size];
41096             }
41097         });
41098         
41099         return cfg;
41100     },
41101     
41102     initEvents : function()
41103     {
41104         this.indicator = this.indicatorEl();
41105         
41106         this.initCurrencyEvent();
41107         
41108         this.initNumberEvent();
41109     },
41110     
41111     initCurrencyEvent : function()
41112     {
41113         if (!this.store) {
41114             throw "can not find store for combo";
41115         }
41116         
41117         this.store = Roo.factory(this.store, Roo.data);
41118         this.store.parent = this;
41119         
41120         this.createList();
41121         
41122         this.triggerEl = this.el.select('.input-group-addon', true).first();
41123         
41124         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
41125         
41126         var _this = this;
41127         
41128         (function(){
41129             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
41130             _this.list.setWidth(lw);
41131         }).defer(100);
41132         
41133         this.list.on('mouseover', this.onViewOver, this);
41134         this.list.on('mousemove', this.onViewMove, this);
41135         this.list.on('scroll', this.onViewScroll, this);
41136         
41137         if(!this.tpl){
41138             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
41139         }
41140         
41141         this.view = new Roo.View(this.list, this.tpl, {
41142             singleSelect:true, store: this.store, selectedClass: this.selectedClass
41143         });
41144         
41145         this.view.on('click', this.onViewClick, this);
41146         
41147         this.store.on('beforeload', this.onBeforeLoad, this);
41148         this.store.on('load', this.onLoad, this);
41149         this.store.on('loadexception', this.onLoadException, this);
41150         
41151         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
41152             "up" : function(e){
41153                 this.inKeyMode = true;
41154                 this.selectPrev();
41155             },
41156
41157             "down" : function(e){
41158                 if(!this.isExpanded()){
41159                     this.onTriggerClick();
41160                 }else{
41161                     this.inKeyMode = true;
41162                     this.selectNext();
41163                 }
41164             },
41165
41166             "enter" : function(e){
41167                 this.collapse();
41168                 
41169                 if(this.fireEvent("specialkey", this, e)){
41170                     this.onViewClick(false);
41171                 }
41172                 
41173                 return true;
41174             },
41175
41176             "esc" : function(e){
41177                 this.collapse();
41178             },
41179
41180             "tab" : function(e){
41181                 this.collapse();
41182                 
41183                 if(this.fireEvent("specialkey", this, e)){
41184                     this.onViewClick(false);
41185                 }
41186                 
41187                 return true;
41188             },
41189
41190             scope : this,
41191
41192             doRelay : function(foo, bar, hname){
41193                 if(hname == 'down' || this.scope.isExpanded()){
41194                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41195                 }
41196                 return true;
41197             },
41198
41199             forceKeyDown: true
41200         });
41201         
41202         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
41203         
41204     },
41205     
41206     initNumberEvent : function(e)
41207     {
41208         this.inputEl().on("keydown" , this.fireKey,  this);
41209         this.inputEl().on("focus", this.onFocus,  this);
41210         this.inputEl().on("blur", this.onBlur,  this);
41211         
41212         this.inputEl().relayEvent('keyup', this);
41213         
41214         if(this.indicator){
41215             this.indicator.addClass('invisible');
41216         }
41217  
41218         this.originalValue = this.getValue();
41219         
41220         if(this.validationEvent == 'keyup'){
41221             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
41222             this.inputEl().on('keyup', this.filterValidation, this);
41223         }
41224         else if(this.validationEvent !== false){
41225             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
41226         }
41227         
41228         if(this.selectOnFocus){
41229             this.on("focus", this.preFocus, this);
41230             
41231         }
41232         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41233             this.inputEl().on("keypress", this.filterKeys, this);
41234         } else {
41235             this.inputEl().relayEvent('keypress', this);
41236         }
41237         
41238         var allowed = "0123456789";
41239         
41240         if(this.allowDecimals){
41241             allowed += this.decimalSeparator;
41242         }
41243         
41244         if(this.allowNegative){
41245             allowed += "-";
41246         }
41247         
41248         if(this.thousandsDelimiter) {
41249             allowed += ",";
41250         }
41251         
41252         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41253         
41254         var keyPress = function(e){
41255             
41256             var k = e.getKey();
41257             
41258             var c = e.getCharCode();
41259             
41260             if(
41261                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41262                     allowed.indexOf(String.fromCharCode(c)) === -1
41263             ){
41264                 e.stopEvent();
41265                 return;
41266             }
41267             
41268             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41269                 return;
41270             }
41271             
41272             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41273                 e.stopEvent();
41274             }
41275         };
41276         
41277         this.inputEl().on("keypress", keyPress, this);
41278         
41279     },
41280     
41281     onTriggerClick : function(e)
41282     {   
41283         if(this.disabled){
41284             return;
41285         }
41286         
41287         this.page = 0;
41288         this.loadNext = false;
41289         
41290         if(this.isExpanded()){
41291             this.collapse();
41292             return;
41293         }
41294         
41295         this.hasFocus = true;
41296         
41297         if(this.triggerAction == 'all') {
41298             this.doQuery(this.allQuery, true);
41299             return;
41300         }
41301         
41302         this.doQuery(this.getRawValue());
41303     },
41304     
41305     getCurrency : function()
41306     {   
41307         var v = this.currencyEl().getValue();
41308         
41309         return v;
41310     },
41311     
41312     restrictHeight : function()
41313     {
41314         this.list.alignTo(this.currencyEl(), this.listAlign);
41315         this.list.alignTo(this.currencyEl(), this.listAlign);
41316     },
41317     
41318     onViewClick : function(view, doFocus, el, e)
41319     {
41320         var index = this.view.getSelectedIndexes()[0];
41321         
41322         var r = this.store.getAt(index);
41323         
41324         if(r){
41325             this.onSelect(r, index);
41326         }
41327     },
41328     
41329     onSelect : function(record, index){
41330         
41331         if(this.fireEvent('beforeselect', this, record, index) !== false){
41332         
41333             this.setFromCurrencyData(index > -1 ? record.data : false);
41334             
41335             this.collapse();
41336             
41337             this.fireEvent('select', this, record, index);
41338         }
41339     },
41340     
41341     setFromCurrencyData : function(o)
41342     {
41343         var currency = '';
41344         
41345         this.lastCurrency = o;
41346         
41347         if (this.currencyField) {
41348             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41349         } else {
41350             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
41351         }
41352         
41353         this.lastSelectionText = currency;
41354         
41355         //setting default currency
41356         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41357             this.setCurrency(this.defaultCurrency);
41358             return;
41359         }
41360         
41361         this.setCurrency(currency);
41362     },
41363     
41364     setFromData : function(o)
41365     {
41366         var c = {};
41367         
41368         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41369         
41370         this.setFromCurrencyData(c);
41371         
41372         var value = '';
41373         
41374         if (this.name) {
41375             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41376         } else {
41377             Roo.log('no value set for '+ (this.name ? this.name : this.id));
41378         }
41379         
41380         this.setValue(value);
41381         
41382     },
41383     
41384     setCurrency : function(v)
41385     {   
41386         this.currencyValue = v;
41387         
41388         if(this.rendered){
41389             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41390             this.validate();
41391         }
41392     },
41393     
41394     setValue : function(v)
41395     {
41396         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41397         
41398         this.value = v;
41399         
41400         if(this.rendered){
41401             
41402             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41403             
41404             this.inputEl().dom.value = (v == '') ? '' :
41405                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41406             
41407             if(!this.allowZero && v === '0') {
41408                 this.hiddenEl().dom.value = '';
41409                 this.inputEl().dom.value = '';
41410             }
41411             
41412             this.validate();
41413         }
41414     },
41415     
41416     getRawValue : function()
41417     {
41418         var v = this.inputEl().getValue();
41419         
41420         return v;
41421     },
41422     
41423     getValue : function()
41424     {
41425         return this.fixPrecision(this.parseValue(this.getRawValue()));
41426     },
41427     
41428     parseValue : function(value)
41429     {
41430         if(this.thousandsDelimiter) {
41431             value += "";
41432             r = new RegExp(",", "g");
41433             value = value.replace(r, "");
41434         }
41435         
41436         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41437         return isNaN(value) ? '' : value;
41438         
41439     },
41440     
41441     fixPrecision : function(value)
41442     {
41443         if(this.thousandsDelimiter) {
41444             value += "";
41445             r = new RegExp(",", "g");
41446             value = value.replace(r, "");
41447         }
41448         
41449         var nan = isNaN(value);
41450         
41451         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41452             return nan ? '' : value;
41453         }
41454         return parseFloat(value).toFixed(this.decimalPrecision);
41455     },
41456     
41457     decimalPrecisionFcn : function(v)
41458     {
41459         return Math.floor(v);
41460     },
41461     
41462     validateValue : function(value)
41463     {
41464         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41465             return false;
41466         }
41467         
41468         var num = this.parseValue(value);
41469         
41470         if(isNaN(num)){
41471             this.markInvalid(String.format(this.nanText, value));
41472             return false;
41473         }
41474         
41475         if(num < this.minValue){
41476             this.markInvalid(String.format(this.minText, this.minValue));
41477             return false;
41478         }
41479         
41480         if(num > this.maxValue){
41481             this.markInvalid(String.format(this.maxText, this.maxValue));
41482             return false;
41483         }
41484         
41485         return true;
41486     },
41487     
41488     validate : function()
41489     {
41490         if(this.disabled || this.allowBlank){
41491             this.markValid();
41492             return true;
41493         }
41494         
41495         var currency = this.getCurrency();
41496         
41497         if(this.validateValue(this.getRawValue()) && currency.length){
41498             this.markValid();
41499             return true;
41500         }
41501         
41502         this.markInvalid();
41503         return false;
41504     },
41505     
41506     getName: function()
41507     {
41508         return this.name;
41509     },
41510     
41511     beforeBlur : function()
41512     {
41513         if(!this.castInt){
41514             return;
41515         }
41516         
41517         var v = this.parseValue(this.getRawValue());
41518         
41519         if(v || v == 0){
41520             this.setValue(v);
41521         }
41522     },
41523     
41524     onBlur : function()
41525     {
41526         this.beforeBlur();
41527         
41528         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41529             //this.el.removeClass(this.focusClass);
41530         }
41531         
41532         this.hasFocus = false;
41533         
41534         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41535             this.validate();
41536         }
41537         
41538         var v = this.getValue();
41539         
41540         if(String(v) !== String(this.startValue)){
41541             this.fireEvent('change', this, v, this.startValue);
41542         }
41543         
41544         this.fireEvent("blur", this);
41545     },
41546     
41547     inputEl : function()
41548     {
41549         return this.el.select('.roo-money-amount-input', true).first();
41550     },
41551     
41552     currencyEl : function()
41553     {
41554         return this.el.select('.roo-money-currency-input', true).first();
41555     },
41556     
41557     hiddenEl : function()
41558     {
41559         return this.el.select('input.hidden-number-input',true).first();
41560     }
41561     
41562 });/**
41563  * @class Roo.bootstrap.BezierSignature
41564  * @extends Roo.bootstrap.Component
41565  * Bootstrap BezierSignature class
41566  * This script refer to:
41567  *    Title: Signature Pad
41568  *    Author: szimek
41569  *    Availability: https://github.com/szimek/signature_pad
41570  *
41571  * @constructor
41572  * Create a new BezierSignature
41573  * @param {Object} config The config object
41574  */
41575
41576 Roo.bootstrap.BezierSignature = function(config){
41577     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
41578     this.addEvents({
41579         "resize" : true
41580     });
41581 };
41582
41583 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
41584 {
41585      
41586     curve_data: [],
41587     
41588     is_empty: true,
41589     
41590     mouse_btn_down: true,
41591     
41592     /**
41593      * @cfg {int} canvas height
41594      */
41595     canvas_height: '200px',
41596     
41597     /**
41598      * @cfg {float|function} Radius of a single dot.
41599      */ 
41600     dot_size: false,
41601     
41602     /**
41603      * @cfg {float} Minimum width of a line. Defaults to 0.5.
41604      */
41605     min_width: 0.5,
41606     
41607     /**
41608      * @cfg {float} Maximum width of a line. Defaults to 2.5.
41609      */
41610     max_width: 2.5,
41611     
41612     /**
41613      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
41614      */
41615     throttle: 16,
41616     
41617     /**
41618      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
41619      */
41620     min_distance: 5,
41621     
41622     /**
41623      * @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.
41624      */
41625     bg_color: 'rgba(0, 0, 0, 0)',
41626     
41627     /**
41628      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
41629      */
41630     dot_color: 'black',
41631     
41632     /**
41633      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
41634      */ 
41635     velocity_filter_weight: 0.7,
41636     
41637     /**
41638      * @cfg {function} Callback when stroke begin. 
41639      */
41640     onBegin: false,
41641     
41642     /**
41643      * @cfg {function} Callback when stroke end.
41644      */
41645     onEnd: false,
41646     
41647     getAutoCreate : function()
41648     {
41649         var cls = 'roo-signature column';
41650         
41651         if(this.cls){
41652             cls += ' ' + this.cls;
41653         }
41654         
41655         var col_sizes = [
41656             'lg',
41657             'md',
41658             'sm',
41659             'xs'
41660         ];
41661         
41662         for(var i = 0; i < col_sizes.length; i++) {
41663             if(this[col_sizes[i]]) {
41664                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
41665             }
41666         }
41667         
41668         var cfg = {
41669             tag: 'div',
41670             cls: cls,
41671             cn: [
41672                 {
41673                     tag: 'div',
41674                     cls: 'roo-signature-body',
41675                     cn: [
41676                         {
41677                             tag: 'canvas',
41678                             cls: 'roo-signature-body-canvas',
41679                             height: this.canvas_height,
41680                             width: this.canvas_width
41681                         }
41682                     ]
41683                 },
41684                 {
41685                     tag: 'input',
41686                     type: 'file',
41687                     style: 'display: none'
41688                 }
41689             ]
41690         };
41691         
41692         return cfg;
41693     },
41694     
41695     initEvents: function() 
41696     {
41697         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
41698         
41699         var canvas = this.canvasEl();
41700         
41701         // mouse && touch event swapping...
41702         canvas.dom.style.touchAction = 'none';
41703         canvas.dom.style.msTouchAction = 'none';
41704         
41705         this.mouse_btn_down = false;
41706         canvas.on('mousedown', this._handleMouseDown, this);
41707         canvas.on('mousemove', this._handleMouseMove, this);
41708         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
41709         
41710         if (window.PointerEvent) {
41711             canvas.on('pointerdown', this._handleMouseDown, this);
41712             canvas.on('pointermove', this._handleMouseMove, this);
41713             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
41714         }
41715         
41716         if ('ontouchstart' in window) {
41717             canvas.on('touchstart', this._handleTouchStart, this);
41718             canvas.on('touchmove', this._handleTouchMove, this);
41719             canvas.on('touchend', this._handleTouchEnd, this);
41720         }
41721         
41722         Roo.EventManager.onWindowResize(this.resize, this, true);
41723         
41724         // file input event
41725         this.fileEl().on('change', this.uploadImage, this);
41726         
41727         this.clear();
41728         
41729         this.resize();
41730     },
41731     
41732     resize: function(){
41733         
41734         var canvas = this.canvasEl().dom;
41735         var ctx = this.canvasElCtx();
41736         var img_data = false;
41737         
41738         if(canvas.width > 0) {
41739             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
41740         }
41741         // setting canvas width will clean img data
41742         canvas.width = 0;
41743         
41744         var style = window.getComputedStyle ? 
41745             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
41746             
41747         var padding_left = parseInt(style.paddingLeft) || 0;
41748         var padding_right = parseInt(style.paddingRight) || 0;
41749         
41750         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
41751         
41752         if(img_data) {
41753             ctx.putImageData(img_data, 0, 0);
41754         }
41755     },
41756     
41757     _handleMouseDown: function(e)
41758     {
41759         if (e.browserEvent.which === 1) {
41760             this.mouse_btn_down = true;
41761             this.strokeBegin(e);
41762         }
41763     },
41764     
41765     _handleMouseMove: function (e)
41766     {
41767         if (this.mouse_btn_down) {
41768             this.strokeMoveUpdate(e);
41769         }
41770     },
41771     
41772     _handleMouseUp: function (e)
41773     {
41774         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
41775             this.mouse_btn_down = false;
41776             this.strokeEnd(e);
41777         }
41778     },
41779     
41780     _handleTouchStart: function (e) {
41781         
41782         e.preventDefault();
41783         if (e.browserEvent.targetTouches.length === 1) {
41784             // var touch = e.browserEvent.changedTouches[0];
41785             // this.strokeBegin(touch);
41786             
41787              this.strokeBegin(e); // assume e catching the correct xy...
41788         }
41789     },
41790     
41791     _handleTouchMove: function (e) {
41792         e.preventDefault();
41793         // var touch = event.targetTouches[0];
41794         // _this._strokeMoveUpdate(touch);
41795         this.strokeMoveUpdate(e);
41796     },
41797     
41798     _handleTouchEnd: function (e) {
41799         var wasCanvasTouched = e.target === this.canvasEl().dom;
41800         if (wasCanvasTouched) {
41801             e.preventDefault();
41802             // var touch = event.changedTouches[0];
41803             // _this._strokeEnd(touch);
41804             this.strokeEnd(e);
41805         }
41806     },
41807     
41808     reset: function () {
41809         this._lastPoints = [];
41810         this._lastVelocity = 0;
41811         this._lastWidth = (this.min_width + this.max_width) / 2;
41812         this.canvasElCtx().fillStyle = this.dot_color;
41813     },
41814     
41815     strokeMoveUpdate: function(e)
41816     {
41817         this.strokeUpdate(e);
41818         
41819         if (this.throttle) {
41820             this.throttleStroke(this.strokeUpdate, this.throttle);
41821         }
41822         else {
41823             this.strokeUpdate(e);
41824         }
41825     },
41826     
41827     strokeBegin: function(e)
41828     {
41829         var newPointGroup = {
41830             color: this.dot_color,
41831             points: []
41832         };
41833         
41834         if (typeof this.onBegin === 'function') {
41835             this.onBegin(e);
41836         }
41837         
41838         this.curve_data.push(newPointGroup);
41839         this.reset();
41840         this.strokeUpdate(e);
41841     },
41842     
41843     strokeUpdate: function(e)
41844     {
41845         var rect = this.canvasEl().dom.getBoundingClientRect();
41846         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
41847         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
41848         var lastPoints = lastPointGroup.points;
41849         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
41850         var isLastPointTooClose = lastPoint
41851             ? point.distanceTo(lastPoint) <= this.min_distance
41852             : false;
41853         var color = lastPointGroup.color;
41854         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
41855             var curve = this.addPoint(point);
41856             if (!lastPoint) {
41857                 this.drawDot({color: color, point: point});
41858             }
41859             else if (curve) {
41860                 this.drawCurve({color: color, curve: curve});
41861             }
41862             lastPoints.push({
41863                 time: point.time,
41864                 x: point.x,
41865                 y: point.y
41866             });
41867         }
41868     },
41869     
41870     strokeEnd: function(e)
41871     {
41872         this.strokeUpdate(e);
41873         if (typeof this.onEnd === 'function') {
41874             this.onEnd(e);
41875         }
41876     },
41877     
41878     addPoint:  function (point) {
41879         var _lastPoints = this._lastPoints;
41880         _lastPoints.push(point);
41881         if (_lastPoints.length > 2) {
41882             if (_lastPoints.length === 3) {
41883                 _lastPoints.unshift(_lastPoints[0]);
41884             }
41885             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
41886             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
41887             _lastPoints.shift();
41888             return curve;
41889         }
41890         return null;
41891     },
41892     
41893     calculateCurveWidths: function (startPoint, endPoint) {
41894         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
41895             (1 - this.velocity_filter_weight) * this._lastVelocity;
41896
41897         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
41898         var widths = {
41899             end: newWidth,
41900             start: this._lastWidth
41901         };
41902         
41903         this._lastVelocity = velocity;
41904         this._lastWidth = newWidth;
41905         return widths;
41906     },
41907     
41908     drawDot: function (_a) {
41909         var color = _a.color, point = _a.point;
41910         var ctx = this.canvasElCtx();
41911         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
41912         ctx.beginPath();
41913         this.drawCurveSegment(point.x, point.y, width);
41914         ctx.closePath();
41915         ctx.fillStyle = color;
41916         ctx.fill();
41917     },
41918     
41919     drawCurve: function (_a) {
41920         var color = _a.color, curve = _a.curve;
41921         var ctx = this.canvasElCtx();
41922         var widthDelta = curve.endWidth - curve.startWidth;
41923         var drawSteps = Math.floor(curve.length()) * 2;
41924         ctx.beginPath();
41925         ctx.fillStyle = color;
41926         for (var i = 0; i < drawSteps; i += 1) {
41927         var t = i / drawSteps;
41928         var tt = t * t;
41929         var ttt = tt * t;
41930         var u = 1 - t;
41931         var uu = u * u;
41932         var uuu = uu * u;
41933         var x = uuu * curve.startPoint.x;
41934         x += 3 * uu * t * curve.control1.x;
41935         x += 3 * u * tt * curve.control2.x;
41936         x += ttt * curve.endPoint.x;
41937         var y = uuu * curve.startPoint.y;
41938         y += 3 * uu * t * curve.control1.y;
41939         y += 3 * u * tt * curve.control2.y;
41940         y += ttt * curve.endPoint.y;
41941         var width = curve.startWidth + ttt * widthDelta;
41942         this.drawCurveSegment(x, y, width);
41943         }
41944         ctx.closePath();
41945         ctx.fill();
41946     },
41947     
41948     drawCurveSegment: function (x, y, width) {
41949         var ctx = this.canvasElCtx();
41950         ctx.moveTo(x, y);
41951         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
41952         this.is_empty = false;
41953     },
41954     
41955     clear: function()
41956     {
41957         var ctx = this.canvasElCtx();
41958         var canvas = this.canvasEl().dom;
41959         ctx.fillStyle = this.bg_color;
41960         ctx.clearRect(0, 0, canvas.width, canvas.height);
41961         ctx.fillRect(0, 0, canvas.width, canvas.height);
41962         this.curve_data = [];
41963         this.reset();
41964         this.is_empty = true;
41965     },
41966     
41967     fileEl: function()
41968     {
41969         return  this.el.select('input',true).first();
41970     },
41971     
41972     canvasEl: function()
41973     {
41974         return this.el.select('canvas',true).first();
41975     },
41976     
41977     canvasElCtx: function()
41978     {
41979         return this.el.select('canvas',true).first().dom.getContext('2d');
41980     },
41981     
41982     getImage: function(type)
41983     {
41984         if(this.is_empty) {
41985             return false;
41986         }
41987         
41988         // encryption ?
41989         return this.canvasEl().dom.toDataURL('image/'+type, 1);
41990     },
41991     
41992     drawFromImage: function(img_src)
41993     {
41994         var img = new Image();
41995         
41996         img.onload = function(){
41997             this.canvasElCtx().drawImage(img, 0, 0);
41998         }.bind(this);
41999         
42000         img.src = img_src;
42001         
42002         this.is_empty = false;
42003     },
42004     
42005     selectImage: function()
42006     {
42007         this.fileEl().dom.click();
42008     },
42009     
42010     uploadImage: function(e)
42011     {
42012         var reader = new FileReader();
42013         
42014         reader.onload = function(e){
42015             var img = new Image();
42016             img.onload = function(){
42017                 this.reset();
42018                 this.canvasElCtx().drawImage(img, 0, 0);
42019             }.bind(this);
42020             img.src = e.target.result;
42021         }.bind(this);
42022         
42023         reader.readAsDataURL(e.target.files[0]);
42024     },
42025     
42026     // Bezier Point Constructor
42027     Point: (function () {
42028         function Point(x, y, time) {
42029             this.x = x;
42030             this.y = y;
42031             this.time = time || Date.now();
42032         }
42033         Point.prototype.distanceTo = function (start) {
42034             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
42035         };
42036         Point.prototype.equals = function (other) {
42037             return this.x === other.x && this.y === other.y && this.time === other.time;
42038         };
42039         Point.prototype.velocityFrom = function (start) {
42040             return this.time !== start.time
42041             ? this.distanceTo(start) / (this.time - start.time)
42042             : 0;
42043         };
42044         return Point;
42045     }()),
42046     
42047     
42048     // Bezier Constructor
42049     Bezier: (function () {
42050         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
42051             this.startPoint = startPoint;
42052             this.control2 = control2;
42053             this.control1 = control1;
42054             this.endPoint = endPoint;
42055             this.startWidth = startWidth;
42056             this.endWidth = endWidth;
42057         }
42058         Bezier.fromPoints = function (points, widths, scope) {
42059             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
42060             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
42061             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
42062         };
42063         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
42064             var dx1 = s1.x - s2.x;
42065             var dy1 = s1.y - s2.y;
42066             var dx2 = s2.x - s3.x;
42067             var dy2 = s2.y - s3.y;
42068             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
42069             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
42070             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
42071             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
42072             var dxm = m1.x - m2.x;
42073             var dym = m1.y - m2.y;
42074             var k = l2 / (l1 + l2);
42075             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
42076             var tx = s2.x - cm.x;
42077             var ty = s2.y - cm.y;
42078             return {
42079                 c1: new scope.Point(m1.x + tx, m1.y + ty),
42080                 c2: new scope.Point(m2.x + tx, m2.y + ty)
42081             };
42082         };
42083         Bezier.prototype.length = function () {
42084             var steps = 10;
42085             var length = 0;
42086             var px;
42087             var py;
42088             for (var i = 0; i <= steps; i += 1) {
42089                 var t = i / steps;
42090                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
42091                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
42092                 if (i > 0) {
42093                     var xdiff = cx - px;
42094                     var ydiff = cy - py;
42095                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
42096                 }
42097                 px = cx;
42098                 py = cy;
42099             }
42100             return length;
42101         };
42102         Bezier.prototype.point = function (t, start, c1, c2, end) {
42103             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
42104             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
42105             + (3.0 * c2 * (1.0 - t) * t * t)
42106             + (end * t * t * t);
42107         };
42108         return Bezier;
42109     }()),
42110     
42111     throttleStroke: function(fn, wait) {
42112       if (wait === void 0) { wait = 250; }
42113       var previous = 0;
42114       var timeout = null;
42115       var result;
42116       var storedContext;
42117       var storedArgs;
42118       var later = function () {
42119           previous = Date.now();
42120           timeout = null;
42121           result = fn.apply(storedContext, storedArgs);
42122           if (!timeout) {
42123               storedContext = null;
42124               storedArgs = [];
42125           }
42126       };
42127       return function wrapper() {
42128           var args = [];
42129           for (var _i = 0; _i < arguments.length; _i++) {
42130               args[_i] = arguments[_i];
42131           }
42132           var now = Date.now();
42133           var remaining = wait - (now - previous);
42134           storedContext = this;
42135           storedArgs = args;
42136           if (remaining <= 0 || remaining > wait) {
42137               if (timeout) {
42138                   clearTimeout(timeout);
42139                   timeout = null;
42140               }
42141               previous = now;
42142               result = fn.apply(storedContext, storedArgs);
42143               if (!timeout) {
42144                   storedContext = null;
42145                   storedArgs = [];
42146               }
42147           }
42148           else if (!timeout) {
42149               timeout = window.setTimeout(later, remaining);
42150           }
42151           return result;
42152       };
42153   }
42154   
42155 });
42156
42157  
42158
42159