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;
1076                 return;
1077             }
1078             cfg.cls += ' col-' + size + '-' + settings[size];
1079             
1080         });
1081         
1082         if (this.hidden) {
1083             cfg.cls += ' hidden';
1084         }
1085         
1086         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1087             cfg.cls +=' alert alert-' + this.alert;
1088         }
1089         
1090         
1091         if (this.html.length) {
1092             cfg.html = this.html;
1093         }
1094         if (this.fa) {
1095             var fasize = '';
1096             if (this.fasize > 1) {
1097                 fasize = ' fa-' + this.fasize + 'x';
1098             }
1099             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1100             
1101             
1102         }
1103         if (this.icon) {
1104             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1105         }
1106         
1107         return cfg;
1108     }
1109    
1110 });
1111
1112  
1113
1114  /*
1115  * - LGPL
1116  *
1117  * page container.
1118  * 
1119  */
1120
1121
1122 /**
1123  * @class Roo.bootstrap.Container
1124  * @extends Roo.bootstrap.Component
1125  * Bootstrap Container class
1126  * @cfg {Boolean} jumbotron is it a jumbotron element
1127  * @cfg {String} html content of element
1128  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1129  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1130  * @cfg {String} header content of header (for panel)
1131  * @cfg {String} footer content of footer (for panel)
1132  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1133  * @cfg {String} tag (header|aside|section) type of HTML tag.
1134  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1135  * @cfg {String} fa font awesome icon
1136  * @cfg {String} icon (info-sign|check|...) glyphicon name
1137  * @cfg {Boolean} hidden (true|false) hide the element
1138  * @cfg {Boolean} expandable (true|false) default false
1139  * @cfg {Boolean} expanded (true|false) default true
1140  * @cfg {String} rheader contet on the right of header
1141  * @cfg {Boolean} clickable (true|false) default false
1142
1143  *     
1144  * @constructor
1145  * Create a new Container
1146  * @param {Object} config The config object
1147  */
1148
1149 Roo.bootstrap.Container = function(config){
1150     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1151     
1152     this.addEvents({
1153         // raw events
1154          /**
1155          * @event expand
1156          * After the panel has been expand
1157          * 
1158          * @param {Roo.bootstrap.Container} this
1159          */
1160         "expand" : true,
1161         /**
1162          * @event collapse
1163          * After the panel has been collapsed
1164          * 
1165          * @param {Roo.bootstrap.Container} this
1166          */
1167         "collapse" : true,
1168         /**
1169          * @event click
1170          * When a element is chick
1171          * @param {Roo.bootstrap.Container} this
1172          * @param {Roo.EventObject} e
1173          */
1174         "click" : true
1175     });
1176 };
1177
1178 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1179     
1180     jumbotron : false,
1181     well: '',
1182     panel : '',
1183     header: '',
1184     footer : '',
1185     sticky: '',
1186     tag : false,
1187     alert : false,
1188     fa: false,
1189     icon : false,
1190     expandable : false,
1191     rheader : '',
1192     expanded : true,
1193     clickable: false,
1194   
1195      
1196     getChildContainer : function() {
1197         
1198         if(!this.el){
1199             return false;
1200         }
1201         
1202         if (this.panel.length) {
1203             return this.el.select('.panel-body',true).first();
1204         }
1205         
1206         return this.el;
1207     },
1208     
1209     
1210     getAutoCreate : function(){
1211         
1212         var cfg = {
1213             tag : this.tag || 'div',
1214             html : '',
1215             cls : ''
1216         };
1217         if (this.jumbotron) {
1218             cfg.cls = 'jumbotron';
1219         }
1220         
1221         
1222         
1223         // - this is applied by the parent..
1224         //if (this.cls) {
1225         //    cfg.cls = this.cls + '';
1226         //}
1227         
1228         if (this.sticky.length) {
1229             
1230             var bd = Roo.get(document.body);
1231             if (!bd.hasClass('bootstrap-sticky')) {
1232                 bd.addClass('bootstrap-sticky');
1233                 Roo.select('html',true).setStyle('height', '100%');
1234             }
1235              
1236             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1237         }
1238         
1239         
1240         if (this.well.length) {
1241             switch (this.well) {
1242                 case 'lg':
1243                 case 'sm':
1244                     cfg.cls +=' well well-' +this.well;
1245                     break;
1246                 default:
1247                     cfg.cls +=' well';
1248                     break;
1249             }
1250         }
1251         
1252         if (this.hidden) {
1253             cfg.cls += ' hidden';
1254         }
1255         
1256         
1257         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1258             cfg.cls +=' alert alert-' + this.alert;
1259         }
1260         
1261         var body = cfg;
1262         
1263         if (this.panel.length) {
1264             cfg.cls += ' panel panel-' + this.panel;
1265             cfg.cn = [];
1266             if (this.header.length) {
1267                 
1268                 var h = [];
1269                 
1270                 if(this.expandable){
1271                     
1272                     cfg.cls = cfg.cls + ' expandable';
1273                     
1274                     h.push({
1275                         tag: 'i',
1276                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1277                     });
1278                     
1279                 }
1280                 
1281                 h.push(
1282                     {
1283                         tag: 'span',
1284                         cls : 'panel-title',
1285                         html : (this.expandable ? '&nbsp;' : '') + this.header
1286                     },
1287                     {
1288                         tag: 'span',
1289                         cls: 'panel-header-right',
1290                         html: this.rheader
1291                     }
1292                 );
1293                 
1294                 cfg.cn.push({
1295                     cls : 'panel-heading',
1296                     style : this.expandable ? 'cursor: pointer' : '',
1297                     cn : h
1298                 });
1299                 
1300             }
1301             
1302             body = false;
1303             cfg.cn.push({
1304                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1305                 html : this.html
1306             });
1307             
1308             
1309             if (this.footer.length) {
1310                 cfg.cn.push({
1311                     cls : 'panel-footer',
1312                     html : this.footer
1313                     
1314                 });
1315             }
1316             
1317         }
1318         
1319         if (body) {
1320             body.html = this.html || cfg.html;
1321             // prefix with the icons..
1322             if (this.fa) {
1323                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1324             }
1325             if (this.icon) {
1326                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1327             }
1328             
1329             
1330         }
1331         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1332             cfg.cls =  'container';
1333         }
1334         
1335         return cfg;
1336     },
1337     
1338     initEvents: function() 
1339     {
1340         if(this.expandable){
1341             var headerEl = this.headerEl();
1342         
1343             if(headerEl){
1344                 headerEl.on('click', this.onToggleClick, this);
1345             }
1346         }
1347         
1348         if(this.clickable){
1349             this.el.on('click', this.onClick, this);
1350         }
1351         
1352     },
1353     
1354     onToggleClick : function()
1355     {
1356         var headerEl = this.headerEl();
1357         
1358         if(!headerEl){
1359             return;
1360         }
1361         
1362         if(this.expanded){
1363             this.collapse();
1364             return;
1365         }
1366         
1367         this.expand();
1368     },
1369     
1370     expand : function()
1371     {
1372         if(this.fireEvent('expand', this)) {
1373             
1374             this.expanded = true;
1375             
1376             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1377             
1378             this.el.select('.panel-body',true).first().removeClass('hide');
1379             
1380             var toggleEl = this.toggleEl();
1381
1382             if(!toggleEl){
1383                 return;
1384             }
1385
1386             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1387         }
1388         
1389     },
1390     
1391     collapse : function()
1392     {
1393         if(this.fireEvent('collapse', this)) {
1394             
1395             this.expanded = false;
1396             
1397             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1398             this.el.select('.panel-body',true).first().addClass('hide');
1399         
1400             var toggleEl = this.toggleEl();
1401
1402             if(!toggleEl){
1403                 return;
1404             }
1405
1406             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1407         }
1408     },
1409     
1410     toggleEl : function()
1411     {
1412         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1413             return;
1414         }
1415         
1416         return this.el.select('.panel-heading .fa',true).first();
1417     },
1418     
1419     headerEl : function()
1420     {
1421         if(!this.el || !this.panel.length || !this.header.length){
1422             return;
1423         }
1424         
1425         return this.el.select('.panel-heading',true).first()
1426     },
1427     
1428     bodyEl : function()
1429     {
1430         if(!this.el || !this.panel.length){
1431             return;
1432         }
1433         
1434         return this.el.select('.panel-body',true).first()
1435     },
1436     
1437     titleEl : function()
1438     {
1439         if(!this.el || !this.panel.length || !this.header.length){
1440             return;
1441         }
1442         
1443         return this.el.select('.panel-title',true).first();
1444     },
1445     
1446     setTitle : function(v)
1447     {
1448         var titleEl = this.titleEl();
1449         
1450         if(!titleEl){
1451             return;
1452         }
1453         
1454         titleEl.dom.innerHTML = v;
1455     },
1456     
1457     getTitle : function()
1458     {
1459         
1460         var titleEl = this.titleEl();
1461         
1462         if(!titleEl){
1463             return '';
1464         }
1465         
1466         return titleEl.dom.innerHTML;
1467     },
1468     
1469     setRightTitle : function(v)
1470     {
1471         var t = this.el.select('.panel-header-right',true).first();
1472         
1473         if(!t){
1474             return;
1475         }
1476         
1477         t.dom.innerHTML = v;
1478     },
1479     
1480     onClick : function(e)
1481     {
1482         e.preventDefault();
1483         
1484         this.fireEvent('click', this, e);
1485     }
1486 });
1487
1488  /*
1489  * - LGPL
1490  *
1491  * image
1492  * 
1493  */
1494
1495
1496 /**
1497  * @class Roo.bootstrap.Img
1498  * @extends Roo.bootstrap.Component
1499  * Bootstrap Img class
1500  * @cfg {Boolean} imgResponsive false | true
1501  * @cfg {String} border rounded | circle | thumbnail
1502  * @cfg {String} src image source
1503  * @cfg {String} alt image alternative text
1504  * @cfg {String} href a tag href
1505  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1506  * @cfg {String} xsUrl xs image source
1507  * @cfg {String} smUrl sm image source
1508  * @cfg {String} mdUrl md image source
1509  * @cfg {String} lgUrl lg image source
1510  * 
1511  * @constructor
1512  * Create a new Input
1513  * @param {Object} config The config object
1514  */
1515
1516 Roo.bootstrap.Img = function(config){
1517     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1518     
1519     this.addEvents({
1520         // img events
1521         /**
1522          * @event click
1523          * The img click event for the img.
1524          * @param {Roo.EventObject} e
1525          */
1526         "click" : true
1527     });
1528 };
1529
1530 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1531     
1532     imgResponsive: true,
1533     border: '',
1534     src: 'about:blank',
1535     href: false,
1536     target: false,
1537     xsUrl: '',
1538     smUrl: '',
1539     mdUrl: '',
1540     lgUrl: '',
1541
1542     getAutoCreate : function()
1543     {   
1544         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1545             return this.createSingleImg();
1546         }
1547         
1548         var cfg = {
1549             tag: 'div',
1550             cls: 'roo-image-responsive-group',
1551             cn: []
1552         };
1553         var _this = this;
1554         
1555         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1556             
1557             if(!_this[size + 'Url']){
1558                 return;
1559             }
1560             
1561             var img = {
1562                 tag: 'img',
1563                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1564                 html: _this.html || cfg.html,
1565                 src: _this[size + 'Url']
1566             };
1567             
1568             img.cls += ' roo-image-responsive-' + size;
1569             
1570             var s = ['xs', 'sm', 'md', 'lg'];
1571             
1572             s.splice(s.indexOf(size), 1);
1573             
1574             Roo.each(s, function(ss){
1575                 img.cls += ' hidden-' + ss;
1576             });
1577             
1578             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1579                 cfg.cls += ' img-' + _this.border;
1580             }
1581             
1582             if(_this.alt){
1583                 cfg.alt = _this.alt;
1584             }
1585             
1586             if(_this.href){
1587                 var a = {
1588                     tag: 'a',
1589                     href: _this.href,
1590                     cn: [
1591                         img
1592                     ]
1593                 };
1594
1595                 if(this.target){
1596                     a.target = _this.target;
1597                 }
1598             }
1599             
1600             cfg.cn.push((_this.href) ? a : img);
1601             
1602         });
1603         
1604         return cfg;
1605     },
1606     
1607     createSingleImg : function()
1608     {
1609         var cfg = {
1610             tag: 'img',
1611             cls: (this.imgResponsive) ? 'img-responsive' : '',
1612             html : null,
1613             src : 'about:blank'  // just incase src get's set to undefined?!?
1614         };
1615         
1616         cfg.html = this.html || cfg.html;
1617         
1618         cfg.src = this.src || cfg.src;
1619         
1620         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1621             cfg.cls += ' img-' + this.border;
1622         }
1623         
1624         if(this.alt){
1625             cfg.alt = this.alt;
1626         }
1627         
1628         if(this.href){
1629             var a = {
1630                 tag: 'a',
1631                 href: this.href,
1632                 cn: [
1633                     cfg
1634                 ]
1635             };
1636             
1637             if(this.target){
1638                 a.target = this.target;
1639             }
1640             
1641         }
1642         
1643         return (this.href) ? a : cfg;
1644     },
1645     
1646     initEvents: function() 
1647     {
1648         if(!this.href){
1649             this.el.on('click', this.onClick, this);
1650         }
1651         
1652     },
1653     
1654     onClick : function(e)
1655     {
1656         Roo.log('img onclick');
1657         this.fireEvent('click', this, e);
1658     },
1659     /**
1660      * Sets the url of the image - used to update it
1661      * @param {String} url the url of the image
1662      */
1663     
1664     setSrc : function(url)
1665     {
1666         this.src =  url;
1667         
1668         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1669             this.el.dom.src =  url;
1670             return;
1671         }
1672         
1673         this.el.select('img', true).first().dom.src =  url;
1674     }
1675     
1676     
1677    
1678 });
1679
1680  /*
1681  * - LGPL
1682  *
1683  * image
1684  * 
1685  */
1686
1687
1688 /**
1689  * @class Roo.bootstrap.Link
1690  * @extends Roo.bootstrap.Component
1691  * Bootstrap Link Class
1692  * @cfg {String} alt image alternative text
1693  * @cfg {String} href a tag href
1694  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1695  * @cfg {String} html the content of the link.
1696  * @cfg {String} anchor name for the anchor link
1697  * @cfg {String} fa - favicon
1698
1699  * @cfg {Boolean} preventDefault (true | false) default false
1700
1701  * 
1702  * @constructor
1703  * Create a new Input
1704  * @param {Object} config The config object
1705  */
1706
1707 Roo.bootstrap.Link = function(config){
1708     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1709     
1710     this.addEvents({
1711         // img events
1712         /**
1713          * @event click
1714          * The img click event for the img.
1715          * @param {Roo.EventObject} e
1716          */
1717         "click" : true
1718     });
1719 };
1720
1721 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1722     
1723     href: false,
1724     target: false,
1725     preventDefault: false,
1726     anchor : false,
1727     alt : false,
1728     fa: false,
1729
1730
1731     getAutoCreate : function()
1732     {
1733         var html = this.html || '';
1734         
1735         if (this.fa !== false) {
1736             html = '<i class="fa fa-' + this.fa + '"></i>';
1737         }
1738         var cfg = {
1739             tag: 'a'
1740         };
1741         // anchor's do not require html/href...
1742         if (this.anchor === false) {
1743             cfg.html = html;
1744             cfg.href = this.href || '#';
1745         } else {
1746             cfg.name = this.anchor;
1747             if (this.html !== false || this.fa !== false) {
1748                 cfg.html = html;
1749             }
1750             if (this.href !== false) {
1751                 cfg.href = this.href;
1752             }
1753         }
1754         
1755         if(this.alt !== false){
1756             cfg.alt = this.alt;
1757         }
1758         
1759         
1760         if(this.target !== false) {
1761             cfg.target = this.target;
1762         }
1763         
1764         return cfg;
1765     },
1766     
1767     initEvents: function() {
1768         
1769         if(!this.href || this.preventDefault){
1770             this.el.on('click', this.onClick, this);
1771         }
1772     },
1773     
1774     onClick : function(e)
1775     {
1776         if(this.preventDefault){
1777             e.preventDefault();
1778         }
1779         //Roo.log('img onclick');
1780         this.fireEvent('click', this, e);
1781     }
1782    
1783 });
1784
1785  /*
1786  * - LGPL
1787  *
1788  * header
1789  * 
1790  */
1791
1792 /**
1793  * @class Roo.bootstrap.Header
1794  * @extends Roo.bootstrap.Component
1795  * Bootstrap Header class
1796  * @cfg {String} html content of header
1797  * @cfg {Number} level (1|2|3|4|5|6) default 1
1798  * 
1799  * @constructor
1800  * Create a new Header
1801  * @param {Object} config The config object
1802  */
1803
1804
1805 Roo.bootstrap.Header  = function(config){
1806     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1807 };
1808
1809 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1810     
1811     //href : false,
1812     html : false,
1813     level : 1,
1814     
1815     
1816     
1817     getAutoCreate : function(){
1818         
1819         
1820         
1821         var cfg = {
1822             tag: 'h' + (1 *this.level),
1823             html: this.html || ''
1824         } ;
1825         
1826         return cfg;
1827     }
1828    
1829 });
1830
1831  
1832
1833  /*
1834  * Based on:
1835  * Ext JS Library 1.1.1
1836  * Copyright(c) 2006-2007, Ext JS, LLC.
1837  *
1838  * Originally Released Under LGPL - original licence link has changed is not relivant.
1839  *
1840  * Fork - LGPL
1841  * <script type="text/javascript">
1842  */
1843  
1844 /**
1845  * @class Roo.bootstrap.MenuMgr
1846  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1847  * @singleton
1848  */
1849 Roo.bootstrap.MenuMgr = function(){
1850    var menus, active, groups = {}, attached = false, lastShow = new Date();
1851
1852    // private - called when first menu is created
1853    function init(){
1854        menus = {};
1855        active = new Roo.util.MixedCollection();
1856        Roo.get(document).addKeyListener(27, function(){
1857            if(active.length > 0){
1858                hideAll();
1859            }
1860        });
1861    }
1862
1863    // private
1864    function hideAll(){
1865        if(active && active.length > 0){
1866            var c = active.clone();
1867            c.each(function(m){
1868                m.hide();
1869            });
1870        }
1871    }
1872
1873    // private
1874    function onHide(m){
1875        active.remove(m);
1876        if(active.length < 1){
1877            Roo.get(document).un("mouseup", onMouseDown);
1878             
1879            attached = false;
1880        }
1881    }
1882
1883    // private
1884    function onShow(m){
1885        var last = active.last();
1886        lastShow = new Date();
1887        active.add(m);
1888        if(!attached){
1889           Roo.get(document).on("mouseup", onMouseDown);
1890            
1891            attached = true;
1892        }
1893        if(m.parentMenu){
1894           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1895           m.parentMenu.activeChild = m;
1896        }else if(last && last.isVisible()){
1897           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1898        }
1899    }
1900
1901    // private
1902    function onBeforeHide(m){
1903        if(m.activeChild){
1904            m.activeChild.hide();
1905        }
1906        if(m.autoHideTimer){
1907            clearTimeout(m.autoHideTimer);
1908            delete m.autoHideTimer;
1909        }
1910    }
1911
1912    // private
1913    function onBeforeShow(m){
1914        var pm = m.parentMenu;
1915        if(!pm && !m.allowOtherMenus){
1916            hideAll();
1917        }else if(pm && pm.activeChild && active != m){
1918            pm.activeChild.hide();
1919        }
1920    }
1921
1922    // private this should really trigger on mouseup..
1923    function onMouseDown(e){
1924         Roo.log("on Mouse Up");
1925         
1926         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1927             Roo.log("MenuManager hideAll");
1928             hideAll();
1929             e.stopEvent();
1930         }
1931         
1932         
1933    }
1934
1935    // private
1936    function onBeforeCheck(mi, state){
1937        if(state){
1938            var g = groups[mi.group];
1939            for(var i = 0, l = g.length; i < l; i++){
1940                if(g[i] != mi){
1941                    g[i].setChecked(false);
1942                }
1943            }
1944        }
1945    }
1946
1947    return {
1948
1949        /**
1950         * Hides all menus that are currently visible
1951         */
1952        hideAll : function(){
1953             hideAll();  
1954        },
1955
1956        // private
1957        register : function(menu){
1958            if(!menus){
1959                init();
1960            }
1961            menus[menu.id] = menu;
1962            menu.on("beforehide", onBeforeHide);
1963            menu.on("hide", onHide);
1964            menu.on("beforeshow", onBeforeShow);
1965            menu.on("show", onShow);
1966            var g = menu.group;
1967            if(g && menu.events["checkchange"]){
1968                if(!groups[g]){
1969                    groups[g] = [];
1970                }
1971                groups[g].push(menu);
1972                menu.on("checkchange", onCheck);
1973            }
1974        },
1975
1976         /**
1977          * Returns a {@link Roo.menu.Menu} object
1978          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1979          * be used to generate and return a new Menu instance.
1980          */
1981        get : function(menu){
1982            if(typeof menu == "string"){ // menu id
1983                return menus[menu];
1984            }else if(menu.events){  // menu instance
1985                return menu;
1986            }
1987            /*else if(typeof menu.length == 'number'){ // array of menu items?
1988                return new Roo.bootstrap.Menu({items:menu});
1989            }else{ // otherwise, must be a config
1990                return new Roo.bootstrap.Menu(menu);
1991            }
1992            */
1993            return false;
1994        },
1995
1996        // private
1997        unregister : function(menu){
1998            delete menus[menu.id];
1999            menu.un("beforehide", onBeforeHide);
2000            menu.un("hide", onHide);
2001            menu.un("beforeshow", onBeforeShow);
2002            menu.un("show", onShow);
2003            var g = menu.group;
2004            if(g && menu.events["checkchange"]){
2005                groups[g].remove(menu);
2006                menu.un("checkchange", onCheck);
2007            }
2008        },
2009
2010        // private
2011        registerCheckable : function(menuItem){
2012            var g = menuItem.group;
2013            if(g){
2014                if(!groups[g]){
2015                    groups[g] = [];
2016                }
2017                groups[g].push(menuItem);
2018                menuItem.on("beforecheckchange", onBeforeCheck);
2019            }
2020        },
2021
2022        // private
2023        unregisterCheckable : function(menuItem){
2024            var g = menuItem.group;
2025            if(g){
2026                groups[g].remove(menuItem);
2027                menuItem.un("beforecheckchange", onBeforeCheck);
2028            }
2029        }
2030    };
2031 }();/*
2032  * - LGPL
2033  *
2034  * menu
2035  * 
2036  */
2037
2038 /**
2039  * @class Roo.bootstrap.Menu
2040  * @extends Roo.bootstrap.Component
2041  * Bootstrap Menu class - container for MenuItems
2042  * @cfg {String} type (dropdown|treeview|submenu) type of menu
2043  * @cfg {bool} hidden  if the menu should be hidden when rendered.
2044  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
2045  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
2046  * 
2047  * @constructor
2048  * Create a new Menu
2049  * @param {Object} config The config object
2050  */
2051
2052
2053 Roo.bootstrap.Menu = function(config){
2054     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2055     if (this.registerMenu && this.type != 'treeview')  {
2056         Roo.bootstrap.MenuMgr.register(this);
2057     }
2058     
2059     
2060     this.addEvents({
2061         /**
2062          * @event beforeshow
2063          * Fires before this menu is displayed (return false to block)
2064          * @param {Roo.menu.Menu} this
2065          */
2066         beforeshow : true,
2067         /**
2068          * @event beforehide
2069          * Fires before this menu is hidden (return false to block)
2070          * @param {Roo.menu.Menu} this
2071          */
2072         beforehide : true,
2073         /**
2074          * @event show
2075          * Fires after this menu is displayed
2076          * @param {Roo.menu.Menu} this
2077          */
2078         show : true,
2079         /**
2080          * @event hide
2081          * Fires after this menu is hidden
2082          * @param {Roo.menu.Menu} this
2083          */
2084         hide : true,
2085         /**
2086          * @event click
2087          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2088          * @param {Roo.menu.Menu} this
2089          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2090          * @param {Roo.EventObject} e
2091          */
2092         click : true,
2093         /**
2094          * @event mouseover
2095          * Fires when the mouse is hovering over this menu
2096          * @param {Roo.menu.Menu} this
2097          * @param {Roo.EventObject} e
2098          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2099          */
2100         mouseover : true,
2101         /**
2102          * @event mouseout
2103          * Fires when the mouse exits this menu
2104          * @param {Roo.menu.Menu} this
2105          * @param {Roo.EventObject} e
2106          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2107          */
2108         mouseout : true,
2109         /**
2110          * @event itemclick
2111          * Fires when a menu item contained in this menu is clicked
2112          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2113          * @param {Roo.EventObject} e
2114          */
2115         itemclick: true
2116     });
2117     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2118 };
2119
2120 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2121     
2122    /// html : false,
2123     //align : '',
2124     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2125     type: false,
2126     /**
2127      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2128      */
2129     registerMenu : true,
2130     
2131     menuItems :false, // stores the menu items..
2132     
2133     hidden:true,
2134         
2135     parentMenu : false,
2136     
2137     stopEvent : true,
2138     
2139     isLink : false,
2140     
2141     getChildContainer : function() {
2142         return this.el;  
2143     },
2144     
2145     getAutoCreate : function(){
2146          
2147         //if (['right'].indexOf(this.align)!==-1) {
2148         //    cfg.cn[1].cls += ' pull-right'
2149         //}
2150         
2151         
2152         var cfg = {
2153             tag : 'ul',
2154             cls : 'dropdown-menu' ,
2155             style : 'z-index:1000'
2156             
2157         };
2158         
2159         if (this.type === 'submenu') {
2160             cfg.cls = 'submenu active';
2161         }
2162         if (this.type === 'treeview') {
2163             cfg.cls = 'treeview-menu';
2164         }
2165         
2166         return cfg;
2167     },
2168     initEvents : function() {
2169         
2170        // Roo.log("ADD event");
2171        // Roo.log(this.triggerEl.dom);
2172         
2173         this.triggerEl.on('click', this.onTriggerClick, this);
2174         
2175         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2176         
2177         
2178         if (this.triggerEl.hasClass('nav-item')) {
2179             // dropdown toggle on the 'a' in BS4?
2180             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
2181         } else {
2182             this.triggerEl.addClass('dropdown-toggle');
2183         }
2184         if (Roo.isTouch) {
2185             this.el.on('touchstart'  , this.onTouch, this);
2186         }
2187         this.el.on('click' , this.onClick, this);
2188
2189         this.el.on("mouseover", this.onMouseOver, this);
2190         this.el.on("mouseout", this.onMouseOut, this);
2191         
2192     },
2193     
2194     findTargetItem : function(e)
2195     {
2196         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2197         if(!t){
2198             return false;
2199         }
2200         //Roo.log(t);         Roo.log(t.id);
2201         if(t && t.id){
2202             //Roo.log(this.menuitems);
2203             return this.menuitems.get(t.id);
2204             
2205             //return this.items.get(t.menuItemId);
2206         }
2207         
2208         return false;
2209     },
2210     
2211     onTouch : function(e) 
2212     {
2213         Roo.log("menu.onTouch");
2214         //e.stopEvent(); this make the user popdown broken
2215         this.onClick(e);
2216     },
2217     
2218     onClick : function(e)
2219     {
2220         Roo.log("menu.onClick");
2221         
2222         var t = this.findTargetItem(e);
2223         if(!t || t.isContainer){
2224             return;
2225         }
2226         Roo.log(e);
2227         /*
2228         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2229             if(t == this.activeItem && t.shouldDeactivate(e)){
2230                 this.activeItem.deactivate();
2231                 delete this.activeItem;
2232                 return;
2233             }
2234             if(t.canActivate){
2235                 this.setActiveItem(t, true);
2236             }
2237             return;
2238             
2239             
2240         }
2241         */
2242        
2243         Roo.log('pass click event');
2244         
2245         t.onClick(e);
2246         
2247         this.fireEvent("click", this, t, e);
2248         
2249         var _this = this;
2250         
2251         if(!t.href.length || t.href == '#'){
2252             (function() { _this.hide(); }).defer(100);
2253         }
2254         
2255     },
2256     
2257     onMouseOver : function(e){
2258         var t  = this.findTargetItem(e);
2259         //Roo.log(t);
2260         //if(t){
2261         //    if(t.canActivate && !t.disabled){
2262         //        this.setActiveItem(t, true);
2263         //    }
2264         //}
2265         
2266         this.fireEvent("mouseover", this, e, t);
2267     },
2268     isVisible : function(){
2269         return !this.hidden;
2270     },
2271     onMouseOut : function(e){
2272         var t  = this.findTargetItem(e);
2273         
2274         //if(t ){
2275         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2276         //        this.activeItem.deactivate();
2277         //        delete this.activeItem;
2278         //    }
2279         //}
2280         this.fireEvent("mouseout", this, e, t);
2281     },
2282     
2283     
2284     /**
2285      * Displays this menu relative to another element
2286      * @param {String/HTMLElement/Roo.Element} element The element to align to
2287      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2288      * the element (defaults to this.defaultAlign)
2289      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2290      */
2291     show : function(el, pos, parentMenu)
2292     {
2293         if (false === this.fireEvent("beforeshow", this)) {
2294             Roo.log("show canceled");
2295             return;
2296         }
2297         this.parentMenu = parentMenu;
2298         if(!this.el){
2299             this.render();
2300         }
2301         
2302         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2303     },
2304      /**
2305      * Displays this menu at a specific xy position
2306      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2307      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2308      */
2309     showAt : function(xy, parentMenu, /* private: */_e){
2310         this.parentMenu = parentMenu;
2311         if(!this.el){
2312             this.render();
2313         }
2314         if(_e !== false){
2315             this.fireEvent("beforeshow", this);
2316             //xy = this.el.adjustForConstraints(xy);
2317         }
2318         
2319         //this.el.show();
2320         this.hideMenuItems();
2321         this.hidden = false;
2322         this.triggerEl.addClass('open');
2323         this.el.addClass('show');
2324         
2325         // reassign x when hitting right
2326         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2327             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2328         }
2329         
2330         // reassign y when hitting bottom
2331         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2332             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2333         }
2334         
2335         // but the list may align on trigger left or trigger top... should it be a properity?
2336         
2337         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2338             this.el.setXY(xy);
2339         }
2340         
2341         this.focus();
2342         this.fireEvent("show", this);
2343     },
2344     
2345     focus : function(){
2346         return;
2347         if(!this.hidden){
2348             this.doFocus.defer(50, this);
2349         }
2350     },
2351
2352     doFocus : function(){
2353         if(!this.hidden){
2354             this.focusEl.focus();
2355         }
2356     },
2357
2358     /**
2359      * Hides this menu and optionally all parent menus
2360      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2361      */
2362     hide : function(deep)
2363     {
2364         if (false === this.fireEvent("beforehide", this)) {
2365             Roo.log("hide canceled");
2366             return;
2367         }
2368         this.hideMenuItems();
2369         if(this.el && this.isVisible()){
2370            
2371             if(this.activeItem){
2372                 this.activeItem.deactivate();
2373                 this.activeItem = null;
2374             }
2375             this.triggerEl.removeClass('open');;
2376             this.el.removeClass('show');
2377             this.hidden = true;
2378             this.fireEvent("hide", this);
2379         }
2380         if(deep === true && this.parentMenu){
2381             this.parentMenu.hide(true);
2382         }
2383     },
2384     
2385     onTriggerClick : function(e)
2386     {
2387         Roo.log('trigger click');
2388         
2389         var target = e.getTarget();
2390         
2391         Roo.log(target.nodeName.toLowerCase());
2392         
2393         if(target.nodeName.toLowerCase() === 'i'){
2394             e.preventDefault();
2395         }
2396         
2397     },
2398     
2399     onTriggerPress  : function(e)
2400     {
2401         Roo.log('trigger press');
2402         //Roo.log(e.getTarget());
2403        // Roo.log(this.triggerEl.dom);
2404        
2405         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2406         var pel = Roo.get(e.getTarget());
2407         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2408             Roo.log('is treeview or dropdown?');
2409             return;
2410         }
2411         
2412         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2413             return;
2414         }
2415         
2416         if (this.isVisible()) {
2417             Roo.log('hide');
2418             this.hide();
2419         } else {
2420             Roo.log('show');
2421             this.show(this.triggerEl, '?', false);
2422         }
2423         
2424         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2425             e.stopEvent();
2426         }
2427         
2428     },
2429        
2430     
2431     hideMenuItems : function()
2432     {
2433         Roo.log("hide Menu Items");
2434         if (!this.el) { 
2435             return;
2436         }
2437         
2438         this.el.select('.open',true).each(function(aa) {
2439             
2440             aa.removeClass('open');
2441          
2442         });
2443     },
2444     addxtypeChild : function (tree, cntr) {
2445         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2446           
2447         this.menuitems.add(comp);
2448         return comp;
2449
2450     },
2451     getEl : function()
2452     {
2453         Roo.log(this.el);
2454         return this.el;
2455     },
2456     
2457     clear : function()
2458     {
2459         this.getEl().dom.innerHTML = '';
2460         this.menuitems.clear();
2461     }
2462 });
2463
2464  
2465  /*
2466  * - LGPL
2467  *
2468  * menu item
2469  * 
2470  */
2471
2472
2473 /**
2474  * @class Roo.bootstrap.MenuItem
2475  * @extends Roo.bootstrap.Component
2476  * Bootstrap MenuItem class
2477  * @cfg {String} html the menu label
2478  * @cfg {String} href the link
2479  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2480  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2481  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2482  * @cfg {String} fa favicon to show on left of menu item.
2483  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2484  * 
2485  * 
2486  * @constructor
2487  * Create a new MenuItem
2488  * @param {Object} config The config object
2489  */
2490
2491
2492 Roo.bootstrap.MenuItem = function(config){
2493     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2494     this.addEvents({
2495         // raw events
2496         /**
2497          * @event click
2498          * The raw click event for the entire grid.
2499          * @param {Roo.bootstrap.MenuItem} this
2500          * @param {Roo.EventObject} e
2501          */
2502         "click" : true
2503     });
2504 };
2505
2506 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2507     
2508     href : false,
2509     html : false,
2510     preventDefault: false,
2511     isContainer : false,
2512     active : false,
2513     fa: false,
2514     
2515     getAutoCreate : function(){
2516         
2517         if(this.isContainer){
2518             return {
2519                 tag: 'li',
2520                 cls: 'dropdown-menu-item '
2521             };
2522         }
2523         var ctag = {
2524             tag: 'span',
2525             html: 'Link'
2526         };
2527         
2528         var anc = {
2529             tag : 'a',
2530             cls : 'dropdown-item',
2531             href : '#',
2532             cn : [  ]
2533         };
2534         
2535         if (this.fa !== false) {
2536             anc.cn.push({
2537                 tag : 'i',
2538                 cls : 'fa fa-' + this.fa
2539             });
2540         }
2541         
2542         anc.cn.push(ctag);
2543         
2544         
2545         var cfg= {
2546             tag: 'li',
2547             cls: 'dropdown-menu-item',
2548             cn: [ anc ]
2549         };
2550         if (this.parent().type == 'treeview') {
2551             cfg.cls = 'treeview-menu';
2552         }
2553         if (this.active) {
2554             cfg.cls += ' active';
2555         }
2556         
2557         
2558         
2559         anc.href = this.href || cfg.cn[0].href ;
2560         ctag.html = this.html || cfg.cn[0].html ;
2561         return cfg;
2562     },
2563     
2564     initEvents: function()
2565     {
2566         if (this.parent().type == 'treeview') {
2567             this.el.select('a').on('click', this.onClick, this);
2568         }
2569         
2570         if (this.menu) {
2571             this.menu.parentType = this.xtype;
2572             this.menu.triggerEl = this.el;
2573             this.menu = this.addxtype(Roo.apply({}, this.menu));
2574         }
2575         
2576     },
2577     onClick : function(e)
2578     {
2579         Roo.log('item on click ');
2580         
2581         if(this.preventDefault){
2582             e.preventDefault();
2583         }
2584         //this.parent().hideMenuItems();
2585         
2586         this.fireEvent('click', this, e);
2587     },
2588     getEl : function()
2589     {
2590         return this.el;
2591     } 
2592 });
2593
2594  
2595
2596  /*
2597  * - LGPL
2598  *
2599  * menu separator
2600  * 
2601  */
2602
2603
2604 /**
2605  * @class Roo.bootstrap.MenuSeparator
2606  * @extends Roo.bootstrap.Component
2607  * Bootstrap MenuSeparator class
2608  * 
2609  * @constructor
2610  * Create a new MenuItem
2611  * @param {Object} config The config object
2612  */
2613
2614
2615 Roo.bootstrap.MenuSeparator = function(config){
2616     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2617 };
2618
2619 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2620     
2621     getAutoCreate : function(){
2622         var cfg = {
2623             cls: 'divider',
2624             tag : 'li'
2625         };
2626         
2627         return cfg;
2628     }
2629    
2630 });
2631
2632  
2633
2634  
2635 /*
2636 * Licence: LGPL
2637 */
2638
2639 /**
2640  * @class Roo.bootstrap.Modal
2641  * @extends Roo.bootstrap.Component
2642  * Bootstrap Modal class
2643  * @cfg {String} title Title of dialog
2644  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2645  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2646  * @cfg {Boolean} specificTitle default false
2647  * @cfg {Array} buttons Array of buttons or standard button set..
2648  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
2649  * @cfg {Boolean} animate default true
2650  * @cfg {Boolean} allow_close default true
2651  * @cfg {Boolean} fitwindow default false
2652  * @cfg {String} size (sm|lg) default empty
2653  * @cfg {Number} max_width set the max width of modal
2654  *
2655  *
2656  * @constructor
2657  * Create a new Modal Dialog
2658  * @param {Object} config The config object
2659  */
2660
2661 Roo.bootstrap.Modal = function(config){
2662     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2663     this.addEvents({
2664         // raw events
2665         /**
2666          * @event btnclick
2667          * The raw btnclick event for the button
2668          * @param {Roo.EventObject} e
2669          */
2670         "btnclick" : true,
2671         /**
2672          * @event resize
2673          * Fire when dialog resize
2674          * @param {Roo.bootstrap.Modal} this
2675          * @param {Roo.EventObject} e
2676          */
2677         "resize" : true
2678     });
2679     this.buttons = this.buttons || [];
2680
2681     if (this.tmpl) {
2682         this.tmpl = Roo.factory(this.tmpl);
2683     }
2684
2685 };
2686
2687 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2688
2689     title : 'test dialog',
2690
2691     buttons : false,
2692
2693     // set on load...
2694
2695     html: false,
2696
2697     tmp: false,
2698
2699     specificTitle: false,
2700
2701     buttonPosition: 'right',
2702
2703     allow_close : true,
2704
2705     animate : true,
2706
2707     fitwindow: false,
2708     
2709      // private
2710     dialogEl: false,
2711     bodyEl:  false,
2712     footerEl:  false,
2713     titleEl:  false,
2714     closeEl:  false,
2715
2716     size: '',
2717     
2718     max_width: 0,
2719     
2720     max_height: 0,
2721     
2722     fit_content: false,
2723
2724     onRender : function(ct, position)
2725     {
2726         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2727
2728         if(!this.el){
2729             var cfg = Roo.apply({},  this.getAutoCreate());
2730             cfg.id = Roo.id();
2731             //if(!cfg.name){
2732             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2733             //}
2734             //if (!cfg.name.length) {
2735             //    delete cfg.name;
2736            // }
2737             if (this.cls) {
2738                 cfg.cls += ' ' + this.cls;
2739             }
2740             if (this.style) {
2741                 cfg.style = this.style;
2742             }
2743             this.el = Roo.get(document.body).createChild(cfg, position);
2744         }
2745         //var type = this.el.dom.type;
2746
2747
2748         if(this.tabIndex !== undefined){
2749             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2750         }
2751
2752         this.dialogEl = this.el.select('.modal-dialog',true).first();
2753         this.bodyEl = this.el.select('.modal-body',true).first();
2754         this.closeEl = this.el.select('.modal-header .close', true).first();
2755         this.headerEl = this.el.select('.modal-header',true).first();
2756         this.titleEl = this.el.select('.modal-title',true).first();
2757         this.footerEl = this.el.select('.modal-footer',true).first();
2758
2759         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2760         
2761         //this.el.addClass("x-dlg-modal");
2762
2763         if (this.buttons.length) {
2764             Roo.each(this.buttons, function(bb) {
2765                 var b = Roo.apply({}, bb);
2766                 b.xns = b.xns || Roo.bootstrap;
2767                 b.xtype = b.xtype || 'Button';
2768                 if (typeof(b.listeners) == 'undefined') {
2769                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2770                 }
2771
2772                 var btn = Roo.factory(b);
2773
2774                 btn.render(this.getButtonContainer());
2775
2776             },this);
2777         }
2778         // render the children.
2779         var nitems = [];
2780
2781         if(typeof(this.items) != 'undefined'){
2782             var items = this.items;
2783             delete this.items;
2784
2785             for(var i =0;i < items.length;i++) {
2786                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2787             }
2788         }
2789
2790         this.items = nitems;
2791
2792         // where are these used - they used to be body/close/footer
2793
2794
2795         this.initEvents();
2796         //this.el.addClass([this.fieldClass, this.cls]);
2797
2798     },
2799
2800     getAutoCreate : function()
2801     {
2802         var bdy = {
2803                 cls : 'modal-body',
2804                 html : this.html || ''
2805         };
2806
2807         var title = {
2808             tag: 'h4',
2809             cls : 'modal-title',
2810             html : this.title
2811         };
2812
2813         if(this.specificTitle){
2814             title = this.title;
2815
2816         }
2817
2818         var header = [];
2819         if (this.allow_close && Roo.bootstrap.version == 3) {
2820             header.push({
2821                 tag: 'button',
2822                 cls : 'close',
2823                 html : '&times'
2824             });
2825         }
2826
2827         header.push(title);
2828
2829         if (this.allow_close && Roo.bootstrap.version == 4) {
2830             header.push({
2831                 tag: 'button',
2832                 cls : 'close',
2833                 html : '&times'
2834             });
2835         }
2836         
2837         var size = '';
2838
2839         if(this.size.length){
2840             size = 'modal-' + this.size;
2841         }
2842         
2843         var footer = Roo.bootstrap.version == 3 ?
2844             {
2845                 cls : 'modal-footer',
2846                 cn : [
2847                     {
2848                         tag: 'div',
2849                         cls: 'btn-' + this.buttonPosition
2850                     }
2851                 ]
2852
2853             } :
2854             {  // BS4 uses mr-auto on left buttons....
2855                 cls : 'modal-footer'
2856             };
2857
2858             
2859
2860         
2861         
2862         var modal = {
2863             cls: "modal",
2864              cn : [
2865                 {
2866                     cls: "modal-dialog " + size,
2867                     cn : [
2868                         {
2869                             cls : "modal-content",
2870                             cn : [
2871                                 {
2872                                     cls : 'modal-header',
2873                                     cn : header
2874                                 },
2875                                 bdy,
2876                                 footer
2877                             ]
2878
2879                         }
2880                     ]
2881
2882                 }
2883             ]
2884         };
2885
2886         if(this.animate){
2887             modal.cls += ' fade';
2888         }
2889
2890         return modal;
2891
2892     },
2893     getChildContainer : function() {
2894
2895          return this.bodyEl;
2896
2897     },
2898     getButtonContainer : function() {
2899         
2900          return Roo.bootstrap.version == 4 ?
2901             this.el.select('.modal-footer',true).first()
2902             : this.el.select('.modal-footer div',true).first();
2903
2904     },
2905     initEvents : function()
2906     {
2907         if (this.allow_close) {
2908             this.closeEl.on('click', this.hide, this);
2909         }
2910         Roo.EventManager.onWindowResize(this.resize, this, true);
2911
2912
2913     },
2914   
2915
2916     resize : function()
2917     {
2918         this.maskEl.setSize(
2919             Roo.lib.Dom.getViewWidth(true),
2920             Roo.lib.Dom.getViewHeight(true)
2921         );
2922         
2923         if (this.fitwindow) {
2924             
2925            
2926             this.setSize(
2927                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2928                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
2929             );
2930             return;
2931         }
2932         
2933         if(this.max_width !== 0) {
2934             
2935             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2936             
2937             if(this.height) {
2938                 this.setSize(w, this.height);
2939                 return;
2940             }
2941             
2942             if(this.max_height) {
2943                 this.setSize(w,Math.min(
2944                     this.max_height,
2945                     Roo.lib.Dom.getViewportHeight(true) - 60
2946                 ));
2947                 
2948                 return;
2949             }
2950             
2951             if(!this.fit_content) {
2952                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2953                 return;
2954             }
2955             
2956             this.setSize(w, Math.min(
2957                 60 +
2958                 this.headerEl.getHeight() + 
2959                 this.footerEl.getHeight() + 
2960                 this.getChildHeight(this.bodyEl.dom.childNodes),
2961                 Roo.lib.Dom.getViewportHeight(true) - 60)
2962             );
2963         }
2964         
2965     },
2966
2967     setSize : function(w,h)
2968     {
2969         if (!w && !h) {
2970             return;
2971         }
2972         
2973         this.resizeTo(w,h);
2974     },
2975
2976     show : function() {
2977
2978         if (!this.rendered) {
2979             this.render();
2980         }
2981
2982         //this.el.setStyle('display', 'block');
2983         this.el.removeClass('hideing');
2984         this.el.dom.style.display='block';
2985         
2986         Roo.get(document.body).addClass('modal-open');
2987  
2988         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2989             
2990             (function(){
2991                 this.el.addClass('show');
2992                 this.el.addClass('in');
2993             }).defer(50, this);
2994         }else{
2995             this.el.addClass('show');
2996             this.el.addClass('in');
2997         }
2998
2999         // not sure how we can show data in here..
3000         //if (this.tmpl) {
3001         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
3002         //}
3003
3004         Roo.get(document.body).addClass("x-body-masked");
3005         
3006         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
3007         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3008         this.maskEl.dom.style.display = 'block';
3009         this.maskEl.addClass('show');
3010         
3011         
3012         this.resize();
3013         
3014         this.fireEvent('show', this);
3015
3016         // set zindex here - otherwise it appears to be ignored...
3017         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3018
3019         (function () {
3020             this.items.forEach( function(e) {
3021                 e.layout ? e.layout() : false;
3022
3023             });
3024         }).defer(100,this);
3025
3026     },
3027     hide : function()
3028     {
3029         if(this.fireEvent("beforehide", this) !== false){
3030             
3031             this.maskEl.removeClass('show');
3032             
3033             this.maskEl.dom.style.display = '';
3034             Roo.get(document.body).removeClass("x-body-masked");
3035             this.el.removeClass('in');
3036             this.el.select('.modal-dialog', true).first().setStyle('transform','');
3037
3038             if(this.animate){ // why
3039                 this.el.addClass('hideing');
3040                 this.el.removeClass('show');
3041                 (function(){
3042                     if (!this.el.hasClass('hideing')) {
3043                         return; // it's been shown again...
3044                     }
3045                     
3046                     this.el.dom.style.display='';
3047
3048                     Roo.get(document.body).removeClass('modal-open');
3049                     this.el.removeClass('hideing');
3050                 }).defer(150,this);
3051                 
3052             }else{
3053                 this.el.removeClass('show');
3054                 this.el.dom.style.display='';
3055                 Roo.get(document.body).removeClass('modal-open');
3056
3057             }
3058             this.fireEvent('hide', this);
3059         }
3060     },
3061     isVisible : function()
3062     {
3063         
3064         return this.el.hasClass('show') && !this.el.hasClass('hideing');
3065         
3066     },
3067
3068     addButton : function(str, cb)
3069     {
3070
3071
3072         var b = Roo.apply({}, { html : str } );
3073         b.xns = b.xns || Roo.bootstrap;
3074         b.xtype = b.xtype || 'Button';
3075         if (typeof(b.listeners) == 'undefined') {
3076             b.listeners = { click : cb.createDelegate(this)  };
3077         }
3078
3079         var btn = Roo.factory(b);
3080
3081         btn.render(this.getButtonContainer());
3082
3083         return btn;
3084
3085     },
3086
3087     setDefaultButton : function(btn)
3088     {
3089         //this.el.select('.modal-footer').()
3090     },
3091
3092     resizeTo: function(w,h)
3093     {
3094         this.dialogEl.setWidth(w);
3095         
3096         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
3097
3098         this.bodyEl.setHeight(h - diff);
3099         
3100         this.fireEvent('resize', this);
3101     },
3102     
3103     setContentSize  : function(w, h)
3104     {
3105
3106     },
3107     onButtonClick: function(btn,e)
3108     {
3109         //Roo.log([a,b,c]);
3110         this.fireEvent('btnclick', btn.name, e);
3111     },
3112      /**
3113      * Set the title of the Dialog
3114      * @param {String} str new Title
3115      */
3116     setTitle: function(str) {
3117         this.titleEl.dom.innerHTML = str;
3118     },
3119     /**
3120      * Set the body of the Dialog
3121      * @param {String} str new Title
3122      */
3123     setBody: function(str) {
3124         this.bodyEl.dom.innerHTML = str;
3125     },
3126     /**
3127      * Set the body of the Dialog using the template
3128      * @param {Obj} data - apply this data to the template and replace the body contents.
3129      */
3130     applyBody: function(obj)
3131     {
3132         if (!this.tmpl) {
3133             Roo.log("Error - using apply Body without a template");
3134             //code
3135         }
3136         this.tmpl.overwrite(this.bodyEl, obj);
3137     },
3138     
3139     getChildHeight : function(child_nodes)
3140     {
3141         if(
3142             !child_nodes ||
3143             child_nodes.length == 0
3144         ) {
3145             return;
3146         }
3147         
3148         var child_height = 0;
3149         
3150         for(var i = 0; i < child_nodes.length; i++) {
3151             
3152             /*
3153             * for modal with tabs...
3154             if(child_nodes[i].classList.contains('roo-layout-panel')) {
3155                 
3156                 var layout_childs = child_nodes[i].childNodes;
3157                 
3158                 for(var j = 0; j < layout_childs.length; j++) {
3159                     
3160                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3161                         
3162                         var layout_body_childs = layout_childs[j].childNodes;
3163                         
3164                         for(var k = 0; k < layout_body_childs.length; k++) {
3165                             
3166                             if(layout_body_childs[k].classList.contains('navbar')) {
3167                                 child_height += layout_body_childs[k].offsetHeight;
3168                                 continue;
3169                             }
3170                             
3171                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3172                                 
3173                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3174                                 
3175                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3176                                     
3177                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3178                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3179                                         continue;
3180                                     }
3181                                     
3182                                 }
3183                                 
3184                             }
3185                             
3186                         }
3187                     }
3188                 }
3189                 continue;
3190             }
3191             */
3192             
3193             child_height += child_nodes[i].offsetHeight;
3194             // Roo.log(child_nodes[i].offsetHeight);
3195         }
3196         
3197         return child_height;
3198     }
3199
3200 });
3201
3202
3203 Roo.apply(Roo.bootstrap.Modal,  {
3204     /**
3205          * Button config that displays a single OK button
3206          * @type Object
3207          */
3208         OK :  [{
3209             name : 'ok',
3210             weight : 'primary',
3211             html : 'OK'
3212         }],
3213         /**
3214          * Button config that displays Yes and No buttons
3215          * @type Object
3216          */
3217         YESNO : [
3218             {
3219                 name  : 'no',
3220                 html : 'No'
3221             },
3222             {
3223                 name  :'yes',
3224                 weight : 'primary',
3225                 html : 'Yes'
3226             }
3227         ],
3228
3229         /**
3230          * Button config that displays OK and Cancel buttons
3231          * @type Object
3232          */
3233         OKCANCEL : [
3234             {
3235                name : 'cancel',
3236                 html : 'Cancel'
3237             },
3238             {
3239                 name : 'ok',
3240                 weight : 'primary',
3241                 html : 'OK'
3242             }
3243         ],
3244         /**
3245          * Button config that displays Yes, No and Cancel buttons
3246          * @type Object
3247          */
3248         YESNOCANCEL : [
3249             {
3250                 name : 'yes',
3251                 weight : 'primary',
3252                 html : 'Yes'
3253             },
3254             {
3255                 name : 'no',
3256                 html : 'No'
3257             },
3258             {
3259                 name : 'cancel',
3260                 html : 'Cancel'
3261             }
3262         ],
3263         
3264         zIndex : 10001
3265 });
3266 /*
3267  * - LGPL
3268  *
3269  * messagebox - can be used as a replace
3270  * 
3271  */
3272 /**
3273  * @class Roo.MessageBox
3274  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3275  * Example usage:
3276  *<pre><code>
3277 // Basic alert:
3278 Roo.Msg.alert('Status', 'Changes saved successfully.');
3279
3280 // Prompt for user data:
3281 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3282     if (btn == 'ok'){
3283         // process text value...
3284     }
3285 });
3286
3287 // Show a dialog using config options:
3288 Roo.Msg.show({
3289    title:'Save Changes?',
3290    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3291    buttons: Roo.Msg.YESNOCANCEL,
3292    fn: processResult,
3293    animEl: 'elId'
3294 });
3295 </code></pre>
3296  * @singleton
3297  */
3298 Roo.bootstrap.MessageBox = function(){
3299     var dlg, opt, mask, waitTimer;
3300     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3301     var buttons, activeTextEl, bwidth;
3302
3303     
3304     // private
3305     var handleButton = function(button){
3306         dlg.hide();
3307         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3308     };
3309
3310     // private
3311     var handleHide = function(){
3312         if(opt && opt.cls){
3313             dlg.el.removeClass(opt.cls);
3314         }
3315         //if(waitTimer){
3316         //    Roo.TaskMgr.stop(waitTimer);
3317         //    waitTimer = null;
3318         //}
3319     };
3320
3321     // private
3322     var updateButtons = function(b){
3323         var width = 0;
3324         if(!b){
3325             buttons["ok"].hide();
3326             buttons["cancel"].hide();
3327             buttons["yes"].hide();
3328             buttons["no"].hide();
3329             dlg.footerEl.hide();
3330             
3331             return width;
3332         }
3333         dlg.footerEl.show();
3334         for(var k in buttons){
3335             if(typeof buttons[k] != "function"){
3336                 if(b[k]){
3337                     buttons[k].show();
3338                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3339                     width += buttons[k].el.getWidth()+15;
3340                 }else{
3341                     buttons[k].hide();
3342                 }
3343             }
3344         }
3345         return width;
3346     };
3347
3348     // private
3349     var handleEsc = function(d, k, e){
3350         if(opt && opt.closable !== false){
3351             dlg.hide();
3352         }
3353         if(e){
3354             e.stopEvent();
3355         }
3356     };
3357
3358     return {
3359         /**
3360          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3361          * @return {Roo.BasicDialog} The BasicDialog element
3362          */
3363         getDialog : function(){
3364            if(!dlg){
3365                 dlg = new Roo.bootstrap.Modal( {
3366                     //draggable: true,
3367                     //resizable:false,
3368                     //constraintoviewport:false,
3369                     //fixedcenter:true,
3370                     //collapsible : false,
3371                     //shim:true,
3372                     //modal: true,
3373                 //    width: 'auto',
3374                   //  height:100,
3375                     //buttonAlign:"center",
3376                     closeClick : function(){
3377                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3378                             handleButton("no");
3379                         }else{
3380                             handleButton("cancel");
3381                         }
3382                     }
3383                 });
3384                 dlg.render();
3385                 dlg.on("hide", handleHide);
3386                 mask = dlg.mask;
3387                 //dlg.addKeyListener(27, handleEsc);
3388                 buttons = {};
3389                 this.buttons = buttons;
3390                 var bt = this.buttonText;
3391                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3392                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3393                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3394                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3395                 //Roo.log(buttons);
3396                 bodyEl = dlg.bodyEl.createChild({
3397
3398                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3399                         '<textarea class="roo-mb-textarea"></textarea>' +
3400                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3401                 });
3402                 msgEl = bodyEl.dom.firstChild;
3403                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3404                 textboxEl.enableDisplayMode();
3405                 textboxEl.addKeyListener([10,13], function(){
3406                     if(dlg.isVisible() && opt && opt.buttons){
3407                         if(opt.buttons.ok){
3408                             handleButton("ok");
3409                         }else if(opt.buttons.yes){
3410                             handleButton("yes");
3411                         }
3412                     }
3413                 });
3414                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3415                 textareaEl.enableDisplayMode();
3416                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3417                 progressEl.enableDisplayMode();
3418                 
3419                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3420                 var pf = progressEl.dom.firstChild;
3421                 if (pf) {
3422                     pp = Roo.get(pf.firstChild);
3423                     pp.setHeight(pf.offsetHeight);
3424                 }
3425                 
3426             }
3427             return dlg;
3428         },
3429
3430         /**
3431          * Updates the message box body text
3432          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3433          * the XHTML-compliant non-breaking space character '&amp;#160;')
3434          * @return {Roo.MessageBox} This message box
3435          */
3436         updateText : function(text)
3437         {
3438             if(!dlg.isVisible() && !opt.width){
3439                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3440                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3441             }
3442             msgEl.innerHTML = text || '&#160;';
3443       
3444             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3445             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3446             var w = Math.max(
3447                     Math.min(opt.width || cw , this.maxWidth), 
3448                     Math.max(opt.minWidth || this.minWidth, bwidth)
3449             );
3450             if(opt.prompt){
3451                 activeTextEl.setWidth(w);
3452             }
3453             if(dlg.isVisible()){
3454                 dlg.fixedcenter = false;
3455             }
3456             // to big, make it scroll. = But as usual stupid IE does not support
3457             // !important..
3458             
3459             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3460                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3461                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3462             } else {
3463                 bodyEl.dom.style.height = '';
3464                 bodyEl.dom.style.overflowY = '';
3465             }
3466             if (cw > w) {
3467                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3468             } else {
3469                 bodyEl.dom.style.overflowX = '';
3470             }
3471             
3472             dlg.setContentSize(w, bodyEl.getHeight());
3473             if(dlg.isVisible()){
3474                 dlg.fixedcenter = true;
3475             }
3476             return this;
3477         },
3478
3479         /**
3480          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3481          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3482          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3483          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3484          * @return {Roo.MessageBox} This message box
3485          */
3486         updateProgress : function(value, text){
3487             if(text){
3488                 this.updateText(text);
3489             }
3490             
3491             if (pp) { // weird bug on my firefox - for some reason this is not defined
3492                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3493                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3494             }
3495             return this;
3496         },        
3497
3498         /**
3499          * Returns true if the message box is currently displayed
3500          * @return {Boolean} True if the message box is visible, else false
3501          */
3502         isVisible : function(){
3503             return dlg && dlg.isVisible();  
3504         },
3505
3506         /**
3507          * Hides the message box if it is displayed
3508          */
3509         hide : function(){
3510             if(this.isVisible()){
3511                 dlg.hide();
3512             }  
3513         },
3514
3515         /**
3516          * Displays a new message box, or reinitializes an existing message box, based on the config options
3517          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3518          * The following config object properties are supported:
3519          * <pre>
3520 Property    Type             Description
3521 ----------  ---------------  ------------------------------------------------------------------------------------
3522 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3523                                    closes (defaults to undefined)
3524 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3525                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3526 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3527                                    progress and wait dialogs will ignore this property and always hide the
3528                                    close button as they can only be closed programmatically.
3529 cls               String           A custom CSS class to apply to the message box element
3530 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3531                                    displayed (defaults to 75)
3532 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3533                                    function will be btn (the name of the button that was clicked, if applicable,
3534                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3535                                    Progress and wait dialogs will ignore this option since they do not respond to
3536                                    user actions and can only be closed programmatically, so any required function
3537                                    should be called by the same code after it closes the dialog.
3538 icon              String           A CSS class that provides a background image to be used as an icon for
3539                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3540 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3541 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3542 modal             Boolean          False to allow user interaction with the page while the message box is
3543                                    displayed (defaults to true)
3544 msg               String           A string that will replace the existing message box body text (defaults
3545                                    to the XHTML-compliant non-breaking space character '&#160;')
3546 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3547 progress          Boolean          True to display a progress bar (defaults to false)
3548 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3549 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3550 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3551 title             String           The title text
3552 value             String           The string value to set into the active textbox element if displayed
3553 wait              Boolean          True to display a progress bar (defaults to false)
3554 width             Number           The width of the dialog in pixels
3555 </pre>
3556          *
3557          * Example usage:
3558          * <pre><code>
3559 Roo.Msg.show({
3560    title: 'Address',
3561    msg: 'Please enter your address:',
3562    width: 300,
3563    buttons: Roo.MessageBox.OKCANCEL,
3564    multiline: true,
3565    fn: saveAddress,
3566    animEl: 'addAddressBtn'
3567 });
3568 </code></pre>
3569          * @param {Object} config Configuration options
3570          * @return {Roo.MessageBox} This message box
3571          */
3572         show : function(options)
3573         {
3574             
3575             // this causes nightmares if you show one dialog after another
3576             // especially on callbacks..
3577              
3578             if(this.isVisible()){
3579                 
3580                 this.hide();
3581                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3582                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3583                 Roo.log("New Dialog Message:" +  options.msg )
3584                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3585                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3586                 
3587             }
3588             var d = this.getDialog();
3589             opt = options;
3590             d.setTitle(opt.title || "&#160;");
3591             d.closeEl.setDisplayed(opt.closable !== false);
3592             activeTextEl = textboxEl;
3593             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3594             if(opt.prompt){
3595                 if(opt.multiline){
3596                     textboxEl.hide();
3597                     textareaEl.show();
3598                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3599                         opt.multiline : this.defaultTextHeight);
3600                     activeTextEl = textareaEl;
3601                 }else{
3602                     textboxEl.show();
3603                     textareaEl.hide();
3604                 }
3605             }else{
3606                 textboxEl.hide();
3607                 textareaEl.hide();
3608             }
3609             progressEl.setDisplayed(opt.progress === true);
3610             if (opt.progress) {
3611                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
3612             }
3613             this.updateProgress(0);
3614             activeTextEl.dom.value = opt.value || "";
3615             if(opt.prompt){
3616                 dlg.setDefaultButton(activeTextEl);
3617             }else{
3618                 var bs = opt.buttons;
3619                 var db = null;
3620                 if(bs && bs.ok){
3621                     db = buttons["ok"];
3622                 }else if(bs && bs.yes){
3623                     db = buttons["yes"];
3624                 }
3625                 dlg.setDefaultButton(db);
3626             }
3627             bwidth = updateButtons(opt.buttons);
3628             this.updateText(opt.msg);
3629             if(opt.cls){
3630                 d.el.addClass(opt.cls);
3631             }
3632             d.proxyDrag = opt.proxyDrag === true;
3633             d.modal = opt.modal !== false;
3634             d.mask = opt.modal !== false ? mask : false;
3635             if(!d.isVisible()){
3636                 // force it to the end of the z-index stack so it gets a cursor in FF
3637                 document.body.appendChild(dlg.el.dom);
3638                 d.animateTarget = null;
3639                 d.show(options.animEl);
3640             }
3641             return this;
3642         },
3643
3644         /**
3645          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3646          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3647          * and closing the message box when the process is complete.
3648          * @param {String} title The title bar text
3649          * @param {String} msg The message box body text
3650          * @return {Roo.MessageBox} This message box
3651          */
3652         progress : function(title, msg){
3653             this.show({
3654                 title : title,
3655                 msg : msg,
3656                 buttons: false,
3657                 progress:true,
3658                 closable:false,
3659                 minWidth: this.minProgressWidth,
3660                 modal : true
3661             });
3662             return this;
3663         },
3664
3665         /**
3666          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3667          * If a callback function is passed it will be called after the user clicks the button, and the
3668          * id of the button that was clicked will be passed as the only parameter to the callback
3669          * (could also be the top-right close button).
3670          * @param {String} title The title bar text
3671          * @param {String} msg The message box body text
3672          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3673          * @param {Object} scope (optional) The scope of the callback function
3674          * @return {Roo.MessageBox} This message box
3675          */
3676         alert : function(title, msg, fn, scope)
3677         {
3678             this.show({
3679                 title : title,
3680                 msg : msg,
3681                 buttons: this.OK,
3682                 fn: fn,
3683                 closable : false,
3684                 scope : scope,
3685                 modal : true
3686             });
3687             return this;
3688         },
3689
3690         /**
3691          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3692          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3693          * You are responsible for closing the message box when the process is complete.
3694          * @param {String} msg The message box body text
3695          * @param {String} title (optional) The title bar text
3696          * @return {Roo.MessageBox} This message box
3697          */
3698         wait : function(msg, title){
3699             this.show({
3700                 title : title,
3701                 msg : msg,
3702                 buttons: false,
3703                 closable:false,
3704                 progress:true,
3705                 modal:true,
3706                 width:300,
3707                 wait:true
3708             });
3709             waitTimer = Roo.TaskMgr.start({
3710                 run: function(i){
3711                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3712                 },
3713                 interval: 1000
3714             });
3715             return this;
3716         },
3717
3718         /**
3719          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3720          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3721          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3722          * @param {String} title The title bar text
3723          * @param {String} msg The message box body text
3724          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3725          * @param {Object} scope (optional) The scope of the callback function
3726          * @return {Roo.MessageBox} This message box
3727          */
3728         confirm : function(title, msg, fn, scope){
3729             this.show({
3730                 title : title,
3731                 msg : msg,
3732                 buttons: this.YESNO,
3733                 fn: fn,
3734                 scope : scope,
3735                 modal : true
3736             });
3737             return this;
3738         },
3739
3740         /**
3741          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3742          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3743          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3744          * (could also be the top-right close button) and the text that was entered will be passed as the two
3745          * parameters to the callback.
3746          * @param {String} title The title bar text
3747          * @param {String} msg The message box body text
3748          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3749          * @param {Object} scope (optional) The scope of the callback function
3750          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3751          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3752          * @return {Roo.MessageBox} This message box
3753          */
3754         prompt : function(title, msg, fn, scope, multiline){
3755             this.show({
3756                 title : title,
3757                 msg : msg,
3758                 buttons: this.OKCANCEL,
3759                 fn: fn,
3760                 minWidth:250,
3761                 scope : scope,
3762                 prompt:true,
3763                 multiline: multiline,
3764                 modal : true
3765             });
3766             return this;
3767         },
3768
3769         /**
3770          * Button config that displays a single OK button
3771          * @type Object
3772          */
3773         OK : {ok:true},
3774         /**
3775          * Button config that displays Yes and No buttons
3776          * @type Object
3777          */
3778         YESNO : {yes:true, no:true},
3779         /**
3780          * Button config that displays OK and Cancel buttons
3781          * @type Object
3782          */
3783         OKCANCEL : {ok:true, cancel:true},
3784         /**
3785          * Button config that displays Yes, No and Cancel buttons
3786          * @type Object
3787          */
3788         YESNOCANCEL : {yes:true, no:true, cancel:true},
3789
3790         /**
3791          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3792          * @type Number
3793          */
3794         defaultTextHeight : 75,
3795         /**
3796          * The maximum width in pixels of the message box (defaults to 600)
3797          * @type Number
3798          */
3799         maxWidth : 600,
3800         /**
3801          * The minimum width in pixels of the message box (defaults to 100)
3802          * @type Number
3803          */
3804         minWidth : 100,
3805         /**
3806          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3807          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3808          * @type Number
3809          */
3810         minProgressWidth : 250,
3811         /**
3812          * An object containing the default button text strings that can be overriden for localized language support.
3813          * Supported properties are: ok, cancel, yes and no.
3814          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3815          * @type Object
3816          */
3817         buttonText : {
3818             ok : "OK",
3819             cancel : "Cancel",
3820             yes : "Yes",
3821             no : "No"
3822         }
3823     };
3824 }();
3825
3826 /**
3827  * Shorthand for {@link Roo.MessageBox}
3828  */
3829 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3830 Roo.Msg = Roo.Msg || Roo.MessageBox;
3831 /*
3832  * - LGPL
3833  *
3834  * navbar
3835  * 
3836  */
3837
3838 /**
3839  * @class Roo.bootstrap.Navbar
3840  * @extends Roo.bootstrap.Component
3841  * Bootstrap Navbar class
3842
3843  * @constructor
3844  * Create a new Navbar
3845  * @param {Object} config The config object
3846  */
3847
3848
3849 Roo.bootstrap.Navbar = function(config){
3850     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3851     this.addEvents({
3852         // raw events
3853         /**
3854          * @event beforetoggle
3855          * Fire before toggle the menu
3856          * @param {Roo.EventObject} e
3857          */
3858         "beforetoggle" : true
3859     });
3860 };
3861
3862 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3863     
3864     
3865    
3866     // private
3867     navItems : false,
3868     loadMask : false,
3869     
3870     
3871     getAutoCreate : function(){
3872         
3873         
3874         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3875         
3876     },
3877     
3878     initEvents :function ()
3879     {
3880         //Roo.log(this.el.select('.navbar-toggle',true));
3881         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
3882         
3883         var mark = {
3884             tag: "div",
3885             cls:"x-dlg-mask"
3886         };
3887         
3888         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3889         
3890         var size = this.el.getSize();
3891         this.maskEl.setSize(size.width, size.height);
3892         this.maskEl.enableDisplayMode("block");
3893         this.maskEl.hide();
3894         
3895         if(this.loadMask){
3896             this.maskEl.show();
3897         }
3898     },
3899     
3900     
3901     getChildContainer : function()
3902     {
3903         if (this.el && this.el.select('.collapse').getCount()) {
3904             return this.el.select('.collapse',true).first();
3905         }
3906         
3907         return this.el;
3908     },
3909     
3910     mask : function()
3911     {
3912         this.maskEl.show();
3913     },
3914     
3915     unmask : function()
3916     {
3917         this.maskEl.hide();
3918     },
3919     onToggle : function()
3920     {
3921         
3922         if(this.fireEvent('beforetoggle', this) === false){
3923             return;
3924         }
3925         var ce = this.el.select('.navbar-collapse',true).first();
3926       
3927         if (!ce.hasClass('show')) {
3928            this.expand();
3929         } else {
3930             this.collapse();
3931         }
3932         
3933         
3934     
3935     },
3936     /**
3937      * Expand the navbar pulldown 
3938      */
3939     expand : function ()
3940     {
3941        
3942         var ce = this.el.select('.navbar-collapse',true).first();
3943         if (ce.hasClass('collapsing')) {
3944             return;
3945         }
3946         ce.dom.style.height = '';
3947                // show it...
3948         ce.addClass('in'); // old...
3949         ce.removeClass('collapse');
3950         ce.addClass('show');
3951         var h = ce.getHeight();
3952         Roo.log(h);
3953         ce.removeClass('show');
3954         // at this point we should be able to see it..
3955         ce.addClass('collapsing');
3956         
3957         ce.setHeight(0); // resize it ...
3958         ce.on('transitionend', function() {
3959             //Roo.log('done transition');
3960             ce.removeClass('collapsing');
3961             ce.addClass('show');
3962             ce.removeClass('collapse');
3963
3964             ce.dom.style.height = '';
3965         }, this, { single: true} );
3966         ce.setHeight(h);
3967         ce.dom.scrollTop = 0;
3968     },
3969     /**
3970      * Collapse the navbar pulldown 
3971      */
3972     collapse : function()
3973     {
3974          var ce = this.el.select('.navbar-collapse',true).first();
3975        
3976         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
3977             // it's collapsed or collapsing..
3978             return;
3979         }
3980         ce.removeClass('in'); // old...
3981         ce.setHeight(ce.getHeight());
3982         ce.removeClass('show');
3983         ce.addClass('collapsing');
3984         
3985         ce.on('transitionend', function() {
3986             ce.dom.style.height = '';
3987             ce.removeClass('collapsing');
3988             ce.addClass('collapse');
3989         }, this, { single: true} );
3990         ce.setHeight(0);
3991     }
3992     
3993     
3994     
3995 });
3996
3997
3998
3999  
4000
4001  /*
4002  * - LGPL
4003  *
4004  * navbar
4005  * 
4006  */
4007
4008 /**
4009  * @class Roo.bootstrap.NavSimplebar
4010  * @extends Roo.bootstrap.Navbar
4011  * Bootstrap Sidebar class
4012  *
4013  * @cfg {Boolean} inverse is inverted color
4014  * 
4015  * @cfg {String} type (nav | pills | tabs)
4016  * @cfg {Boolean} arrangement stacked | justified
4017  * @cfg {String} align (left | right) alignment
4018  * 
4019  * @cfg {Boolean} main (true|false) main nav bar? default false
4020  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
4021  * 
4022  * @cfg {String} tag (header|footer|nav|div) default is nav 
4023
4024  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
4025  * 
4026  * 
4027  * @constructor
4028  * Create a new Sidebar
4029  * @param {Object} config The config object
4030  */
4031
4032
4033 Roo.bootstrap.NavSimplebar = function(config){
4034     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
4035 };
4036
4037 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
4038     
4039     inverse: false,
4040     
4041     type: false,
4042     arrangement: '',
4043     align : false,
4044     
4045     weight : 'light',
4046     
4047     main : false,
4048     
4049     
4050     tag : false,
4051     
4052     
4053     getAutoCreate : function(){
4054         
4055         
4056         var cfg = {
4057             tag : this.tag || 'div',
4058             cls : 'navbar navbar-expand-lg roo-navbar-simple'
4059         };
4060         if (['light','white'].indexOf(this.weight) > -1) {
4061             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4062         }
4063         cfg.cls += ' bg-' + this.weight;
4064         
4065         if (this.inverse) {
4066             cfg.cls += ' navbar-inverse';
4067             
4068         }
4069         
4070         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
4071         
4072         //if (Roo.bootstrap.version == 4) {
4073         //    return cfg;
4074         //}
4075         
4076         cfg.cn = [
4077             {
4078                 cls: 'nav',
4079                 tag : 'ul'
4080             }
4081         ];
4082         
4083          
4084         this.type = this.type || 'nav';
4085         if (['tabs','pills'].indexOf(this.type) != -1) {
4086             cfg.cn[0].cls += ' nav-' + this.type
4087         
4088         
4089         } else {
4090             if (this.type!=='nav') {
4091                 Roo.log('nav type must be nav/tabs/pills')
4092             }
4093             cfg.cn[0].cls += ' navbar-nav'
4094         }
4095         
4096         
4097         
4098         
4099         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
4100             cfg.cn[0].cls += ' nav-' + this.arrangement;
4101         }
4102         
4103         
4104         if (this.align === 'right') {
4105             cfg.cn[0].cls += ' navbar-right';
4106         }
4107         
4108         
4109         
4110         
4111         return cfg;
4112     
4113         
4114     }
4115     
4116     
4117     
4118 });
4119
4120
4121
4122  
4123
4124  
4125        /*
4126  * - LGPL
4127  *
4128  * navbar
4129  * navbar-fixed-top
4130  * navbar-expand-md  fixed-top 
4131  */
4132
4133 /**
4134  * @class Roo.bootstrap.NavHeaderbar
4135  * @extends Roo.bootstrap.NavSimplebar
4136  * Bootstrap Sidebar class
4137  *
4138  * @cfg {String} brand what is brand
4139  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4140  * @cfg {String} brand_href href of the brand
4141  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
4142  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4143  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4144  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4145  * 
4146  * @constructor
4147  * Create a new Sidebar
4148  * @param {Object} config The config object
4149  */
4150
4151
4152 Roo.bootstrap.NavHeaderbar = function(config){
4153     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4154       
4155 };
4156
4157 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
4158     
4159     position: '',
4160     brand: '',
4161     brand_href: false,
4162     srButton : true,
4163     autohide : false,
4164     desktopCenter : false,
4165    
4166     
4167     getAutoCreate : function(){
4168         
4169         var   cfg = {
4170             tag: this.nav || 'nav',
4171             cls: 'navbar navbar-expand-md',
4172             role: 'navigation',
4173             cn: []
4174         };
4175         
4176         var cn = cfg.cn;
4177         if (this.desktopCenter) {
4178             cn.push({cls : 'container', cn : []});
4179             cn = cn[0].cn;
4180         }
4181         
4182         if(this.srButton){
4183             var btn = {
4184                 tag: 'button',
4185                 type: 'button',
4186                 cls: 'navbar-toggle navbar-toggler',
4187                 'data-toggle': 'collapse',
4188                 cn: [
4189                     {
4190                         tag: 'span',
4191                         cls: 'sr-only',
4192                         html: 'Toggle navigation'
4193                     },
4194                     {
4195                         tag: 'span',
4196                         cls: 'icon-bar navbar-toggler-icon'
4197                     },
4198                     {
4199                         tag: 'span',
4200                         cls: 'icon-bar'
4201                     },
4202                     {
4203                         tag: 'span',
4204                         cls: 'icon-bar'
4205                     }
4206                 ]
4207             };
4208             
4209             cn.push( Roo.bootstrap.version == 4 ? btn : {
4210                 tag: 'div',
4211                 cls: 'navbar-header',
4212                 cn: [
4213                     btn
4214                 ]
4215             });
4216         }
4217         
4218         cn.push({
4219             tag: 'div',
4220             cls: 'collapse navbar-collapse',
4221             cn : []
4222         });
4223         
4224         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4225         
4226         if (['light','white'].indexOf(this.weight) > -1) {
4227             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4228         }
4229         cfg.cls += ' bg-' + this.weight;
4230         
4231         
4232         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4233             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4234             
4235             // tag can override this..
4236             
4237             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4238         }
4239         
4240         if (this.brand !== '') {
4241             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4242             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4243                 tag: 'a',
4244                 href: this.brand_href ? this.brand_href : '#',
4245                 cls: 'navbar-brand',
4246                 cn: [
4247                 this.brand
4248                 ]
4249             });
4250         }
4251         
4252         if(this.main){
4253             cfg.cls += ' main-nav';
4254         }
4255         
4256         
4257         return cfg;
4258
4259         
4260     },
4261     getHeaderChildContainer : function()
4262     {
4263         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4264             return this.el.select('.navbar-header',true).first();
4265         }
4266         
4267         return this.getChildContainer();
4268     },
4269     
4270     
4271     initEvents : function()
4272     {
4273         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4274         
4275         if (this.autohide) {
4276             
4277             var prevScroll = 0;
4278             var ft = this.el;
4279             
4280             Roo.get(document).on('scroll',function(e) {
4281                 var ns = Roo.get(document).getScroll().top;
4282                 var os = prevScroll;
4283                 prevScroll = ns;
4284                 
4285                 if(ns > os){
4286                     ft.removeClass('slideDown');
4287                     ft.addClass('slideUp');
4288                     return;
4289                 }
4290                 ft.removeClass('slideUp');
4291                 ft.addClass('slideDown');
4292                  
4293               
4294           },this);
4295         }
4296     }    
4297     
4298 });
4299
4300
4301
4302  
4303
4304  /*
4305  * - LGPL
4306  *
4307  * navbar
4308  * 
4309  */
4310
4311 /**
4312  * @class Roo.bootstrap.NavSidebar
4313  * @extends Roo.bootstrap.Navbar
4314  * Bootstrap Sidebar class
4315  * 
4316  * @constructor
4317  * Create a new Sidebar
4318  * @param {Object} config The config object
4319  */
4320
4321
4322 Roo.bootstrap.NavSidebar = function(config){
4323     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4324 };
4325
4326 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4327     
4328     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4329     
4330     getAutoCreate : function(){
4331         
4332         
4333         return  {
4334             tag: 'div',
4335             cls: 'sidebar sidebar-nav'
4336         };
4337     
4338         
4339     }
4340     
4341     
4342     
4343 });
4344
4345
4346
4347  
4348
4349  /*
4350  * - LGPL
4351  *
4352  * nav group
4353  * 
4354  */
4355
4356 /**
4357  * @class Roo.bootstrap.NavGroup
4358  * @extends Roo.bootstrap.Component
4359  * Bootstrap NavGroup class
4360  * @cfg {String} align (left|right)
4361  * @cfg {Boolean} inverse
4362  * @cfg {String} type (nav|pills|tab) default nav
4363  * @cfg {String} navId - reference Id for navbar.
4364
4365  * 
4366  * @constructor
4367  * Create a new nav group
4368  * @param {Object} config The config object
4369  */
4370
4371 Roo.bootstrap.NavGroup = function(config){
4372     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4373     this.navItems = [];
4374    
4375     Roo.bootstrap.NavGroup.register(this);
4376      this.addEvents({
4377         /**
4378              * @event changed
4379              * Fires when the active item changes
4380              * @param {Roo.bootstrap.NavGroup} this
4381              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4382              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4383          */
4384         'changed': true
4385      });
4386     
4387 };
4388
4389 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4390     
4391     align: '',
4392     inverse: false,
4393     form: false,
4394     type: 'nav',
4395     navId : '',
4396     // private
4397     
4398     navItems : false, 
4399     
4400     getAutoCreate : function()
4401     {
4402         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4403         
4404         cfg = {
4405             tag : 'ul',
4406             cls: 'nav' 
4407         };
4408         if (Roo.bootstrap.version == 4) {
4409             if (['tabs','pills'].indexOf(this.type) != -1) {
4410                 cfg.cls += ' nav-' + this.type; 
4411             } else {
4412                 cfg.cls += ' navbar-nav';
4413             }
4414         } else {
4415             if (['tabs','pills'].indexOf(this.type) != -1) {
4416                 cfg.cls += ' nav-' + this.type
4417             } else {
4418                 if (this.type !== 'nav') {
4419                     Roo.log('nav type must be nav/tabs/pills')
4420                 }
4421                 cfg.cls += ' navbar-nav'
4422             }
4423         }
4424         
4425         if (this.parent() && this.parent().sidebar) {
4426             cfg = {
4427                 tag: 'ul',
4428                 cls: 'dashboard-menu sidebar-menu'
4429             };
4430             
4431             return cfg;
4432         }
4433         
4434         if (this.form === true) {
4435             cfg = {
4436                 tag: 'form',
4437                 cls: 'navbar-form form-inline'
4438             };
4439             
4440             if (this.align === 'right') {
4441                 cfg.cls += ' navbar-right ml-md-auto';
4442             } else {
4443                 cfg.cls += ' navbar-left';
4444             }
4445         }
4446         
4447         if (this.align === 'right') {
4448             cfg.cls += ' navbar-right ml-md-auto';
4449         } else {
4450             cfg.cls += ' mr-auto';
4451         }
4452         
4453         if (this.inverse) {
4454             cfg.cls += ' navbar-inverse';
4455             
4456         }
4457         
4458         
4459         return cfg;
4460     },
4461     /**
4462     * sets the active Navigation item
4463     * @param {Roo.bootstrap.NavItem} the new current navitem
4464     */
4465     setActiveItem : function(item)
4466     {
4467         var prev = false;
4468         Roo.each(this.navItems, function(v){
4469             if (v == item) {
4470                 return ;
4471             }
4472             if (v.isActive()) {
4473                 v.setActive(false, true);
4474                 prev = v;
4475                 
4476             }
4477             
4478         });
4479
4480         item.setActive(true, true);
4481         this.fireEvent('changed', this, item, prev);
4482         
4483         
4484     },
4485     /**
4486     * gets the active Navigation item
4487     * @return {Roo.bootstrap.NavItem} the current navitem
4488     */
4489     getActive : function()
4490     {
4491         
4492         var prev = false;
4493         Roo.each(this.navItems, function(v){
4494             
4495             if (v.isActive()) {
4496                 prev = v;
4497                 
4498             }
4499             
4500         });
4501         return prev;
4502     },
4503     
4504     indexOfNav : function()
4505     {
4506         
4507         var prev = false;
4508         Roo.each(this.navItems, function(v,i){
4509             
4510             if (v.isActive()) {
4511                 prev = i;
4512                 
4513             }
4514             
4515         });
4516         return prev;
4517     },
4518     /**
4519     * adds a Navigation item
4520     * @param {Roo.bootstrap.NavItem} the navitem to add
4521     */
4522     addItem : function(cfg)
4523     {
4524         if (this.form && Roo.bootstrap.version == 4) {
4525             cfg.tag = 'div';
4526         }
4527         var cn = new Roo.bootstrap.NavItem(cfg);
4528         this.register(cn);
4529         cn.parentId = this.id;
4530         cn.onRender(this.el, null);
4531         return cn;
4532     },
4533     /**
4534     * register a Navigation item
4535     * @param {Roo.bootstrap.NavItem} the navitem to add
4536     */
4537     register : function(item)
4538     {
4539         this.navItems.push( item);
4540         item.navId = this.navId;
4541     
4542     },
4543     
4544     /**
4545     * clear all the Navigation item
4546     */
4547    
4548     clearAll : function()
4549     {
4550         this.navItems = [];
4551         this.el.dom.innerHTML = '';
4552     },
4553     
4554     getNavItem: function(tabId)
4555     {
4556         var ret = false;
4557         Roo.each(this.navItems, function(e) {
4558             if (e.tabId == tabId) {
4559                ret =  e;
4560                return false;
4561             }
4562             return true;
4563             
4564         });
4565         return ret;
4566     },
4567     
4568     setActiveNext : function()
4569     {
4570         var i = this.indexOfNav(this.getActive());
4571         if (i > this.navItems.length) {
4572             return;
4573         }
4574         this.setActiveItem(this.navItems[i+1]);
4575     },
4576     setActivePrev : function()
4577     {
4578         var i = this.indexOfNav(this.getActive());
4579         if (i  < 1) {
4580             return;
4581         }
4582         this.setActiveItem(this.navItems[i-1]);
4583     },
4584     clearWasActive : function(except) {
4585         Roo.each(this.navItems, function(e) {
4586             if (e.tabId != except.tabId && e.was_active) {
4587                e.was_active = false;
4588                return false;
4589             }
4590             return true;
4591             
4592         });
4593     },
4594     getWasActive : function ()
4595     {
4596         var r = false;
4597         Roo.each(this.navItems, function(e) {
4598             if (e.was_active) {
4599                r = e;
4600                return false;
4601             }
4602             return true;
4603             
4604         });
4605         return r;
4606     }
4607     
4608     
4609 });
4610
4611  
4612 Roo.apply(Roo.bootstrap.NavGroup, {
4613     
4614     groups: {},
4615      /**
4616     * register a Navigation Group
4617     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4618     */
4619     register : function(navgrp)
4620     {
4621         this.groups[navgrp.navId] = navgrp;
4622         
4623     },
4624     /**
4625     * fetch a Navigation Group based on the navigation ID
4626     * @param {string} the navgroup to add
4627     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4628     */
4629     get: function(navId) {
4630         if (typeof(this.groups[navId]) == 'undefined') {
4631             return false;
4632             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4633         }
4634         return this.groups[navId] ;
4635     }
4636     
4637     
4638     
4639 });
4640
4641  /*
4642  * - LGPL
4643  *
4644  * row
4645  * 
4646  */
4647
4648 /**
4649  * @class Roo.bootstrap.NavItem
4650  * @extends Roo.bootstrap.Component
4651  * Bootstrap Navbar.NavItem class
4652  * @cfg {String} href  link to
4653  * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
4654
4655  * @cfg {String} html content of button
4656  * @cfg {String} badge text inside badge
4657  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4658  * @cfg {String} glyphicon DEPRICATED - use fa
4659  * @cfg {String} icon DEPRICATED - use fa
4660  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4661  * @cfg {Boolean} active Is item active
4662  * @cfg {Boolean} disabled Is item disabled
4663  
4664  * @cfg {Boolean} preventDefault (true | false) default false
4665  * @cfg {String} tabId the tab that this item activates.
4666  * @cfg {String} tagtype (a|span) render as a href or span?
4667  * @cfg {Boolean} animateRef (true|false) link to element default false  
4668   
4669  * @constructor
4670  * Create a new Navbar Item
4671  * @param {Object} config The config object
4672  */
4673 Roo.bootstrap.NavItem = function(config){
4674     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4675     this.addEvents({
4676         // raw events
4677         /**
4678          * @event click
4679          * The raw click event for the entire grid.
4680          * @param {Roo.EventObject} e
4681          */
4682         "click" : true,
4683          /**
4684             * @event changed
4685             * Fires when the active item active state changes
4686             * @param {Roo.bootstrap.NavItem} this
4687             * @param {boolean} state the new state
4688              
4689          */
4690         'changed': true,
4691         /**
4692             * @event scrollto
4693             * Fires when scroll to element
4694             * @param {Roo.bootstrap.NavItem} this
4695             * @param {Object} options
4696             * @param {Roo.EventObject} e
4697              
4698          */
4699         'scrollto': true
4700     });
4701    
4702 };
4703
4704 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4705     
4706     href: false,
4707     html: '',
4708     badge: '',
4709     icon: false,
4710     fa : false,
4711     glyphicon: false,
4712     active: false,
4713     preventDefault : false,
4714     tabId : false,
4715     tagtype : 'a',
4716     tag: 'li',
4717     disabled : false,
4718     animateRef : false,
4719     was_active : false,
4720     button_weight : '',
4721     button_outline : false,
4722     
4723     navLink: false,
4724     
4725     getAutoCreate : function(){
4726          
4727         var cfg = {
4728             tag: this.tag,
4729             cls: 'nav-item'
4730         };
4731         
4732         if (this.active) {
4733             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4734         }
4735         if (this.disabled) {
4736             cfg.cls += ' disabled';
4737         }
4738         
4739         // BS4 only?
4740         if (this.button_weight.length) {
4741             cfg.tag = this.href ? 'a' : 'button';
4742             cfg.html = this.html || '';
4743             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
4744             if (this.href) {
4745                 cfg.href = this.href;
4746             }
4747             if (this.fa) {
4748                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
4749             }
4750             
4751             // menu .. should add dropdown-menu class - so no need for carat..
4752             
4753             if (this.badge !== '') {
4754                  
4755                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4756             }
4757             return cfg;
4758         }
4759         
4760         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
4761             cfg.cn = [
4762                 {
4763                     tag: this.tagtype,
4764                     href : this.href || "#",
4765                     html: this.html || ''
4766                 }
4767             ];
4768             if (this.tagtype == 'a') {
4769                 cfg.cn[0].cls = 'nav-link';
4770             }
4771             if (this.icon) {
4772                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
4773             }
4774             if (this.fa) {
4775                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
4776             }
4777             if(this.glyphicon) {
4778                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4779             }
4780             
4781             if (this.menu) {
4782                 
4783                 cfg.cn[0].html += " <span class='caret'></span>";
4784              
4785             }
4786             
4787             if (this.badge !== '') {
4788                  
4789                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4790             }
4791         }
4792         
4793         
4794         
4795         return cfg;
4796     },
4797     onRender : function(ct, position)
4798     {
4799        // Roo.log("Call onRender: " + this.xtype);
4800         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
4801             this.tag = 'div';
4802         }
4803         
4804         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
4805         this.navLink = this.el.select('.nav-link',true).first();
4806         return ret;
4807     },
4808       
4809     
4810     initEvents: function() 
4811     {
4812         if (typeof (this.menu) != 'undefined') {
4813             this.menu.parentType = this.xtype;
4814             this.menu.triggerEl = this.el;
4815             this.menu = this.addxtype(Roo.apply({}, this.menu));
4816         }
4817         
4818         this.el.select('a',true).on('click', this.onClick, this);
4819         
4820         if(this.tagtype == 'span'){
4821             this.el.select('span',true).on('click', this.onClick, this);
4822         }
4823        
4824         // at this point parent should be available..
4825         this.parent().register(this);
4826     },
4827     
4828     onClick : function(e)
4829     {
4830         if (e.getTarget('.dropdown-menu-item')) {
4831             // did you click on a menu itemm.... - then don't trigger onclick..
4832             return;
4833         }
4834         
4835         if(
4836                 this.preventDefault || 
4837                 this.href == '#' 
4838         ){
4839             Roo.log("NavItem - prevent Default?");
4840             e.preventDefault();
4841         }
4842         
4843         if (this.disabled) {
4844             return;
4845         }
4846         
4847         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4848         if (tg && tg.transition) {
4849             Roo.log("waiting for the transitionend");
4850             return;
4851         }
4852         
4853         
4854         
4855         //Roo.log("fire event clicked");
4856         if(this.fireEvent('click', this, e) === false){
4857             return;
4858         };
4859         
4860         if(this.tagtype == 'span'){
4861             return;
4862         }
4863         
4864         //Roo.log(this.href);
4865         var ael = this.el.select('a',true).first();
4866         //Roo.log(ael);
4867         
4868         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4869             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4870             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4871                 return; // ignore... - it's a 'hash' to another page.
4872             }
4873             Roo.log("NavItem - prevent Default?");
4874             e.preventDefault();
4875             this.scrollToElement(e);
4876         }
4877         
4878         
4879         var p =  this.parent();
4880    
4881         if (['tabs','pills'].indexOf(p.type)!==-1) {
4882             if (typeof(p.setActiveItem) !== 'undefined') {
4883                 p.setActiveItem(this);
4884             }
4885         }
4886         
4887         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4888         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4889             // remove the collapsed menu expand...
4890             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4891         }
4892     },
4893     
4894     isActive: function () {
4895         return this.active
4896     },
4897     setActive : function(state, fire, is_was_active)
4898     {
4899         if (this.active && !state && this.navId) {
4900             this.was_active = true;
4901             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4902             if (nv) {
4903                 nv.clearWasActive(this);
4904             }
4905             
4906         }
4907         this.active = state;
4908         
4909         if (!state ) {
4910             this.el.removeClass('active');
4911             this.navLink ? this.navLink.removeClass('active') : false;
4912         } else if (!this.el.hasClass('active')) {
4913             
4914             this.el.addClass('active');
4915             if (Roo.bootstrap.version == 4 && this.navLink ) {
4916                 this.navLink.addClass('active');
4917             }
4918             
4919         }
4920         if (fire) {
4921             this.fireEvent('changed', this, state);
4922         }
4923         
4924         // show a panel if it's registered and related..
4925         
4926         if (!this.navId || !this.tabId || !state || is_was_active) {
4927             return;
4928         }
4929         
4930         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4931         if (!tg) {
4932             return;
4933         }
4934         var pan = tg.getPanelByName(this.tabId);
4935         if (!pan) {
4936             return;
4937         }
4938         // if we can not flip to new panel - go back to old nav highlight..
4939         if (false == tg.showPanel(pan)) {
4940             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4941             if (nv) {
4942                 var onav = nv.getWasActive();
4943                 if (onav) {
4944                     onav.setActive(true, false, true);
4945                 }
4946             }
4947             
4948         }
4949         
4950         
4951         
4952     },
4953      // this should not be here...
4954     setDisabled : function(state)
4955     {
4956         this.disabled = state;
4957         if (!state ) {
4958             this.el.removeClass('disabled');
4959         } else if (!this.el.hasClass('disabled')) {
4960             this.el.addClass('disabled');
4961         }
4962         
4963     },
4964     
4965     /**
4966      * Fetch the element to display the tooltip on.
4967      * @return {Roo.Element} defaults to this.el
4968      */
4969     tooltipEl : function()
4970     {
4971         return this.el.select('' + this.tagtype + '', true).first();
4972     },
4973     
4974     scrollToElement : function(e)
4975     {
4976         var c = document.body;
4977         
4978         /*
4979          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4980          */
4981         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4982             c = document.documentElement;
4983         }
4984         
4985         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4986         
4987         if(!target){
4988             return;
4989         }
4990
4991         var o = target.calcOffsetsTo(c);
4992         
4993         var options = {
4994             target : target,
4995             value : o[1]
4996         };
4997         
4998         this.fireEvent('scrollto', this, options, e);
4999         
5000         Roo.get(c).scrollTo('top', options.value, true);
5001         
5002         return;
5003     }
5004 });
5005  
5006
5007  /*
5008  * - LGPL
5009  *
5010  * sidebar item
5011  *
5012  *  li
5013  *    <span> icon </span>
5014  *    <span> text </span>
5015  *    <span>badge </span>
5016  */
5017
5018 /**
5019  * @class Roo.bootstrap.NavSidebarItem
5020  * @extends Roo.bootstrap.NavItem
5021  * Bootstrap Navbar.NavSidebarItem class
5022  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
5023  * {Boolean} open is the menu open
5024  * {Boolean} buttonView use button as the tigger el rather that a (default false)
5025  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
5026  * {String} buttonSize (sm|md|lg)the extra classes for the button
5027  * {Boolean} showArrow show arrow next to the text (default true)
5028  * @constructor
5029  * Create a new Navbar Button
5030  * @param {Object} config The config object
5031  */
5032 Roo.bootstrap.NavSidebarItem = function(config){
5033     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
5034     this.addEvents({
5035         // raw events
5036         /**
5037          * @event click
5038          * The raw click event for the entire grid.
5039          * @param {Roo.EventObject} e
5040          */
5041         "click" : true,
5042          /**
5043             * @event changed
5044             * Fires when the active item active state changes
5045             * @param {Roo.bootstrap.NavSidebarItem} this
5046             * @param {boolean} state the new state
5047              
5048          */
5049         'changed': true
5050     });
5051    
5052 };
5053
5054 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
5055     
5056     badgeWeight : 'default',
5057     
5058     open: false,
5059     
5060     buttonView : false,
5061     
5062     buttonWeight : 'default',
5063     
5064     buttonSize : 'md',
5065     
5066     showArrow : true,
5067     
5068     getAutoCreate : function(){
5069         
5070         
5071         var a = {
5072                 tag: 'a',
5073                 href : this.href || '#',
5074                 cls: '',
5075                 html : '',
5076                 cn : []
5077         };
5078         
5079         if(this.buttonView){
5080             a = {
5081                 tag: 'button',
5082                 href : this.href || '#',
5083                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
5084                 html : this.html,
5085                 cn : []
5086             };
5087         }
5088         
5089         var cfg = {
5090             tag: 'li',
5091             cls: '',
5092             cn: [ a ]
5093         };
5094         
5095         if (this.active) {
5096             cfg.cls += ' active';
5097         }
5098         
5099         if (this.disabled) {
5100             cfg.cls += ' disabled';
5101         }
5102         if (this.open) {
5103             cfg.cls += ' open x-open';
5104         }
5105         // left icon..
5106         if (this.glyphicon || this.icon) {
5107             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
5108             a.cn.push({ tag : 'i', cls : c }) ;
5109         }
5110         
5111         if(!this.buttonView){
5112             var span = {
5113                 tag: 'span',
5114                 html : this.html || ''
5115             };
5116
5117             a.cn.push(span);
5118             
5119         }
5120         
5121         if (this.badge !== '') {
5122             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
5123         }
5124         
5125         if (this.menu) {
5126             
5127             if(this.showArrow){
5128                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5129             }
5130             
5131             a.cls += ' dropdown-toggle treeview' ;
5132         }
5133         
5134         return cfg;
5135     },
5136     
5137     initEvents : function()
5138     { 
5139         if (typeof (this.menu) != 'undefined') {
5140             this.menu.parentType = this.xtype;
5141             this.menu.triggerEl = this.el;
5142             this.menu = this.addxtype(Roo.apply({}, this.menu));
5143         }
5144         
5145         this.el.on('click', this.onClick, this);
5146         
5147         if(this.badge !== ''){
5148             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5149         }
5150         
5151     },
5152     
5153     onClick : function(e)
5154     {
5155         if(this.disabled){
5156             e.preventDefault();
5157             return;
5158         }
5159         
5160         if(this.preventDefault){
5161             e.preventDefault();
5162         }
5163         
5164         this.fireEvent('click', this, e);
5165     },
5166     
5167     disable : function()
5168     {
5169         this.setDisabled(true);
5170     },
5171     
5172     enable : function()
5173     {
5174         this.setDisabled(false);
5175     },
5176     
5177     setDisabled : function(state)
5178     {
5179         if(this.disabled == state){
5180             return;
5181         }
5182         
5183         this.disabled = state;
5184         
5185         if (state) {
5186             this.el.addClass('disabled');
5187             return;
5188         }
5189         
5190         this.el.removeClass('disabled');
5191         
5192         return;
5193     },
5194     
5195     setActive : function(state)
5196     {
5197         if(this.active == state){
5198             return;
5199         }
5200         
5201         this.active = state;
5202         
5203         if (state) {
5204             this.el.addClass('active');
5205             return;
5206         }
5207         
5208         this.el.removeClass('active');
5209         
5210         return;
5211     },
5212     
5213     isActive: function () 
5214     {
5215         return this.active;
5216     },
5217     
5218     setBadge : function(str)
5219     {
5220         if(!this.badgeEl){
5221             return;
5222         }
5223         
5224         this.badgeEl.dom.innerHTML = str;
5225     }
5226     
5227    
5228      
5229  
5230 });
5231  
5232
5233  /*
5234  * - LGPL
5235  *
5236  * row
5237  * 
5238  */
5239
5240 /**
5241  * @class Roo.bootstrap.Row
5242  * @extends Roo.bootstrap.Component
5243  * Bootstrap Row class (contains columns...)
5244  * 
5245  * @constructor
5246  * Create a new Row
5247  * @param {Object} config The config object
5248  */
5249
5250 Roo.bootstrap.Row = function(config){
5251     Roo.bootstrap.Row.superclass.constructor.call(this, config);
5252 };
5253
5254 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
5255     
5256     getAutoCreate : function(){
5257        return {
5258             cls: 'row clearfix'
5259        };
5260     }
5261     
5262     
5263 });
5264
5265  
5266
5267  /*
5268  * - LGPL
5269  *
5270  * element
5271  * 
5272  */
5273
5274 /**
5275  * @class Roo.bootstrap.Element
5276  * @extends Roo.bootstrap.Component
5277  * Bootstrap Element class
5278  * @cfg {String} html contents of the element
5279  * @cfg {String} tag tag of the element
5280  * @cfg {String} cls class of the element
5281  * @cfg {Boolean} preventDefault (true|false) default false
5282  * @cfg {Boolean} clickable (true|false) default false
5283  * 
5284  * @constructor
5285  * Create a new Element
5286  * @param {Object} config The config object
5287  */
5288
5289 Roo.bootstrap.Element = function(config){
5290     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5291     
5292     this.addEvents({
5293         // raw events
5294         /**
5295          * @event click
5296          * When a element is chick
5297          * @param {Roo.bootstrap.Element} this
5298          * @param {Roo.EventObject} e
5299          */
5300         "click" : true
5301     });
5302 };
5303
5304 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5305     
5306     tag: 'div',
5307     cls: '',
5308     html: '',
5309     preventDefault: false, 
5310     clickable: false,
5311     
5312     getAutoCreate : function(){
5313         
5314         var cfg = {
5315             tag: this.tag,
5316             // cls: this.cls, double assign in parent class Component.js :: onRender
5317             html: this.html
5318         };
5319         
5320         return cfg;
5321     },
5322     
5323     initEvents: function() 
5324     {
5325         Roo.bootstrap.Element.superclass.initEvents.call(this);
5326         
5327         if(this.clickable){
5328             this.el.on('click', this.onClick, this);
5329         }
5330         
5331     },
5332     
5333     onClick : function(e)
5334     {
5335         if(this.preventDefault){
5336             e.preventDefault();
5337         }
5338         
5339         this.fireEvent('click', this, e);
5340     },
5341     
5342     getValue : function()
5343     {
5344         return this.el.dom.innerHTML;
5345     },
5346     
5347     setValue : function(value)
5348     {
5349         this.el.dom.innerHTML = value;
5350     }
5351    
5352 });
5353
5354  
5355
5356  /*
5357  * - LGPL
5358  *
5359  * pagination
5360  * 
5361  */
5362
5363 /**
5364  * @class Roo.bootstrap.Pagination
5365  * @extends Roo.bootstrap.Component
5366  * Bootstrap Pagination class
5367  * @cfg {String} size xs | sm | md | lg
5368  * @cfg {Boolean} inverse false | true
5369  * 
5370  * @constructor
5371  * Create a new Pagination
5372  * @param {Object} config The config object
5373  */
5374
5375 Roo.bootstrap.Pagination = function(config){
5376     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5377 };
5378
5379 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5380     
5381     cls: false,
5382     size: false,
5383     inverse: false,
5384     
5385     getAutoCreate : function(){
5386         var cfg = {
5387             tag: 'ul',
5388                 cls: 'pagination'
5389         };
5390         if (this.inverse) {
5391             cfg.cls += ' inverse';
5392         }
5393         if (this.html) {
5394             cfg.html=this.html;
5395         }
5396         if (this.cls) {
5397             cfg.cls += " " + this.cls;
5398         }
5399         return cfg;
5400     }
5401    
5402 });
5403
5404  
5405
5406  /*
5407  * - LGPL
5408  *
5409  * Pagination item
5410  * 
5411  */
5412
5413
5414 /**
5415  * @class Roo.bootstrap.PaginationItem
5416  * @extends Roo.bootstrap.Component
5417  * Bootstrap PaginationItem class
5418  * @cfg {String} html text
5419  * @cfg {String} href the link
5420  * @cfg {Boolean} preventDefault (true | false) default true
5421  * @cfg {Boolean} active (true | false) default false
5422  * @cfg {Boolean} disabled default false
5423  * 
5424  * 
5425  * @constructor
5426  * Create a new PaginationItem
5427  * @param {Object} config The config object
5428  */
5429
5430
5431 Roo.bootstrap.PaginationItem = function(config){
5432     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5433     this.addEvents({
5434         // raw events
5435         /**
5436          * @event click
5437          * The raw click event for the entire grid.
5438          * @param {Roo.EventObject} e
5439          */
5440         "click" : true
5441     });
5442 };
5443
5444 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5445     
5446     href : false,
5447     html : false,
5448     preventDefault: true,
5449     active : false,
5450     cls : false,
5451     disabled: false,
5452     
5453     getAutoCreate : function(){
5454         var cfg= {
5455             tag: 'li',
5456             cn: [
5457                 {
5458                     tag : 'a',
5459                     href : this.href ? this.href : '#',
5460                     html : this.html ? this.html : ''
5461                 }
5462             ]
5463         };
5464         
5465         if(this.cls){
5466             cfg.cls = this.cls;
5467         }
5468         
5469         if(this.disabled){
5470             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5471         }
5472         
5473         if(this.active){
5474             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5475         }
5476         
5477         return cfg;
5478     },
5479     
5480     initEvents: function() {
5481         
5482         this.el.on('click', this.onClick, this);
5483         
5484     },
5485     onClick : function(e)
5486     {
5487         Roo.log('PaginationItem on click ');
5488         if(this.preventDefault){
5489             e.preventDefault();
5490         }
5491         
5492         if(this.disabled){
5493             return;
5494         }
5495         
5496         this.fireEvent('click', this, e);
5497     }
5498    
5499 });
5500
5501  
5502
5503  /*
5504  * - LGPL
5505  *
5506  * slider
5507  * 
5508  */
5509
5510
5511 /**
5512  * @class Roo.bootstrap.Slider
5513  * @extends Roo.bootstrap.Component
5514  * Bootstrap Slider class
5515  *    
5516  * @constructor
5517  * Create a new Slider
5518  * @param {Object} config The config object
5519  */
5520
5521 Roo.bootstrap.Slider = function(config){
5522     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5523 };
5524
5525 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5526     
5527     getAutoCreate : function(){
5528         
5529         var cfg = {
5530             tag: 'div',
5531             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5532             cn: [
5533                 {
5534                     tag: 'a',
5535                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5536                 }
5537             ]
5538         };
5539         
5540         return cfg;
5541     }
5542    
5543 });
5544
5545  /*
5546  * Based on:
5547  * Ext JS Library 1.1.1
5548  * Copyright(c) 2006-2007, Ext JS, LLC.
5549  *
5550  * Originally Released Under LGPL - original licence link has changed is not relivant.
5551  *
5552  * Fork - LGPL
5553  * <script type="text/javascript">
5554  */
5555  
5556
5557 /**
5558  * @class Roo.grid.ColumnModel
5559  * @extends Roo.util.Observable
5560  * This is the default implementation of a ColumnModel used by the Grid. It defines
5561  * the columns in the grid.
5562  * <br>Usage:<br>
5563  <pre><code>
5564  var colModel = new Roo.grid.ColumnModel([
5565         {header: "Ticker", width: 60, sortable: true, locked: true},
5566         {header: "Company Name", width: 150, sortable: true},
5567         {header: "Market Cap.", width: 100, sortable: true},
5568         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5569         {header: "Employees", width: 100, sortable: true, resizable: false}
5570  ]);
5571  </code></pre>
5572  * <p>
5573  
5574  * The config options listed for this class are options which may appear in each
5575  * individual column definition.
5576  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5577  * @constructor
5578  * @param {Object} config An Array of column config objects. See this class's
5579  * config objects for details.
5580 */
5581 Roo.grid.ColumnModel = function(config){
5582         /**
5583      * The config passed into the constructor
5584      */
5585     this.config = config;
5586     this.lookup = {};
5587
5588     // if no id, create one
5589     // if the column does not have a dataIndex mapping,
5590     // map it to the order it is in the config
5591     for(var i = 0, len = config.length; i < len; i++){
5592         var c = config[i];
5593         if(typeof c.dataIndex == "undefined"){
5594             c.dataIndex = i;
5595         }
5596         if(typeof c.renderer == "string"){
5597             c.renderer = Roo.util.Format[c.renderer];
5598         }
5599         if(typeof c.id == "undefined"){
5600             c.id = Roo.id();
5601         }
5602         if(c.editor && c.editor.xtype){
5603             c.editor  = Roo.factory(c.editor, Roo.grid);
5604         }
5605         if(c.editor && c.editor.isFormField){
5606             c.editor = new Roo.grid.GridEditor(c.editor);
5607         }
5608         this.lookup[c.id] = c;
5609     }
5610
5611     /**
5612      * The width of columns which have no width specified (defaults to 100)
5613      * @type Number
5614      */
5615     this.defaultWidth = 100;
5616
5617     /**
5618      * Default sortable of columns which have no sortable specified (defaults to false)
5619      * @type Boolean
5620      */
5621     this.defaultSortable = false;
5622
5623     this.addEvents({
5624         /**
5625              * @event widthchange
5626              * Fires when the width of a column changes.
5627              * @param {ColumnModel} this
5628              * @param {Number} columnIndex The column index
5629              * @param {Number} newWidth The new width
5630              */
5631             "widthchange": true,
5632         /**
5633              * @event headerchange
5634              * Fires when the text of a header changes.
5635              * @param {ColumnModel} this
5636              * @param {Number} columnIndex The column index
5637              * @param {Number} newText The new header text
5638              */
5639             "headerchange": true,
5640         /**
5641              * @event hiddenchange
5642              * Fires when a column is hidden or "unhidden".
5643              * @param {ColumnModel} this
5644              * @param {Number} columnIndex The column index
5645              * @param {Boolean} hidden true if hidden, false otherwise
5646              */
5647             "hiddenchange": true,
5648             /**
5649          * @event columnmoved
5650          * Fires when a column is moved.
5651          * @param {ColumnModel} this
5652          * @param {Number} oldIndex
5653          * @param {Number} newIndex
5654          */
5655         "columnmoved" : true,
5656         /**
5657          * @event columlockchange
5658          * Fires when a column's locked state is changed
5659          * @param {ColumnModel} this
5660          * @param {Number} colIndex
5661          * @param {Boolean} locked true if locked
5662          */
5663         "columnlockchange" : true
5664     });
5665     Roo.grid.ColumnModel.superclass.constructor.call(this);
5666 };
5667 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5668     /**
5669      * @cfg {String} header The header text to display in the Grid view.
5670      */
5671     /**
5672      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5673      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5674      * specified, the column's index is used as an index into the Record's data Array.
5675      */
5676     /**
5677      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5678      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5679      */
5680     /**
5681      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5682      * Defaults to the value of the {@link #defaultSortable} property.
5683      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5684      */
5685     /**
5686      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5687      */
5688     /**
5689      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5690      */
5691     /**
5692      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5693      */
5694     /**
5695      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5696      */
5697     /**
5698      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5699      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5700      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5701      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5702      */
5703        /**
5704      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5705      */
5706     /**
5707      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5708      */
5709     /**
5710      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5711      */
5712     /**
5713      * @cfg {String} cursor (Optional)
5714      */
5715     /**
5716      * @cfg {String} tooltip (Optional)
5717      */
5718     /**
5719      * @cfg {Number} xs (Optional)
5720      */
5721     /**
5722      * @cfg {Number} sm (Optional)
5723      */
5724     /**
5725      * @cfg {Number} md (Optional)
5726      */
5727     /**
5728      * @cfg {Number} lg (Optional)
5729      */
5730     /**
5731      * Returns the id of the column at the specified index.
5732      * @param {Number} index The column index
5733      * @return {String} the id
5734      */
5735     getColumnId : function(index){
5736         return this.config[index].id;
5737     },
5738
5739     /**
5740      * Returns the column for a specified id.
5741      * @param {String} id The column id
5742      * @return {Object} the column
5743      */
5744     getColumnById : function(id){
5745         return this.lookup[id];
5746     },
5747
5748     
5749     /**
5750      * Returns the column for a specified dataIndex.
5751      * @param {String} dataIndex The column dataIndex
5752      * @return {Object|Boolean} the column or false if not found
5753      */
5754     getColumnByDataIndex: function(dataIndex){
5755         var index = this.findColumnIndex(dataIndex);
5756         return index > -1 ? this.config[index] : false;
5757     },
5758     
5759     /**
5760      * Returns the index for a specified column id.
5761      * @param {String} id The column id
5762      * @return {Number} the index, or -1 if not found
5763      */
5764     getIndexById : function(id){
5765         for(var i = 0, len = this.config.length; i < len; i++){
5766             if(this.config[i].id == id){
5767                 return i;
5768             }
5769         }
5770         return -1;
5771     },
5772     
5773     /**
5774      * Returns the index for a specified column dataIndex.
5775      * @param {String} dataIndex The column dataIndex
5776      * @return {Number} the index, or -1 if not found
5777      */
5778     
5779     findColumnIndex : function(dataIndex){
5780         for(var i = 0, len = this.config.length; i < len; i++){
5781             if(this.config[i].dataIndex == dataIndex){
5782                 return i;
5783             }
5784         }
5785         return -1;
5786     },
5787     
5788     
5789     moveColumn : function(oldIndex, newIndex){
5790         var c = this.config[oldIndex];
5791         this.config.splice(oldIndex, 1);
5792         this.config.splice(newIndex, 0, c);
5793         this.dataMap = null;
5794         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5795     },
5796
5797     isLocked : function(colIndex){
5798         return this.config[colIndex].locked === true;
5799     },
5800
5801     setLocked : function(colIndex, value, suppressEvent){
5802         if(this.isLocked(colIndex) == value){
5803             return;
5804         }
5805         this.config[colIndex].locked = value;
5806         if(!suppressEvent){
5807             this.fireEvent("columnlockchange", this, colIndex, value);
5808         }
5809     },
5810
5811     getTotalLockedWidth : function(){
5812         var totalWidth = 0;
5813         for(var i = 0; i < this.config.length; i++){
5814             if(this.isLocked(i) && !this.isHidden(i)){
5815                 this.totalWidth += this.getColumnWidth(i);
5816             }
5817         }
5818         return totalWidth;
5819     },
5820
5821     getLockedCount : function(){
5822         for(var i = 0, len = this.config.length; i < len; i++){
5823             if(!this.isLocked(i)){
5824                 return i;
5825             }
5826         }
5827         
5828         return this.config.length;
5829     },
5830
5831     /**
5832      * Returns the number of columns.
5833      * @return {Number}
5834      */
5835     getColumnCount : function(visibleOnly){
5836         if(visibleOnly === true){
5837             var c = 0;
5838             for(var i = 0, len = this.config.length; i < len; i++){
5839                 if(!this.isHidden(i)){
5840                     c++;
5841                 }
5842             }
5843             return c;
5844         }
5845         return this.config.length;
5846     },
5847
5848     /**
5849      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5850      * @param {Function} fn
5851      * @param {Object} scope (optional)
5852      * @return {Array} result
5853      */
5854     getColumnsBy : function(fn, scope){
5855         var r = [];
5856         for(var i = 0, len = this.config.length; i < len; i++){
5857             var c = this.config[i];
5858             if(fn.call(scope||this, c, i) === true){
5859                 r[r.length] = c;
5860             }
5861         }
5862         return r;
5863     },
5864
5865     /**
5866      * Returns true if the specified column is sortable.
5867      * @param {Number} col The column index
5868      * @return {Boolean}
5869      */
5870     isSortable : function(col){
5871         if(typeof this.config[col].sortable == "undefined"){
5872             return this.defaultSortable;
5873         }
5874         return this.config[col].sortable;
5875     },
5876
5877     /**
5878      * Returns the rendering (formatting) function defined for the column.
5879      * @param {Number} col The column index.
5880      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5881      */
5882     getRenderer : function(col){
5883         if(!this.config[col].renderer){
5884             return Roo.grid.ColumnModel.defaultRenderer;
5885         }
5886         return this.config[col].renderer;
5887     },
5888
5889     /**
5890      * Sets the rendering (formatting) function for a column.
5891      * @param {Number} col The column index
5892      * @param {Function} fn The function to use to process the cell's raw data
5893      * to return HTML markup for the grid view. The render function is called with
5894      * the following parameters:<ul>
5895      * <li>Data value.</li>
5896      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5897      * <li>css A CSS style string to apply to the table cell.</li>
5898      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5899      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5900      * <li>Row index</li>
5901      * <li>Column index</li>
5902      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5903      */
5904     setRenderer : function(col, fn){
5905         this.config[col].renderer = fn;
5906     },
5907
5908     /**
5909      * Returns the width for the specified column.
5910      * @param {Number} col The column index
5911      * @return {Number}
5912      */
5913     getColumnWidth : function(col){
5914         return this.config[col].width * 1 || this.defaultWidth;
5915     },
5916
5917     /**
5918      * Sets the width for a column.
5919      * @param {Number} col The column index
5920      * @param {Number} width The new width
5921      */
5922     setColumnWidth : function(col, width, suppressEvent){
5923         this.config[col].width = width;
5924         this.totalWidth = null;
5925         if(!suppressEvent){
5926              this.fireEvent("widthchange", this, col, width);
5927         }
5928     },
5929
5930     /**
5931      * Returns the total width of all columns.
5932      * @param {Boolean} includeHidden True to include hidden column widths
5933      * @return {Number}
5934      */
5935     getTotalWidth : function(includeHidden){
5936         if(!this.totalWidth){
5937             this.totalWidth = 0;
5938             for(var i = 0, len = this.config.length; i < len; i++){
5939                 if(includeHidden || !this.isHidden(i)){
5940                     this.totalWidth += this.getColumnWidth(i);
5941                 }
5942             }
5943         }
5944         return this.totalWidth;
5945     },
5946
5947     /**
5948      * Returns the header for the specified column.
5949      * @param {Number} col The column index
5950      * @return {String}
5951      */
5952     getColumnHeader : function(col){
5953         return this.config[col].header;
5954     },
5955
5956     /**
5957      * Sets the header for a column.
5958      * @param {Number} col The column index
5959      * @param {String} header The new header
5960      */
5961     setColumnHeader : function(col, header){
5962         this.config[col].header = header;
5963         this.fireEvent("headerchange", this, col, header);
5964     },
5965
5966     /**
5967      * Returns the tooltip for the specified column.
5968      * @param {Number} col The column index
5969      * @return {String}
5970      */
5971     getColumnTooltip : function(col){
5972             return this.config[col].tooltip;
5973     },
5974     /**
5975      * Sets the tooltip for a column.
5976      * @param {Number} col The column index
5977      * @param {String} tooltip The new tooltip
5978      */
5979     setColumnTooltip : function(col, tooltip){
5980             this.config[col].tooltip = tooltip;
5981     },
5982
5983     /**
5984      * Returns the dataIndex for the specified column.
5985      * @param {Number} col The column index
5986      * @return {Number}
5987      */
5988     getDataIndex : function(col){
5989         return this.config[col].dataIndex;
5990     },
5991
5992     /**
5993      * Sets the dataIndex for a column.
5994      * @param {Number} col The column index
5995      * @param {Number} dataIndex The new dataIndex
5996      */
5997     setDataIndex : function(col, dataIndex){
5998         this.config[col].dataIndex = dataIndex;
5999     },
6000
6001     
6002     
6003     /**
6004      * Returns true if the cell is editable.
6005      * @param {Number} colIndex The column index
6006      * @param {Number} rowIndex The row index - this is nto actually used..?
6007      * @return {Boolean}
6008      */
6009     isCellEditable : function(colIndex, rowIndex){
6010         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
6011     },
6012
6013     /**
6014      * Returns the editor defined for the cell/column.
6015      * return false or null to disable editing.
6016      * @param {Number} colIndex The column index
6017      * @param {Number} rowIndex The row index
6018      * @return {Object}
6019      */
6020     getCellEditor : function(colIndex, rowIndex){
6021         return this.config[colIndex].editor;
6022     },
6023
6024     /**
6025      * Sets if a column is editable.
6026      * @param {Number} col The column index
6027      * @param {Boolean} editable True if the column is editable
6028      */
6029     setEditable : function(col, editable){
6030         this.config[col].editable = editable;
6031     },
6032
6033
6034     /**
6035      * Returns true if the column is hidden.
6036      * @param {Number} colIndex The column index
6037      * @return {Boolean}
6038      */
6039     isHidden : function(colIndex){
6040         return this.config[colIndex].hidden;
6041     },
6042
6043
6044     /**
6045      * Returns true if the column width cannot be changed
6046      */
6047     isFixed : function(colIndex){
6048         return this.config[colIndex].fixed;
6049     },
6050
6051     /**
6052      * Returns true if the column can be resized
6053      * @return {Boolean}
6054      */
6055     isResizable : function(colIndex){
6056         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
6057     },
6058     /**
6059      * Sets if a column is hidden.
6060      * @param {Number} colIndex The column index
6061      * @param {Boolean} hidden True if the column is hidden
6062      */
6063     setHidden : function(colIndex, hidden){
6064         this.config[colIndex].hidden = hidden;
6065         this.totalWidth = null;
6066         this.fireEvent("hiddenchange", this, colIndex, hidden);
6067     },
6068
6069     /**
6070      * Sets the editor for a column.
6071      * @param {Number} col The column index
6072      * @param {Object} editor The editor object
6073      */
6074     setEditor : function(col, editor){
6075         this.config[col].editor = editor;
6076     }
6077 });
6078
6079 Roo.grid.ColumnModel.defaultRenderer = function(value)
6080 {
6081     if(typeof value == "object") {
6082         return value;
6083     }
6084         if(typeof value == "string" && value.length < 1){
6085             return "&#160;";
6086         }
6087     
6088         return String.format("{0}", value);
6089 };
6090
6091 // Alias for backwards compatibility
6092 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
6093 /*
6094  * Based on:
6095  * Ext JS Library 1.1.1
6096  * Copyright(c) 2006-2007, Ext JS, LLC.
6097  *
6098  * Originally Released Under LGPL - original licence link has changed is not relivant.
6099  *
6100  * Fork - LGPL
6101  * <script type="text/javascript">
6102  */
6103  
6104 /**
6105  * @class Roo.LoadMask
6106  * A simple utility class for generically masking elements while loading data.  If the element being masked has
6107  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
6108  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
6109  * element's UpdateManager load indicator and will be destroyed after the initial load.
6110  * @constructor
6111  * Create a new LoadMask
6112  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
6113  * @param {Object} config The config object
6114  */
6115 Roo.LoadMask = function(el, config){
6116     this.el = Roo.get(el);
6117     Roo.apply(this, config);
6118     if(this.store){
6119         this.store.on('beforeload', this.onBeforeLoad, this);
6120         this.store.on('load', this.onLoad, this);
6121         this.store.on('loadexception', this.onLoadException, this);
6122         this.removeMask = false;
6123     }else{
6124         var um = this.el.getUpdateManager();
6125         um.showLoadIndicator = false; // disable the default indicator
6126         um.on('beforeupdate', this.onBeforeLoad, this);
6127         um.on('update', this.onLoad, this);
6128         um.on('failure', this.onLoad, this);
6129         this.removeMask = true;
6130     }
6131 };
6132
6133 Roo.LoadMask.prototype = {
6134     /**
6135      * @cfg {Boolean} removeMask
6136      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6137      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
6138      */
6139     /**
6140      * @cfg {String} msg
6141      * The text to display in a centered loading message box (defaults to 'Loading...')
6142      */
6143     msg : 'Loading...',
6144     /**
6145      * @cfg {String} msgCls
6146      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6147      */
6148     msgCls : 'x-mask-loading',
6149
6150     /**
6151      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6152      * @type Boolean
6153      */
6154     disabled: false,
6155
6156     /**
6157      * Disables the mask to prevent it from being displayed
6158      */
6159     disable : function(){
6160        this.disabled = true;
6161     },
6162
6163     /**
6164      * Enables the mask so that it can be displayed
6165      */
6166     enable : function(){
6167         this.disabled = false;
6168     },
6169     
6170     onLoadException : function()
6171     {
6172         Roo.log(arguments);
6173         
6174         if (typeof(arguments[3]) != 'undefined') {
6175             Roo.MessageBox.alert("Error loading",arguments[3]);
6176         } 
6177         /*
6178         try {
6179             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6180                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6181             }   
6182         } catch(e) {
6183             
6184         }
6185         */
6186     
6187         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6188     },
6189     // private
6190     onLoad : function()
6191     {
6192         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6193     },
6194
6195     // private
6196     onBeforeLoad : function(){
6197         if(!this.disabled){
6198             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6199         }
6200     },
6201
6202     // private
6203     destroy : function(){
6204         if(this.store){
6205             this.store.un('beforeload', this.onBeforeLoad, this);
6206             this.store.un('load', this.onLoad, this);
6207             this.store.un('loadexception', this.onLoadException, this);
6208         }else{
6209             var um = this.el.getUpdateManager();
6210             um.un('beforeupdate', this.onBeforeLoad, this);
6211             um.un('update', this.onLoad, this);
6212             um.un('failure', this.onLoad, this);
6213         }
6214     }
6215 };/*
6216  * - LGPL
6217  *
6218  * table
6219  * 
6220  */
6221
6222 /**
6223  * @class Roo.bootstrap.Table
6224  * @extends Roo.bootstrap.Component
6225  * Bootstrap Table class
6226  * @cfg {String} cls table class
6227  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6228  * @cfg {String} bgcolor Specifies the background color for a table
6229  * @cfg {Number} border Specifies whether the table cells should have borders or not
6230  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6231  * @cfg {Number} cellspacing Specifies the space between cells
6232  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6233  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6234  * @cfg {String} sortable Specifies that the table should be sortable
6235  * @cfg {String} summary Specifies a summary of the content of a table
6236  * @cfg {Number} width Specifies the width of a table
6237  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6238  * 
6239  * @cfg {boolean} striped Should the rows be alternative striped
6240  * @cfg {boolean} bordered Add borders to the table
6241  * @cfg {boolean} hover Add hover highlighting
6242  * @cfg {boolean} condensed Format condensed
6243  * @cfg {boolean} responsive Format condensed
6244  * @cfg {Boolean} loadMask (true|false) default false
6245  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6246  * @cfg {Boolean} headerShow (true|false) generate thead, default true
6247  * @cfg {Boolean} rowSelection (true|false) default false
6248  * @cfg {Boolean} cellSelection (true|false) default false
6249  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6250  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
6251  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
6252  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
6253  
6254  * 
6255  * @constructor
6256  * Create a new Table
6257  * @param {Object} config The config object
6258  */
6259
6260 Roo.bootstrap.Table = function(config){
6261     Roo.bootstrap.Table.superclass.constructor.call(this, config);
6262     
6263   
6264     
6265     // BC...
6266     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6267     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6268     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6269     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6270     
6271     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6272     if (this.sm) {
6273         this.sm.grid = this;
6274         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6275         this.sm = this.selModel;
6276         this.sm.xmodule = this.xmodule || false;
6277     }
6278     
6279     if (this.cm && typeof(this.cm.config) == 'undefined') {
6280         this.colModel = new Roo.grid.ColumnModel(this.cm);
6281         this.cm = this.colModel;
6282         this.cm.xmodule = this.xmodule || false;
6283     }
6284     if (this.store) {
6285         this.store= Roo.factory(this.store, Roo.data);
6286         this.ds = this.store;
6287         this.ds.xmodule = this.xmodule || false;
6288          
6289     }
6290     if (this.footer && this.store) {
6291         this.footer.dataSource = this.ds;
6292         this.footer = Roo.factory(this.footer);
6293     }
6294     
6295     /** @private */
6296     this.addEvents({
6297         /**
6298          * @event cellclick
6299          * Fires when a cell is clicked
6300          * @param {Roo.bootstrap.Table} this
6301          * @param {Roo.Element} el
6302          * @param {Number} rowIndex
6303          * @param {Number} columnIndex
6304          * @param {Roo.EventObject} e
6305          */
6306         "cellclick" : true,
6307         /**
6308          * @event celldblclick
6309          * Fires when a cell is double clicked
6310          * @param {Roo.bootstrap.Table} this
6311          * @param {Roo.Element} el
6312          * @param {Number} rowIndex
6313          * @param {Number} columnIndex
6314          * @param {Roo.EventObject} e
6315          */
6316         "celldblclick" : true,
6317         /**
6318          * @event rowclick
6319          * Fires when a row is clicked
6320          * @param {Roo.bootstrap.Table} this
6321          * @param {Roo.Element} el
6322          * @param {Number} rowIndex
6323          * @param {Roo.EventObject} e
6324          */
6325         "rowclick" : true,
6326         /**
6327          * @event rowdblclick
6328          * Fires when a row is double clicked
6329          * @param {Roo.bootstrap.Table} this
6330          * @param {Roo.Element} el
6331          * @param {Number} rowIndex
6332          * @param {Roo.EventObject} e
6333          */
6334         "rowdblclick" : true,
6335         /**
6336          * @event mouseover
6337          * Fires when a mouseover occur
6338          * @param {Roo.bootstrap.Table} this
6339          * @param {Roo.Element} el
6340          * @param {Number} rowIndex
6341          * @param {Number} columnIndex
6342          * @param {Roo.EventObject} e
6343          */
6344         "mouseover" : true,
6345         /**
6346          * @event mouseout
6347          * Fires when a mouseout occur
6348          * @param {Roo.bootstrap.Table} this
6349          * @param {Roo.Element} el
6350          * @param {Number} rowIndex
6351          * @param {Number} columnIndex
6352          * @param {Roo.EventObject} e
6353          */
6354         "mouseout" : true,
6355         /**
6356          * @event rowclass
6357          * Fires when a row is rendered, so you can change add a style to it.
6358          * @param {Roo.bootstrap.Table} this
6359          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6360          */
6361         'rowclass' : true,
6362           /**
6363          * @event rowsrendered
6364          * Fires when all the  rows have been rendered
6365          * @param {Roo.bootstrap.Table} this
6366          */
6367         'rowsrendered' : true,
6368         /**
6369          * @event contextmenu
6370          * The raw contextmenu event for the entire grid.
6371          * @param {Roo.EventObject} e
6372          */
6373         "contextmenu" : true,
6374         /**
6375          * @event rowcontextmenu
6376          * Fires when a row is right clicked
6377          * @param {Roo.bootstrap.Table} this
6378          * @param {Number} rowIndex
6379          * @param {Roo.EventObject} e
6380          */
6381         "rowcontextmenu" : true,
6382         /**
6383          * @event cellcontextmenu
6384          * Fires when a cell is right clicked
6385          * @param {Roo.bootstrap.Table} this
6386          * @param {Number} rowIndex
6387          * @param {Number} cellIndex
6388          * @param {Roo.EventObject} e
6389          */
6390          "cellcontextmenu" : true,
6391          /**
6392          * @event headercontextmenu
6393          * Fires when a header is right clicked
6394          * @param {Roo.bootstrap.Table} this
6395          * @param {Number} columnIndex
6396          * @param {Roo.EventObject} e
6397          */
6398         "headercontextmenu" : true
6399     });
6400 };
6401
6402 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6403     
6404     cls: false,
6405     align: false,
6406     bgcolor: false,
6407     border: false,
6408     cellpadding: false,
6409     cellspacing: false,
6410     frame: false,
6411     rules: false,
6412     sortable: false,
6413     summary: false,
6414     width: false,
6415     striped : false,
6416     scrollBody : false,
6417     bordered: false,
6418     hover:  false,
6419     condensed : false,
6420     responsive : false,
6421     sm : false,
6422     cm : false,
6423     store : false,
6424     loadMask : false,
6425     footerShow : true,
6426     headerShow : true,
6427   
6428     rowSelection : false,
6429     cellSelection : false,
6430     layout : false,
6431     
6432     // Roo.Element - the tbody
6433     mainBody: false,
6434     // Roo.Element - thead element
6435     mainHead: false,
6436     
6437     container: false, // used by gridpanel...
6438     
6439     lazyLoad : false,
6440     
6441     CSS : Roo.util.CSS,
6442     
6443     auto_hide_footer : false,
6444     
6445     getAutoCreate : function()
6446     {
6447         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6448         
6449         cfg = {
6450             tag: 'table',
6451             cls : 'table',
6452             cn : []
6453         };
6454         if (this.scrollBody) {
6455             cfg.cls += ' table-body-fixed';
6456         }    
6457         if (this.striped) {
6458             cfg.cls += ' table-striped';
6459         }
6460         
6461         if (this.hover) {
6462             cfg.cls += ' table-hover';
6463         }
6464         if (this.bordered) {
6465             cfg.cls += ' table-bordered';
6466         }
6467         if (this.condensed) {
6468             cfg.cls += ' table-condensed';
6469         }
6470         if (this.responsive) {
6471             cfg.cls += ' table-responsive';
6472         }
6473         
6474         if (this.cls) {
6475             cfg.cls+=  ' ' +this.cls;
6476         }
6477         
6478         // this lot should be simplifed...
6479         var _t = this;
6480         var cp = [
6481             'align',
6482             'bgcolor',
6483             'border',
6484             'cellpadding',
6485             'cellspacing',
6486             'frame',
6487             'rules',
6488             'sortable',
6489             'summary',
6490             'width'
6491         ].forEach(function(k) {
6492             if (_t[k]) {
6493                 cfg[k] = _t[k];
6494             }
6495         });
6496         
6497         
6498         if (this.layout) {
6499             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6500         }
6501         
6502         if(this.store || this.cm){
6503             if(this.headerShow){
6504                 cfg.cn.push(this.renderHeader());
6505             }
6506             
6507             cfg.cn.push(this.renderBody());
6508             
6509             if(this.footerShow){
6510                 cfg.cn.push(this.renderFooter());
6511             }
6512             // where does this come from?
6513             //cfg.cls+=  ' TableGrid';
6514         }
6515         
6516         return { cn : [ cfg ] };
6517     },
6518     
6519     initEvents : function()
6520     {   
6521         if(!this.store || !this.cm){
6522             return;
6523         }
6524         if (this.selModel) {
6525             this.selModel.initEvents();
6526         }
6527         
6528         
6529         //Roo.log('initEvents with ds!!!!');
6530         
6531         this.mainBody = this.el.select('tbody', true).first();
6532         this.mainHead = this.el.select('thead', true).first();
6533         this.mainFoot = this.el.select('tfoot', true).first();
6534         
6535         
6536         
6537         var _this = this;
6538         
6539         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6540             e.on('click', _this.sort, _this);
6541         });
6542         
6543         this.mainBody.on("click", this.onClick, this);
6544         this.mainBody.on("dblclick", this.onDblClick, this);
6545         
6546         // why is this done????? = it breaks dialogs??
6547         //this.parent().el.setStyle('position', 'relative');
6548         
6549         
6550         if (this.footer) {
6551             this.footer.parentId = this.id;
6552             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6553             
6554             if(this.lazyLoad){
6555                 this.el.select('tfoot tr td').first().addClass('hide');
6556             }
6557         } 
6558         
6559         if(this.loadMask) {
6560             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6561         }
6562         
6563         this.store.on('load', this.onLoad, this);
6564         this.store.on('beforeload', this.onBeforeLoad, this);
6565         this.store.on('update', this.onUpdate, this);
6566         this.store.on('add', this.onAdd, this);
6567         this.store.on("clear", this.clear, this);
6568         
6569         this.el.on("contextmenu", this.onContextMenu, this);
6570         
6571         this.mainBody.on('scroll', this.onBodyScroll, this);
6572         
6573         this.cm.on("headerchange", this.onHeaderChange, this);
6574         
6575         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6576         
6577     },
6578     
6579     onContextMenu : function(e, t)
6580     {
6581         this.processEvent("contextmenu", e);
6582     },
6583     
6584     processEvent : function(name, e)
6585     {
6586         if (name != 'touchstart' ) {
6587             this.fireEvent(name, e);    
6588         }
6589         
6590         var t = e.getTarget();
6591         
6592         var cell = Roo.get(t);
6593         
6594         if(!cell){
6595             return;
6596         }
6597         
6598         if(cell.findParent('tfoot', false, true)){
6599             return;
6600         }
6601         
6602         if(cell.findParent('thead', false, true)){
6603             
6604             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6605                 cell = Roo.get(t).findParent('th', false, true);
6606                 if (!cell) {
6607                     Roo.log("failed to find th in thead?");
6608                     Roo.log(e.getTarget());
6609                     return;
6610                 }
6611             }
6612             
6613             var cellIndex = cell.dom.cellIndex;
6614             
6615             var ename = name == 'touchstart' ? 'click' : name;
6616             this.fireEvent("header" + ename, this, cellIndex, e);
6617             
6618             return;
6619         }
6620         
6621         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6622             cell = Roo.get(t).findParent('td', false, true);
6623             if (!cell) {
6624                 Roo.log("failed to find th in tbody?");
6625                 Roo.log(e.getTarget());
6626                 return;
6627             }
6628         }
6629         
6630         var row = cell.findParent('tr', false, true);
6631         var cellIndex = cell.dom.cellIndex;
6632         var rowIndex = row.dom.rowIndex - 1;
6633         
6634         if(row !== false){
6635             
6636             this.fireEvent("row" + name, this, rowIndex, e);
6637             
6638             if(cell !== false){
6639             
6640                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6641             }
6642         }
6643         
6644     },
6645     
6646     onMouseover : function(e, el)
6647     {
6648         var cell = Roo.get(el);
6649         
6650         if(!cell){
6651             return;
6652         }
6653         
6654         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6655             cell = cell.findParent('td', false, true);
6656         }
6657         
6658         var row = cell.findParent('tr', false, true);
6659         var cellIndex = cell.dom.cellIndex;
6660         var rowIndex = row.dom.rowIndex - 1; // start from 0
6661         
6662         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6663         
6664     },
6665     
6666     onMouseout : function(e, el)
6667     {
6668         var cell = Roo.get(el);
6669         
6670         if(!cell){
6671             return;
6672         }
6673         
6674         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6675             cell = cell.findParent('td', false, true);
6676         }
6677         
6678         var row = cell.findParent('tr', false, true);
6679         var cellIndex = cell.dom.cellIndex;
6680         var rowIndex = row.dom.rowIndex - 1; // start from 0
6681         
6682         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6683         
6684     },
6685     
6686     onClick : function(e, el)
6687     {
6688         var cell = Roo.get(el);
6689         
6690         if(!cell || (!this.cellSelection && !this.rowSelection)){
6691             return;
6692         }
6693         
6694         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6695             cell = cell.findParent('td', false, true);
6696         }
6697         
6698         if(!cell || typeof(cell) == 'undefined'){
6699             return;
6700         }
6701         
6702         var row = cell.findParent('tr', false, true);
6703         
6704         if(!row || typeof(row) == 'undefined'){
6705             return;
6706         }
6707         
6708         var cellIndex = cell.dom.cellIndex;
6709         var rowIndex = this.getRowIndex(row);
6710         
6711         // why??? - should these not be based on SelectionModel?
6712         if(this.cellSelection){
6713             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6714         }
6715         
6716         if(this.rowSelection){
6717             this.fireEvent('rowclick', this, row, rowIndex, e);
6718         }
6719         
6720         
6721     },
6722         
6723     onDblClick : function(e,el)
6724     {
6725         var cell = Roo.get(el);
6726         
6727         if(!cell || (!this.cellSelection && !this.rowSelection)){
6728             return;
6729         }
6730         
6731         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6732             cell = cell.findParent('td', false, true);
6733         }
6734         
6735         if(!cell || typeof(cell) == 'undefined'){
6736             return;
6737         }
6738         
6739         var row = cell.findParent('tr', false, true);
6740         
6741         if(!row || typeof(row) == 'undefined'){
6742             return;
6743         }
6744         
6745         var cellIndex = cell.dom.cellIndex;
6746         var rowIndex = this.getRowIndex(row);
6747         
6748         if(this.cellSelection){
6749             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6750         }
6751         
6752         if(this.rowSelection){
6753             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6754         }
6755     },
6756     
6757     sort : function(e,el)
6758     {
6759         var col = Roo.get(el);
6760         
6761         if(!col.hasClass('sortable')){
6762             return;
6763         }
6764         
6765         var sort = col.attr('sort');
6766         var dir = 'ASC';
6767         
6768         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6769             dir = 'DESC';
6770         }
6771         
6772         this.store.sortInfo = {field : sort, direction : dir};
6773         
6774         if (this.footer) {
6775             Roo.log("calling footer first");
6776             this.footer.onClick('first');
6777         } else {
6778         
6779             this.store.load({ params : { start : 0 } });
6780         }
6781     },
6782     
6783     renderHeader : function()
6784     {
6785         var header = {
6786             tag: 'thead',
6787             cn : []
6788         };
6789         
6790         var cm = this.cm;
6791         this.totalWidth = 0;
6792         
6793         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6794             
6795             var config = cm.config[i];
6796             
6797             var c = {
6798                 tag: 'th',
6799                 cls : 'x-hcol-' + i,
6800                 style : '',
6801                 html: cm.getColumnHeader(i)
6802             };
6803             
6804             var hh = '';
6805             
6806             if(typeof(config.sortable) != 'undefined' && config.sortable){
6807                 c.cls = 'sortable';
6808                 c.html = '<i class="glyphicon"></i>' + c.html;
6809             }
6810             
6811             if(typeof(config.lgHeader) != 'undefined'){
6812                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6813             }
6814             
6815             if(typeof(config.mdHeader) != 'undefined'){
6816                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6817             }
6818             
6819             if(typeof(config.smHeader) != 'undefined'){
6820                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6821             }
6822             
6823             if(typeof(config.xsHeader) != 'undefined'){
6824                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6825             }
6826             
6827             if(hh.length){
6828                 c.html = hh;
6829             }
6830             
6831             if(typeof(config.tooltip) != 'undefined'){
6832                 c.tooltip = config.tooltip;
6833             }
6834             
6835             if(typeof(config.colspan) != 'undefined'){
6836                 c.colspan = config.colspan;
6837             }
6838             
6839             if(typeof(config.hidden) != 'undefined' && config.hidden){
6840                 c.style += ' display:none;';
6841             }
6842             
6843             if(typeof(config.dataIndex) != 'undefined'){
6844                 c.sort = config.dataIndex;
6845             }
6846             
6847            
6848             
6849             if(typeof(config.align) != 'undefined' && config.align.length){
6850                 c.style += ' text-align:' + config.align + ';';
6851             }
6852             
6853             if(typeof(config.width) != 'undefined'){
6854                 c.style += ' width:' + config.width + 'px;';
6855                 this.totalWidth += config.width;
6856             } else {
6857                 this.totalWidth += 100; // assume minimum of 100 per column?
6858             }
6859             
6860             if(typeof(config.cls) != 'undefined'){
6861                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6862             }
6863             
6864             ['xs','sm','md','lg'].map(function(size){
6865                 
6866                 if(typeof(config[size]) == 'undefined'){
6867                     return;
6868                 }
6869                  
6870                 if (!config[size]) { // 0 = hidden
6871                     // BS 4 '0' is treated as hide that column and below.
6872                     c.cls += ' hidden-' + size + ' hidden' + size + 'down';
6873                     return;
6874                 }
6875                 
6876                 c.cls += ' col-' + size + '-' + config[size] + (
6877                     size == 'xs' ? (' col-' + + config[size] ) : '' // bs4 col-{num} replaces col-xs
6878                 );
6879                 
6880                 
6881             });
6882             
6883             header.cn.push(c)
6884         }
6885         
6886         return header;
6887     },
6888     
6889     renderBody : function()
6890     {
6891         var body = {
6892             tag: 'tbody',
6893             cn : [
6894                 {
6895                     tag: 'tr',
6896                     cn : [
6897                         {
6898                             tag : 'td',
6899                             colspan :  this.cm.getColumnCount()
6900                         }
6901                     ]
6902                 }
6903             ]
6904         };
6905         
6906         return body;
6907     },
6908     
6909     renderFooter : function()
6910     {
6911         var footer = {
6912             tag: 'tfoot',
6913             cn : [
6914                 {
6915                     tag: 'tr',
6916                     cn : [
6917                         {
6918                             tag : 'td',
6919                             colspan :  this.cm.getColumnCount()
6920                         }
6921                     ]
6922                 }
6923             ]
6924         };
6925         
6926         return footer;
6927     },
6928     
6929     
6930     
6931     onLoad : function()
6932     {
6933 //        Roo.log('ds onload');
6934         this.clear();
6935         
6936         var _this = this;
6937         var cm = this.cm;
6938         var ds = this.store;
6939         
6940         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6941             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6942             if (_this.store.sortInfo) {
6943                     
6944                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6945                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6946                 }
6947                 
6948                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6949                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6950                 }
6951             }
6952         });
6953         
6954         var tbody =  this.mainBody;
6955               
6956         if(ds.getCount() > 0){
6957             ds.data.each(function(d,rowIndex){
6958                 var row =  this.renderRow(cm, ds, rowIndex);
6959                 
6960                 tbody.createChild(row);
6961                 
6962                 var _this = this;
6963                 
6964                 if(row.cellObjects.length){
6965                     Roo.each(row.cellObjects, function(r){
6966                         _this.renderCellObject(r);
6967                     })
6968                 }
6969                 
6970             }, this);
6971         }
6972         
6973         var tfoot = this.el.select('tfoot', true).first();
6974         
6975         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6976             
6977             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6978             
6979             var total = this.ds.getTotalCount();
6980             
6981             if(this.footer.pageSize < total){
6982                 this.mainFoot.show();
6983             }
6984         }
6985         
6986         Roo.each(this.el.select('tbody td', true).elements, function(e){
6987             e.on('mouseover', _this.onMouseover, _this);
6988         });
6989         
6990         Roo.each(this.el.select('tbody td', true).elements, function(e){
6991             e.on('mouseout', _this.onMouseout, _this);
6992         });
6993         this.fireEvent('rowsrendered', this);
6994         
6995         this.autoSize();
6996     },
6997     
6998     
6999     onUpdate : function(ds,record)
7000     {
7001         this.refreshRow(record);
7002         this.autoSize();
7003     },
7004     
7005     onRemove : function(ds, record, index, isUpdate){
7006         if(isUpdate !== true){
7007             this.fireEvent("beforerowremoved", this, index, record);
7008         }
7009         var bt = this.mainBody.dom;
7010         
7011         var rows = this.el.select('tbody > tr', true).elements;
7012         
7013         if(typeof(rows[index]) != 'undefined'){
7014             bt.removeChild(rows[index].dom);
7015         }
7016         
7017 //        if(bt.rows[index]){
7018 //            bt.removeChild(bt.rows[index]);
7019 //        }
7020         
7021         if(isUpdate !== true){
7022             //this.stripeRows(index);
7023             //this.syncRowHeights(index, index);
7024             //this.layout();
7025             this.fireEvent("rowremoved", this, index, record);
7026         }
7027     },
7028     
7029     onAdd : function(ds, records, rowIndex)
7030     {
7031         //Roo.log('on Add called');
7032         // - note this does not handle multiple adding very well..
7033         var bt = this.mainBody.dom;
7034         for (var i =0 ; i < records.length;i++) {
7035             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
7036             //Roo.log(records[i]);
7037             //Roo.log(this.store.getAt(rowIndex+i));
7038             this.insertRow(this.store, rowIndex + i, false);
7039             return;
7040         }
7041         
7042     },
7043     
7044     
7045     refreshRow : function(record){
7046         var ds = this.store, index;
7047         if(typeof record == 'number'){
7048             index = record;
7049             record = ds.getAt(index);
7050         }else{
7051             index = ds.indexOf(record);
7052         }
7053         this.insertRow(ds, index, true);
7054         this.autoSize();
7055         this.onRemove(ds, record, index+1, true);
7056         this.autoSize();
7057         //this.syncRowHeights(index, index);
7058         //this.layout();
7059         this.fireEvent("rowupdated", this, index, record);
7060     },
7061     
7062     insertRow : function(dm, rowIndex, isUpdate){
7063         
7064         if(!isUpdate){
7065             this.fireEvent("beforerowsinserted", this, rowIndex);
7066         }
7067             //var s = this.getScrollState();
7068         var row = this.renderRow(this.cm, this.store, rowIndex);
7069         // insert before rowIndex..
7070         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
7071         
7072         var _this = this;
7073                 
7074         if(row.cellObjects.length){
7075             Roo.each(row.cellObjects, function(r){
7076                 _this.renderCellObject(r);
7077             })
7078         }
7079             
7080         if(!isUpdate){
7081             this.fireEvent("rowsinserted", this, rowIndex);
7082             //this.syncRowHeights(firstRow, lastRow);
7083             //this.stripeRows(firstRow);
7084             //this.layout();
7085         }
7086         
7087     },
7088     
7089     
7090     getRowDom : function(rowIndex)
7091     {
7092         var rows = this.el.select('tbody > tr', true).elements;
7093         
7094         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
7095         
7096     },
7097     // returns the object tree for a tr..
7098   
7099     
7100     renderRow : function(cm, ds, rowIndex) 
7101     {
7102         var d = ds.getAt(rowIndex);
7103         
7104         var row = {
7105             tag : 'tr',
7106             cls : 'x-row-' + rowIndex,
7107             cn : []
7108         };
7109             
7110         var cellObjects = [];
7111         
7112         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7113             var config = cm.config[i];
7114             
7115             var renderer = cm.getRenderer(i);
7116             var value = '';
7117             var id = false;
7118             
7119             if(typeof(renderer) !== 'undefined'){
7120                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
7121             }
7122             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
7123             // and are rendered into the cells after the row is rendered - using the id for the element.
7124             
7125             if(typeof(value) === 'object'){
7126                 id = Roo.id();
7127                 cellObjects.push({
7128                     container : id,
7129                     cfg : value 
7130                 })
7131             }
7132             
7133             var rowcfg = {
7134                 record: d,
7135                 rowIndex : rowIndex,
7136                 colIndex : i,
7137                 rowClass : ''
7138             };
7139
7140             this.fireEvent('rowclass', this, rowcfg);
7141             
7142             var td = {
7143                 tag: 'td',
7144                 cls : rowcfg.rowClass + ' x-col-' + i,
7145                 style: '',
7146                 html: (typeof(value) === 'object') ? '' : value
7147             };
7148             
7149             if (id) {
7150                 td.id = id;
7151             }
7152             
7153             if(typeof(config.colspan) != 'undefined'){
7154                 td.colspan = config.colspan;
7155             }
7156             
7157             if(typeof(config.hidden) != 'undefined' && config.hidden){
7158                 td.style += ' display:none;';
7159             }
7160             
7161             if(typeof(config.align) != 'undefined' && config.align.length){
7162                 td.style += ' text-align:' + config.align + ';';
7163             }
7164             if(typeof(config.valign) != 'undefined' && config.valign.length){
7165                 td.style += ' vertical-align:' + config.valign + ';';
7166             }
7167             
7168             if(typeof(config.width) != 'undefined'){
7169                 td.style += ' width:' +  config.width + 'px;';
7170             }
7171             
7172             if(typeof(config.cursor) != 'undefined'){
7173                 td.style += ' cursor:' +  config.cursor + ';';
7174             }
7175             
7176             if(typeof(config.cls) != 'undefined'){
7177                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7178             }
7179             
7180             ['xs','sm','md','lg'].map(function(size){
7181                 
7182                 if(typeof(config[size]) == 'undefined'){
7183                     return;
7184                 }
7185                 
7186                 if (!config[size]) { // 0 = hidden
7187                     td.cls += ' hidden-' + size;
7188                     return;
7189                 }
7190                 
7191                 td.cls += ' col-' + size + '-' + config[size];
7192
7193             });
7194             
7195             row.cn.push(td);
7196            
7197         }
7198         
7199         row.cellObjects = cellObjects;
7200         
7201         return row;
7202           
7203     },
7204     
7205     
7206     
7207     onBeforeLoad : function()
7208     {
7209         
7210     },
7211      /**
7212      * Remove all rows
7213      */
7214     clear : function()
7215     {
7216         this.el.select('tbody', true).first().dom.innerHTML = '';
7217     },
7218     /**
7219      * Show or hide a row.
7220      * @param {Number} rowIndex to show or hide
7221      * @param {Boolean} state hide
7222      */
7223     setRowVisibility : function(rowIndex, state)
7224     {
7225         var bt = this.mainBody.dom;
7226         
7227         var rows = this.el.select('tbody > tr', true).elements;
7228         
7229         if(typeof(rows[rowIndex]) == 'undefined'){
7230             return;
7231         }
7232         rows[rowIndex].dom.style.display = state ? '' : 'none';
7233     },
7234     
7235     
7236     getSelectionModel : function(){
7237         if(!this.selModel){
7238             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7239         }
7240         return this.selModel;
7241     },
7242     /*
7243      * Render the Roo.bootstrap object from renderder
7244      */
7245     renderCellObject : function(r)
7246     {
7247         var _this = this;
7248         
7249         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7250         
7251         var t = r.cfg.render(r.container);
7252         
7253         if(r.cfg.cn){
7254             Roo.each(r.cfg.cn, function(c){
7255                 var child = {
7256                     container: t.getChildContainer(),
7257                     cfg: c
7258                 };
7259                 _this.renderCellObject(child);
7260             })
7261         }
7262     },
7263     
7264     getRowIndex : function(row)
7265     {
7266         var rowIndex = -1;
7267         
7268         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7269             if(el != row){
7270                 return;
7271             }
7272             
7273             rowIndex = index;
7274         });
7275         
7276         return rowIndex;
7277     },
7278      /**
7279      * Returns the grid's underlying element = used by panel.Grid
7280      * @return {Element} The element
7281      */
7282     getGridEl : function(){
7283         return this.el;
7284     },
7285      /**
7286      * Forces a resize - used by panel.Grid
7287      * @return {Element} The element
7288      */
7289     autoSize : function()
7290     {
7291         //var ctr = Roo.get(this.container.dom.parentElement);
7292         var ctr = Roo.get(this.el.dom);
7293         
7294         var thd = this.getGridEl().select('thead',true).first();
7295         var tbd = this.getGridEl().select('tbody', true).first();
7296         var tfd = this.getGridEl().select('tfoot', true).first();
7297         
7298         var cw = ctr.getWidth();
7299         
7300         if (tbd) {
7301             
7302             tbd.setSize(ctr.getWidth(),
7303                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7304             );
7305             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7306             cw -= barsize;
7307         }
7308         cw = Math.max(cw, this.totalWidth);
7309         this.getGridEl().select('tr',true).setWidth(cw);
7310         // resize 'expandable coloumn?
7311         
7312         return; // we doe not have a view in this design..
7313         
7314     },
7315     onBodyScroll: function()
7316     {
7317         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7318         if(this.mainHead){
7319             this.mainHead.setStyle({
7320                 'position' : 'relative',
7321                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7322             });
7323         }
7324         
7325         if(this.lazyLoad){
7326             
7327             var scrollHeight = this.mainBody.dom.scrollHeight;
7328             
7329             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7330             
7331             var height = this.mainBody.getHeight();
7332             
7333             if(scrollHeight - height == scrollTop) {
7334                 
7335                 var total = this.ds.getTotalCount();
7336                 
7337                 if(this.footer.cursor + this.footer.pageSize < total){
7338                     
7339                     this.footer.ds.load({
7340                         params : {
7341                             start : this.footer.cursor + this.footer.pageSize,
7342                             limit : this.footer.pageSize
7343                         },
7344                         add : true
7345                     });
7346                 }
7347             }
7348             
7349         }
7350     },
7351     
7352     onHeaderChange : function()
7353     {
7354         var header = this.renderHeader();
7355         var table = this.el.select('table', true).first();
7356         
7357         this.mainHead.remove();
7358         this.mainHead = table.createChild(header, this.mainBody, false);
7359     },
7360     
7361     onHiddenChange : function(colModel, colIndex, hidden)
7362     {
7363         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7364         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7365         
7366         this.CSS.updateRule(thSelector, "display", "");
7367         this.CSS.updateRule(tdSelector, "display", "");
7368         
7369         if(hidden){
7370             this.CSS.updateRule(thSelector, "display", "none");
7371             this.CSS.updateRule(tdSelector, "display", "none");
7372         }
7373         
7374         this.onHeaderChange();
7375         this.onLoad();
7376     },
7377     
7378     setColumnWidth: function(col_index, width)
7379     {
7380         // width = "md-2 xs-2..."
7381         if(!this.colModel.config[col_index]) {
7382             return;
7383         }
7384         
7385         var w = width.split(" ");
7386         
7387         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7388         
7389         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7390         
7391         
7392         for(var j = 0; j < w.length; j++) {
7393             
7394             if(!w[j]) {
7395                 continue;
7396             }
7397             
7398             var size_cls = w[j].split("-");
7399             
7400             if(!Number.isInteger(size_cls[1] * 1)) {
7401                 continue;
7402             }
7403             
7404             if(!this.colModel.config[col_index][size_cls[0]]) {
7405                 continue;
7406             }
7407             
7408             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7409                 continue;
7410             }
7411             
7412             h_row[0].classList.replace(
7413                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7414                 "col-"+size_cls[0]+"-"+size_cls[1]
7415             );
7416             
7417             for(var i = 0; i < rows.length; i++) {
7418                 
7419                 var size_cls = w[j].split("-");
7420                 
7421                 if(!Number.isInteger(size_cls[1] * 1)) {
7422                     continue;
7423                 }
7424                 
7425                 if(!this.colModel.config[col_index][size_cls[0]]) {
7426                     continue;
7427                 }
7428                 
7429                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7430                     continue;
7431                 }
7432                 
7433                 rows[i].classList.replace(
7434                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7435                     "col-"+size_cls[0]+"-"+size_cls[1]
7436                 );
7437             }
7438             
7439             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7440         }
7441     }
7442 });
7443
7444  
7445
7446  /*
7447  * - LGPL
7448  *
7449  * table cell
7450  * 
7451  */
7452
7453 /**
7454  * @class Roo.bootstrap.TableCell
7455  * @extends Roo.bootstrap.Component
7456  * Bootstrap TableCell class
7457  * @cfg {String} html cell contain text
7458  * @cfg {String} cls cell class
7459  * @cfg {String} tag cell tag (td|th) default td
7460  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7461  * @cfg {String} align Aligns the content in a cell
7462  * @cfg {String} axis Categorizes cells
7463  * @cfg {String} bgcolor Specifies the background color of a cell
7464  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7465  * @cfg {Number} colspan Specifies the number of columns a cell should span
7466  * @cfg {String} headers Specifies one or more header cells a cell is related to
7467  * @cfg {Number} height Sets the height of a cell
7468  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7469  * @cfg {Number} rowspan Sets the number of rows a cell should span
7470  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7471  * @cfg {String} valign Vertical aligns the content in a cell
7472  * @cfg {Number} width Specifies the width of a cell
7473  * 
7474  * @constructor
7475  * Create a new TableCell
7476  * @param {Object} config The config object
7477  */
7478
7479 Roo.bootstrap.TableCell = function(config){
7480     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7481 };
7482
7483 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7484     
7485     html: false,
7486     cls: false,
7487     tag: false,
7488     abbr: false,
7489     align: false,
7490     axis: false,
7491     bgcolor: false,
7492     charoff: false,
7493     colspan: false,
7494     headers: false,
7495     height: false,
7496     nowrap: false,
7497     rowspan: false,
7498     scope: false,
7499     valign: false,
7500     width: false,
7501     
7502     
7503     getAutoCreate : function(){
7504         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7505         
7506         cfg = {
7507             tag: 'td'
7508         };
7509         
7510         if(this.tag){
7511             cfg.tag = this.tag;
7512         }
7513         
7514         if (this.html) {
7515             cfg.html=this.html
7516         }
7517         if (this.cls) {
7518             cfg.cls=this.cls
7519         }
7520         if (this.abbr) {
7521             cfg.abbr=this.abbr
7522         }
7523         if (this.align) {
7524             cfg.align=this.align
7525         }
7526         if (this.axis) {
7527             cfg.axis=this.axis
7528         }
7529         if (this.bgcolor) {
7530             cfg.bgcolor=this.bgcolor
7531         }
7532         if (this.charoff) {
7533             cfg.charoff=this.charoff
7534         }
7535         if (this.colspan) {
7536             cfg.colspan=this.colspan
7537         }
7538         if (this.headers) {
7539             cfg.headers=this.headers
7540         }
7541         if (this.height) {
7542             cfg.height=this.height
7543         }
7544         if (this.nowrap) {
7545             cfg.nowrap=this.nowrap
7546         }
7547         if (this.rowspan) {
7548             cfg.rowspan=this.rowspan
7549         }
7550         if (this.scope) {
7551             cfg.scope=this.scope
7552         }
7553         if (this.valign) {
7554             cfg.valign=this.valign
7555         }
7556         if (this.width) {
7557             cfg.width=this.width
7558         }
7559         
7560         
7561         return cfg;
7562     }
7563    
7564 });
7565
7566  
7567
7568  /*
7569  * - LGPL
7570  *
7571  * table row
7572  * 
7573  */
7574
7575 /**
7576  * @class Roo.bootstrap.TableRow
7577  * @extends Roo.bootstrap.Component
7578  * Bootstrap TableRow class
7579  * @cfg {String} cls row class
7580  * @cfg {String} align Aligns the content in a table row
7581  * @cfg {String} bgcolor Specifies a background color for a table row
7582  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7583  * @cfg {String} valign Vertical aligns the content in a table row
7584  * 
7585  * @constructor
7586  * Create a new TableRow
7587  * @param {Object} config The config object
7588  */
7589
7590 Roo.bootstrap.TableRow = function(config){
7591     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7592 };
7593
7594 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7595     
7596     cls: false,
7597     align: false,
7598     bgcolor: false,
7599     charoff: false,
7600     valign: false,
7601     
7602     getAutoCreate : function(){
7603         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7604         
7605         cfg = {
7606             tag: 'tr'
7607         };
7608             
7609         if(this.cls){
7610             cfg.cls = this.cls;
7611         }
7612         if(this.align){
7613             cfg.align = this.align;
7614         }
7615         if(this.bgcolor){
7616             cfg.bgcolor = this.bgcolor;
7617         }
7618         if(this.charoff){
7619             cfg.charoff = this.charoff;
7620         }
7621         if(this.valign){
7622             cfg.valign = this.valign;
7623         }
7624         
7625         return cfg;
7626     }
7627    
7628 });
7629
7630  
7631
7632  /*
7633  * - LGPL
7634  *
7635  * table body
7636  * 
7637  */
7638
7639 /**
7640  * @class Roo.bootstrap.TableBody
7641  * @extends Roo.bootstrap.Component
7642  * Bootstrap TableBody class
7643  * @cfg {String} cls element class
7644  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7645  * @cfg {String} align Aligns the content inside the element
7646  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7647  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7648  * 
7649  * @constructor
7650  * Create a new TableBody
7651  * @param {Object} config The config object
7652  */
7653
7654 Roo.bootstrap.TableBody = function(config){
7655     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7656 };
7657
7658 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7659     
7660     cls: false,
7661     tag: false,
7662     align: false,
7663     charoff: false,
7664     valign: false,
7665     
7666     getAutoCreate : function(){
7667         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7668         
7669         cfg = {
7670             tag: 'tbody'
7671         };
7672             
7673         if (this.cls) {
7674             cfg.cls=this.cls
7675         }
7676         if(this.tag){
7677             cfg.tag = this.tag;
7678         }
7679         
7680         if(this.align){
7681             cfg.align = this.align;
7682         }
7683         if(this.charoff){
7684             cfg.charoff = this.charoff;
7685         }
7686         if(this.valign){
7687             cfg.valign = this.valign;
7688         }
7689         
7690         return cfg;
7691     }
7692     
7693     
7694 //    initEvents : function()
7695 //    {
7696 //        
7697 //        if(!this.store){
7698 //            return;
7699 //        }
7700 //        
7701 //        this.store = Roo.factory(this.store, Roo.data);
7702 //        this.store.on('load', this.onLoad, this);
7703 //        
7704 //        this.store.load();
7705 //        
7706 //    },
7707 //    
7708 //    onLoad: function () 
7709 //    {   
7710 //        this.fireEvent('load', this);
7711 //    }
7712 //    
7713 //   
7714 });
7715
7716  
7717
7718  /*
7719  * Based on:
7720  * Ext JS Library 1.1.1
7721  * Copyright(c) 2006-2007, Ext JS, LLC.
7722  *
7723  * Originally Released Under LGPL - original licence link has changed is not relivant.
7724  *
7725  * Fork - LGPL
7726  * <script type="text/javascript">
7727  */
7728
7729 // as we use this in bootstrap.
7730 Roo.namespace('Roo.form');
7731  /**
7732  * @class Roo.form.Action
7733  * Internal Class used to handle form actions
7734  * @constructor
7735  * @param {Roo.form.BasicForm} el The form element or its id
7736  * @param {Object} config Configuration options
7737  */
7738
7739  
7740  
7741 // define the action interface
7742 Roo.form.Action = function(form, options){
7743     this.form = form;
7744     this.options = options || {};
7745 };
7746 /**
7747  * Client Validation Failed
7748  * @const 
7749  */
7750 Roo.form.Action.CLIENT_INVALID = 'client';
7751 /**
7752  * Server Validation Failed
7753  * @const 
7754  */
7755 Roo.form.Action.SERVER_INVALID = 'server';
7756  /**
7757  * Connect to Server Failed
7758  * @const 
7759  */
7760 Roo.form.Action.CONNECT_FAILURE = 'connect';
7761 /**
7762  * Reading Data from Server Failed
7763  * @const 
7764  */
7765 Roo.form.Action.LOAD_FAILURE = 'load';
7766
7767 Roo.form.Action.prototype = {
7768     type : 'default',
7769     failureType : undefined,
7770     response : undefined,
7771     result : undefined,
7772
7773     // interface method
7774     run : function(options){
7775
7776     },
7777
7778     // interface method
7779     success : function(response){
7780
7781     },
7782
7783     // interface method
7784     handleResponse : function(response){
7785
7786     },
7787
7788     // default connection failure
7789     failure : function(response){
7790         
7791         this.response = response;
7792         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7793         this.form.afterAction(this, false);
7794     },
7795
7796     processResponse : function(response){
7797         this.response = response;
7798         if(!response.responseText){
7799             return true;
7800         }
7801         this.result = this.handleResponse(response);
7802         return this.result;
7803     },
7804
7805     // utility functions used internally
7806     getUrl : function(appendParams){
7807         var url = this.options.url || this.form.url || this.form.el.dom.action;
7808         if(appendParams){
7809             var p = this.getParams();
7810             if(p){
7811                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7812             }
7813         }
7814         return url;
7815     },
7816
7817     getMethod : function(){
7818         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7819     },
7820
7821     getParams : function(){
7822         var bp = this.form.baseParams;
7823         var p = this.options.params;
7824         if(p){
7825             if(typeof p == "object"){
7826                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7827             }else if(typeof p == 'string' && bp){
7828                 p += '&' + Roo.urlEncode(bp);
7829             }
7830         }else if(bp){
7831             p = Roo.urlEncode(bp);
7832         }
7833         return p;
7834     },
7835
7836     createCallback : function(){
7837         return {
7838             success: this.success,
7839             failure: this.failure,
7840             scope: this,
7841             timeout: (this.form.timeout*1000),
7842             upload: this.form.fileUpload ? this.success : undefined
7843         };
7844     }
7845 };
7846
7847 Roo.form.Action.Submit = function(form, options){
7848     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7849 };
7850
7851 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7852     type : 'submit',
7853
7854     haveProgress : false,
7855     uploadComplete : false,
7856     
7857     // uploadProgress indicator.
7858     uploadProgress : function()
7859     {
7860         if (!this.form.progressUrl) {
7861             return;
7862         }
7863         
7864         if (!this.haveProgress) {
7865             Roo.MessageBox.progress("Uploading", "Uploading");
7866         }
7867         if (this.uploadComplete) {
7868            Roo.MessageBox.hide();
7869            return;
7870         }
7871         
7872         this.haveProgress = true;
7873    
7874         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7875         
7876         var c = new Roo.data.Connection();
7877         c.request({
7878             url : this.form.progressUrl,
7879             params: {
7880                 id : uid
7881             },
7882             method: 'GET',
7883             success : function(req){
7884                //console.log(data);
7885                 var rdata = false;
7886                 var edata;
7887                 try  {
7888                    rdata = Roo.decode(req.responseText)
7889                 } catch (e) {
7890                     Roo.log("Invalid data from server..");
7891                     Roo.log(edata);
7892                     return;
7893                 }
7894                 if (!rdata || !rdata.success) {
7895                     Roo.log(rdata);
7896                     Roo.MessageBox.alert(Roo.encode(rdata));
7897                     return;
7898                 }
7899                 var data = rdata.data;
7900                 
7901                 if (this.uploadComplete) {
7902                    Roo.MessageBox.hide();
7903                    return;
7904                 }
7905                    
7906                 if (data){
7907                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7908                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7909                     );
7910                 }
7911                 this.uploadProgress.defer(2000,this);
7912             },
7913        
7914             failure: function(data) {
7915                 Roo.log('progress url failed ');
7916                 Roo.log(data);
7917             },
7918             scope : this
7919         });
7920            
7921     },
7922     
7923     
7924     run : function()
7925     {
7926         // run get Values on the form, so it syncs any secondary forms.
7927         this.form.getValues();
7928         
7929         var o = this.options;
7930         var method = this.getMethod();
7931         var isPost = method == 'POST';
7932         if(o.clientValidation === false || this.form.isValid()){
7933             
7934             if (this.form.progressUrl) {
7935                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7936                     (new Date() * 1) + '' + Math.random());
7937                     
7938             } 
7939             
7940             
7941             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7942                 form:this.form.el.dom,
7943                 url:this.getUrl(!isPost),
7944                 method: method,
7945                 params:isPost ? this.getParams() : null,
7946                 isUpload: this.form.fileUpload,
7947                 formData : this.form.formData
7948             }));
7949             
7950             this.uploadProgress();
7951
7952         }else if (o.clientValidation !== false){ // client validation failed
7953             this.failureType = Roo.form.Action.CLIENT_INVALID;
7954             this.form.afterAction(this, false);
7955         }
7956     },
7957
7958     success : function(response)
7959     {
7960         this.uploadComplete= true;
7961         if (this.haveProgress) {
7962             Roo.MessageBox.hide();
7963         }
7964         
7965         
7966         var result = this.processResponse(response);
7967         if(result === true || result.success){
7968             this.form.afterAction(this, true);
7969             return;
7970         }
7971         if(result.errors){
7972             this.form.markInvalid(result.errors);
7973             this.failureType = Roo.form.Action.SERVER_INVALID;
7974         }
7975         this.form.afterAction(this, false);
7976     },
7977     failure : function(response)
7978     {
7979         this.uploadComplete= true;
7980         if (this.haveProgress) {
7981             Roo.MessageBox.hide();
7982         }
7983         
7984         this.response = response;
7985         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7986         this.form.afterAction(this, false);
7987     },
7988     
7989     handleResponse : function(response){
7990         if(this.form.errorReader){
7991             var rs = this.form.errorReader.read(response);
7992             var errors = [];
7993             if(rs.records){
7994                 for(var i = 0, len = rs.records.length; i < len; i++) {
7995                     var r = rs.records[i];
7996                     errors[i] = r.data;
7997                 }
7998             }
7999             if(errors.length < 1){
8000                 errors = null;
8001             }
8002             return {
8003                 success : rs.success,
8004                 errors : errors
8005             };
8006         }
8007         var ret = false;
8008         try {
8009             ret = Roo.decode(response.responseText);
8010         } catch (e) {
8011             ret = {
8012                 success: false,
8013                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
8014                 errors : []
8015             };
8016         }
8017         return ret;
8018         
8019     }
8020 });
8021
8022
8023 Roo.form.Action.Load = function(form, options){
8024     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
8025     this.reader = this.form.reader;
8026 };
8027
8028 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
8029     type : 'load',
8030
8031     run : function(){
8032         
8033         Roo.Ajax.request(Roo.apply(
8034                 this.createCallback(), {
8035                     method:this.getMethod(),
8036                     url:this.getUrl(false),
8037                     params:this.getParams()
8038         }));
8039     },
8040
8041     success : function(response){
8042         
8043         var result = this.processResponse(response);
8044         if(result === true || !result.success || !result.data){
8045             this.failureType = Roo.form.Action.LOAD_FAILURE;
8046             this.form.afterAction(this, false);
8047             return;
8048         }
8049         this.form.clearInvalid();
8050         this.form.setValues(result.data);
8051         this.form.afterAction(this, true);
8052     },
8053
8054     handleResponse : function(response){
8055         if(this.form.reader){
8056             var rs = this.form.reader.read(response);
8057             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
8058             return {
8059                 success : rs.success,
8060                 data : data
8061             };
8062         }
8063         return Roo.decode(response.responseText);
8064     }
8065 });
8066
8067 Roo.form.Action.ACTION_TYPES = {
8068     'load' : Roo.form.Action.Load,
8069     'submit' : Roo.form.Action.Submit
8070 };/*
8071  * - LGPL
8072  *
8073  * form
8074  *
8075  */
8076
8077 /**
8078  * @class Roo.bootstrap.Form
8079  * @extends Roo.bootstrap.Component
8080  * Bootstrap Form class
8081  * @cfg {String} method  GET | POST (default POST)
8082  * @cfg {String} labelAlign top | left (default top)
8083  * @cfg {String} align left  | right - for navbars
8084  * @cfg {Boolean} loadMask load mask when submit (default true)
8085
8086  *
8087  * @constructor
8088  * Create a new Form
8089  * @param {Object} config The config object
8090  */
8091
8092
8093 Roo.bootstrap.Form = function(config){
8094     
8095     Roo.bootstrap.Form.superclass.constructor.call(this, config);
8096     
8097     Roo.bootstrap.Form.popover.apply();
8098     
8099     this.addEvents({
8100         /**
8101          * @event clientvalidation
8102          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
8103          * @param {Form} this
8104          * @param {Boolean} valid true if the form has passed client-side validation
8105          */
8106         clientvalidation: true,
8107         /**
8108          * @event beforeaction
8109          * Fires before any action is performed. Return false to cancel the action.
8110          * @param {Form} this
8111          * @param {Action} action The action to be performed
8112          */
8113         beforeaction: true,
8114         /**
8115          * @event actionfailed
8116          * Fires when an action fails.
8117          * @param {Form} this
8118          * @param {Action} action The action that failed
8119          */
8120         actionfailed : true,
8121         /**
8122          * @event actioncomplete
8123          * Fires when an action is completed.
8124          * @param {Form} this
8125          * @param {Action} action The action that completed
8126          */
8127         actioncomplete : true
8128     });
8129 };
8130
8131 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
8132
8133      /**
8134      * @cfg {String} method
8135      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8136      */
8137     method : 'POST',
8138     /**
8139      * @cfg {String} url
8140      * The URL to use for form actions if one isn't supplied in the action options.
8141      */
8142     /**
8143      * @cfg {Boolean} fileUpload
8144      * Set to true if this form is a file upload.
8145      */
8146
8147     /**
8148      * @cfg {Object} baseParams
8149      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8150      */
8151
8152     /**
8153      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8154      */
8155     timeout: 30,
8156     /**
8157      * @cfg {Sting} align (left|right) for navbar forms
8158      */
8159     align : 'left',
8160
8161     // private
8162     activeAction : null,
8163
8164     /**
8165      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8166      * element by passing it or its id or mask the form itself by passing in true.
8167      * @type Mixed
8168      */
8169     waitMsgTarget : false,
8170
8171     loadMask : true,
8172     
8173     /**
8174      * @cfg {Boolean} errorMask (true|false) default false
8175      */
8176     errorMask : false,
8177     
8178     /**
8179      * @cfg {Number} maskOffset Default 100
8180      */
8181     maskOffset : 100,
8182     
8183     /**
8184      * @cfg {Boolean} maskBody
8185      */
8186     maskBody : false,
8187
8188     getAutoCreate : function(){
8189
8190         var cfg = {
8191             tag: 'form',
8192             method : this.method || 'POST',
8193             id : this.id || Roo.id(),
8194             cls : ''
8195         };
8196         if (this.parent().xtype.match(/^Nav/)) {
8197             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8198
8199         }
8200
8201         if (this.labelAlign == 'left' ) {
8202             cfg.cls += ' form-horizontal';
8203         }
8204
8205
8206         return cfg;
8207     },
8208     initEvents : function()
8209     {
8210         this.el.on('submit', this.onSubmit, this);
8211         // this was added as random key presses on the form where triggering form submit.
8212         this.el.on('keypress', function(e) {
8213             if (e.getCharCode() != 13) {
8214                 return true;
8215             }
8216             // we might need to allow it for textareas.. and some other items.
8217             // check e.getTarget().
8218
8219             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8220                 return true;
8221             }
8222
8223             Roo.log("keypress blocked");
8224
8225             e.preventDefault();
8226             return false;
8227         });
8228         
8229     },
8230     // private
8231     onSubmit : function(e){
8232         e.stopEvent();
8233     },
8234
8235      /**
8236      * Returns true if client-side validation on the form is successful.
8237      * @return Boolean
8238      */
8239     isValid : function(){
8240         var items = this.getItems();
8241         var valid = true;
8242         var target = false;
8243         
8244         items.each(function(f){
8245             
8246             if(f.validate()){
8247                 return;
8248             }
8249             
8250             Roo.log('invalid field: ' + f.name);
8251             
8252             valid = false;
8253
8254             if(!target && f.el.isVisible(true)){
8255                 target = f;
8256             }
8257            
8258         });
8259         
8260         if(this.errorMask && !valid){
8261             Roo.bootstrap.Form.popover.mask(this, target);
8262         }
8263         
8264         return valid;
8265     },
8266     
8267     /**
8268      * Returns true if any fields in this form have changed since their original load.
8269      * @return Boolean
8270      */
8271     isDirty : function(){
8272         var dirty = false;
8273         var items = this.getItems();
8274         items.each(function(f){
8275            if(f.isDirty()){
8276                dirty = true;
8277                return false;
8278            }
8279            return true;
8280         });
8281         return dirty;
8282     },
8283      /**
8284      * Performs a predefined action (submit or load) or custom actions you define on this form.
8285      * @param {String} actionName The name of the action type
8286      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8287      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8288      * accept other config options):
8289      * <pre>
8290 Property          Type             Description
8291 ----------------  ---------------  ----------------------------------------------------------------------------------
8292 url               String           The url for the action (defaults to the form's url)
8293 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8294 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8295 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8296                                    validate the form on the client (defaults to false)
8297      * </pre>
8298      * @return {BasicForm} this
8299      */
8300     doAction : function(action, options){
8301         if(typeof action == 'string'){
8302             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8303         }
8304         if(this.fireEvent('beforeaction', this, action) !== false){
8305             this.beforeAction(action);
8306             action.run.defer(100, action);
8307         }
8308         return this;
8309     },
8310
8311     // private
8312     beforeAction : function(action){
8313         var o = action.options;
8314         
8315         if(this.loadMask){
8316             
8317             if(this.maskBody){
8318                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8319             } else {
8320                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8321             }
8322         }
8323         // not really supported yet.. ??
8324
8325         //if(this.waitMsgTarget === true){
8326         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8327         //}else if(this.waitMsgTarget){
8328         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8329         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8330         //}else {
8331         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8332        // }
8333
8334     },
8335
8336     // private
8337     afterAction : function(action, success){
8338         this.activeAction = null;
8339         var o = action.options;
8340
8341         if(this.loadMask){
8342             
8343             if(this.maskBody){
8344                 Roo.get(document.body).unmask();
8345             } else {
8346                 this.el.unmask();
8347             }
8348         }
8349         
8350         //if(this.waitMsgTarget === true){
8351 //            this.el.unmask();
8352         //}else if(this.waitMsgTarget){
8353         //    this.waitMsgTarget.unmask();
8354         //}else{
8355         //    Roo.MessageBox.updateProgress(1);
8356         //    Roo.MessageBox.hide();
8357        // }
8358         //
8359         if(success){
8360             if(o.reset){
8361                 this.reset();
8362             }
8363             Roo.callback(o.success, o.scope, [this, action]);
8364             this.fireEvent('actioncomplete', this, action);
8365
8366         }else{
8367
8368             // failure condition..
8369             // we have a scenario where updates need confirming.
8370             // eg. if a locking scenario exists..
8371             // we look for { errors : { needs_confirm : true }} in the response.
8372             if (
8373                 (typeof(action.result) != 'undefined')  &&
8374                 (typeof(action.result.errors) != 'undefined')  &&
8375                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8376            ){
8377                 var _t = this;
8378                 Roo.log("not supported yet");
8379                  /*
8380
8381                 Roo.MessageBox.confirm(
8382                     "Change requires confirmation",
8383                     action.result.errorMsg,
8384                     function(r) {
8385                         if (r != 'yes') {
8386                             return;
8387                         }
8388                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8389                     }
8390
8391                 );
8392                 */
8393
8394
8395                 return;
8396             }
8397
8398             Roo.callback(o.failure, o.scope, [this, action]);
8399             // show an error message if no failed handler is set..
8400             if (!this.hasListener('actionfailed')) {
8401                 Roo.log("need to add dialog support");
8402                 /*
8403                 Roo.MessageBox.alert("Error",
8404                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8405                         action.result.errorMsg :
8406                         "Saving Failed, please check your entries or try again"
8407                 );
8408                 */
8409             }
8410
8411             this.fireEvent('actionfailed', this, action);
8412         }
8413
8414     },
8415     /**
8416      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8417      * @param {String} id The value to search for
8418      * @return Field
8419      */
8420     findField : function(id){
8421         var items = this.getItems();
8422         var field = items.get(id);
8423         if(!field){
8424              items.each(function(f){
8425                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8426                     field = f;
8427                     return false;
8428                 }
8429                 return true;
8430             });
8431         }
8432         return field || null;
8433     },
8434      /**
8435      * Mark fields in this form invalid in bulk.
8436      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8437      * @return {BasicForm} this
8438      */
8439     markInvalid : function(errors){
8440         if(errors instanceof Array){
8441             for(var i = 0, len = errors.length; i < len; i++){
8442                 var fieldError = errors[i];
8443                 var f = this.findField(fieldError.id);
8444                 if(f){
8445                     f.markInvalid(fieldError.msg);
8446                 }
8447             }
8448         }else{
8449             var field, id;
8450             for(id in errors){
8451                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8452                     field.markInvalid(errors[id]);
8453                 }
8454             }
8455         }
8456         //Roo.each(this.childForms || [], function (f) {
8457         //    f.markInvalid(errors);
8458         //});
8459
8460         return this;
8461     },
8462
8463     /**
8464      * Set values for fields in this form in bulk.
8465      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8466      * @return {BasicForm} this
8467      */
8468     setValues : function(values){
8469         if(values instanceof Array){ // array of objects
8470             for(var i = 0, len = values.length; i < len; i++){
8471                 var v = values[i];
8472                 var f = this.findField(v.id);
8473                 if(f){
8474                     f.setValue(v.value);
8475                     if(this.trackResetOnLoad){
8476                         f.originalValue = f.getValue();
8477                     }
8478                 }
8479             }
8480         }else{ // object hash
8481             var field, id;
8482             for(id in values){
8483                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8484
8485                     if (field.setFromData &&
8486                         field.valueField &&
8487                         field.displayField &&
8488                         // combos' with local stores can
8489                         // be queried via setValue()
8490                         // to set their value..
8491                         (field.store && !field.store.isLocal)
8492                         ) {
8493                         // it's a combo
8494                         var sd = { };
8495                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8496                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8497                         field.setFromData(sd);
8498
8499                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8500                         
8501                         field.setFromData(values);
8502                         
8503                     } else {
8504                         field.setValue(values[id]);
8505                     }
8506
8507
8508                     if(this.trackResetOnLoad){
8509                         field.originalValue = field.getValue();
8510                     }
8511                 }
8512             }
8513         }
8514
8515         //Roo.each(this.childForms || [], function (f) {
8516         //    f.setValues(values);
8517         //});
8518
8519         return this;
8520     },
8521
8522     /**
8523      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8524      * they are returned as an array.
8525      * @param {Boolean} asString
8526      * @return {Object}
8527      */
8528     getValues : function(asString){
8529         //if (this.childForms) {
8530             // copy values from the child forms
8531         //    Roo.each(this.childForms, function (f) {
8532         //        this.setValues(f.getValues());
8533         //    }, this);
8534         //}
8535
8536
8537
8538         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8539         if(asString === true){
8540             return fs;
8541         }
8542         return Roo.urlDecode(fs);
8543     },
8544
8545     /**
8546      * Returns the fields in this form as an object with key/value pairs.
8547      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8548      * @return {Object}
8549      */
8550     getFieldValues : function(with_hidden)
8551     {
8552         var items = this.getItems();
8553         var ret = {};
8554         items.each(function(f){
8555             
8556             if (!f.getName()) {
8557                 return;
8558             }
8559             
8560             var v = f.getValue();
8561             
8562             if (f.inputType =='radio') {
8563                 if (typeof(ret[f.getName()]) == 'undefined') {
8564                     ret[f.getName()] = ''; // empty..
8565                 }
8566
8567                 if (!f.el.dom.checked) {
8568                     return;
8569
8570                 }
8571                 v = f.el.dom.value;
8572
8573             }
8574             
8575             if(f.xtype == 'MoneyField'){
8576                 ret[f.currencyName] = f.getCurrency();
8577             }
8578
8579             // not sure if this supported any more..
8580             if ((typeof(v) == 'object') && f.getRawValue) {
8581                 v = f.getRawValue() ; // dates..
8582             }
8583             // combo boxes where name != hiddenName...
8584             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8585                 ret[f.name] = f.getRawValue();
8586             }
8587             ret[f.getName()] = v;
8588         });
8589
8590         return ret;
8591     },
8592
8593     /**
8594      * Clears all invalid messages in this form.
8595      * @return {BasicForm} this
8596      */
8597     clearInvalid : function(){
8598         var items = this.getItems();
8599
8600         items.each(function(f){
8601            f.clearInvalid();
8602         });
8603
8604         return this;
8605     },
8606
8607     /**
8608      * Resets this form.
8609      * @return {BasicForm} this
8610      */
8611     reset : function(){
8612         var items = this.getItems();
8613         items.each(function(f){
8614             f.reset();
8615         });
8616
8617         Roo.each(this.childForms || [], function (f) {
8618             f.reset();
8619         });
8620
8621
8622         return this;
8623     },
8624     
8625     getItems : function()
8626     {
8627         var r=new Roo.util.MixedCollection(false, function(o){
8628             return o.id || (o.id = Roo.id());
8629         });
8630         var iter = function(el) {
8631             if (el.inputEl) {
8632                 r.add(el);
8633             }
8634             if (!el.items) {
8635                 return;
8636             }
8637             Roo.each(el.items,function(e) {
8638                 iter(e);
8639             });
8640         };
8641
8642         iter(this);
8643         return r;
8644     },
8645     
8646     hideFields : function(items)
8647     {
8648         Roo.each(items, function(i){
8649             
8650             var f = this.findField(i);
8651             
8652             if(!f){
8653                 return;
8654             }
8655             
8656             f.hide();
8657             
8658         }, this);
8659     },
8660     
8661     showFields : function(items)
8662     {
8663         Roo.each(items, function(i){
8664             
8665             var f = this.findField(i);
8666             
8667             if(!f){
8668                 return;
8669             }
8670             
8671             f.show();
8672             
8673         }, this);
8674     }
8675
8676 });
8677
8678 Roo.apply(Roo.bootstrap.Form, {
8679     
8680     popover : {
8681         
8682         padding : 5,
8683         
8684         isApplied : false,
8685         
8686         isMasked : false,
8687         
8688         form : false,
8689         
8690         target : false,
8691         
8692         toolTip : false,
8693         
8694         intervalID : false,
8695         
8696         maskEl : false,
8697         
8698         apply : function()
8699         {
8700             if(this.isApplied){
8701                 return;
8702             }
8703             
8704             this.maskEl = {
8705                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8706                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8707                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8708                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8709             };
8710             
8711             this.maskEl.top.enableDisplayMode("block");
8712             this.maskEl.left.enableDisplayMode("block");
8713             this.maskEl.bottom.enableDisplayMode("block");
8714             this.maskEl.right.enableDisplayMode("block");
8715             
8716             this.toolTip = new Roo.bootstrap.Tooltip({
8717                 cls : 'roo-form-error-popover',
8718                 alignment : {
8719                     'left' : ['r-l', [-2,0], 'right'],
8720                     'right' : ['l-r', [2,0], 'left'],
8721                     'bottom' : ['tl-bl', [0,2], 'top'],
8722                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8723                 }
8724             });
8725             
8726             this.toolTip.render(Roo.get(document.body));
8727
8728             this.toolTip.el.enableDisplayMode("block");
8729             
8730             Roo.get(document.body).on('click', function(){
8731                 this.unmask();
8732             }, this);
8733             
8734             Roo.get(document.body).on('touchstart', function(){
8735                 this.unmask();
8736             }, this);
8737             
8738             this.isApplied = true
8739         },
8740         
8741         mask : function(form, target)
8742         {
8743             this.form = form;
8744             
8745             this.target = target;
8746             
8747             if(!this.form.errorMask || !target.el){
8748                 return;
8749             }
8750             
8751             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8752             
8753             Roo.log(scrollable);
8754             
8755             var ot = this.target.el.calcOffsetsTo(scrollable);
8756             
8757             var scrollTo = ot[1] - this.form.maskOffset;
8758             
8759             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8760             
8761             scrollable.scrollTo('top', scrollTo);
8762             
8763             var box = this.target.el.getBox();
8764             Roo.log(box);
8765             var zIndex = Roo.bootstrap.Modal.zIndex++;
8766
8767             
8768             this.maskEl.top.setStyle('position', 'absolute');
8769             this.maskEl.top.setStyle('z-index', zIndex);
8770             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8771             this.maskEl.top.setLeft(0);
8772             this.maskEl.top.setTop(0);
8773             this.maskEl.top.show();
8774             
8775             this.maskEl.left.setStyle('position', 'absolute');
8776             this.maskEl.left.setStyle('z-index', zIndex);
8777             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8778             this.maskEl.left.setLeft(0);
8779             this.maskEl.left.setTop(box.y - this.padding);
8780             this.maskEl.left.show();
8781
8782             this.maskEl.bottom.setStyle('position', 'absolute');
8783             this.maskEl.bottom.setStyle('z-index', zIndex);
8784             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8785             this.maskEl.bottom.setLeft(0);
8786             this.maskEl.bottom.setTop(box.bottom + this.padding);
8787             this.maskEl.bottom.show();
8788
8789             this.maskEl.right.setStyle('position', 'absolute');
8790             this.maskEl.right.setStyle('z-index', zIndex);
8791             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8792             this.maskEl.right.setLeft(box.right + this.padding);
8793             this.maskEl.right.setTop(box.y - this.padding);
8794             this.maskEl.right.show();
8795
8796             this.toolTip.bindEl = this.target.el;
8797
8798             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8799
8800             var tip = this.target.blankText;
8801
8802             if(this.target.getValue() !== '' ) {
8803                 
8804                 if (this.target.invalidText.length) {
8805                     tip = this.target.invalidText;
8806                 } else if (this.target.regexText.length){
8807                     tip = this.target.regexText;
8808                 }
8809             }
8810
8811             this.toolTip.show(tip);
8812
8813             this.intervalID = window.setInterval(function() {
8814                 Roo.bootstrap.Form.popover.unmask();
8815             }, 10000);
8816
8817             window.onwheel = function(){ return false;};
8818             
8819             (function(){ this.isMasked = true; }).defer(500, this);
8820             
8821         },
8822         
8823         unmask : function()
8824         {
8825             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8826                 return;
8827             }
8828             
8829             this.maskEl.top.setStyle('position', 'absolute');
8830             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8831             this.maskEl.top.hide();
8832
8833             this.maskEl.left.setStyle('position', 'absolute');
8834             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8835             this.maskEl.left.hide();
8836
8837             this.maskEl.bottom.setStyle('position', 'absolute');
8838             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8839             this.maskEl.bottom.hide();
8840
8841             this.maskEl.right.setStyle('position', 'absolute');
8842             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8843             this.maskEl.right.hide();
8844             
8845             this.toolTip.hide();
8846             
8847             this.toolTip.el.hide();
8848             
8849             window.onwheel = function(){ return true;};
8850             
8851             if(this.intervalID){
8852                 window.clearInterval(this.intervalID);
8853                 this.intervalID = false;
8854             }
8855             
8856             this.isMasked = false;
8857             
8858         }
8859         
8860     }
8861     
8862 });
8863
8864 /*
8865  * Based on:
8866  * Ext JS Library 1.1.1
8867  * Copyright(c) 2006-2007, Ext JS, LLC.
8868  *
8869  * Originally Released Under LGPL - original licence link has changed is not relivant.
8870  *
8871  * Fork - LGPL
8872  * <script type="text/javascript">
8873  */
8874 /**
8875  * @class Roo.form.VTypes
8876  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8877  * @singleton
8878  */
8879 Roo.form.VTypes = function(){
8880     // closure these in so they are only created once.
8881     var alpha = /^[a-zA-Z_]+$/;
8882     var alphanum = /^[a-zA-Z0-9_]+$/;
8883     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8884     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8885
8886     // All these messages and functions are configurable
8887     return {
8888         /**
8889          * The function used to validate email addresses
8890          * @param {String} value The email address
8891          */
8892         'email' : function(v){
8893             return email.test(v);
8894         },
8895         /**
8896          * The error text to display when the email validation function returns false
8897          * @type String
8898          */
8899         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8900         /**
8901          * The keystroke filter mask to be applied on email input
8902          * @type RegExp
8903          */
8904         'emailMask' : /[a-z0-9_\.\-@]/i,
8905
8906         /**
8907          * The function used to validate URLs
8908          * @param {String} value The URL
8909          */
8910         'url' : function(v){
8911             return url.test(v);
8912         },
8913         /**
8914          * The error text to display when the url validation function returns false
8915          * @type String
8916          */
8917         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8918         
8919         /**
8920          * The function used to validate alpha values
8921          * @param {String} value The value
8922          */
8923         'alpha' : function(v){
8924             return alpha.test(v);
8925         },
8926         /**
8927          * The error text to display when the alpha validation function returns false
8928          * @type String
8929          */
8930         'alphaText' : 'This field should only contain letters and _',
8931         /**
8932          * The keystroke filter mask to be applied on alpha input
8933          * @type RegExp
8934          */
8935         'alphaMask' : /[a-z_]/i,
8936
8937         /**
8938          * The function used to validate alphanumeric values
8939          * @param {String} value The value
8940          */
8941         'alphanum' : function(v){
8942             return alphanum.test(v);
8943         },
8944         /**
8945          * The error text to display when the alphanumeric validation function returns false
8946          * @type String
8947          */
8948         'alphanumText' : 'This field should only contain letters, numbers and _',
8949         /**
8950          * The keystroke filter mask to be applied on alphanumeric input
8951          * @type RegExp
8952          */
8953         'alphanumMask' : /[a-z0-9_]/i
8954     };
8955 }();/*
8956  * - LGPL
8957  *
8958  * Input
8959  * 
8960  */
8961
8962 /**
8963  * @class Roo.bootstrap.Input
8964  * @extends Roo.bootstrap.Component
8965  * Bootstrap Input class
8966  * @cfg {Boolean} disabled is it disabled
8967  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8968  * @cfg {String} name name of the input
8969  * @cfg {string} fieldLabel - the label associated
8970  * @cfg {string} placeholder - placeholder to put in text.
8971  * @cfg {string}  before - input group add on before
8972  * @cfg {string} after - input group add on after
8973  * @cfg {string} size - (lg|sm) or leave empty..
8974  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8975  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8976  * @cfg {Number} md colspan out of 12 for computer-sized screens
8977  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8978  * @cfg {string} value default value of the input
8979  * @cfg {Number} labelWidth set the width of label 
8980  * @cfg {Number} labellg set the width of label (1-12)
8981  * @cfg {Number} labelmd set the width of label (1-12)
8982  * @cfg {Number} labelsm set the width of label (1-12)
8983  * @cfg {Number} labelxs set the width of label (1-12)
8984  * @cfg {String} labelAlign (top|left)
8985  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8986  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8987  * @cfg {String} indicatorpos (left|right) default left
8988  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8989  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8990
8991  * @cfg {String} align (left|center|right) Default left
8992  * @cfg {Boolean} forceFeedback (true|false) Default false
8993  * 
8994  * @constructor
8995  * Create a new Input
8996  * @param {Object} config The config object
8997  */
8998
8999 Roo.bootstrap.Input = function(config){
9000     
9001     Roo.bootstrap.Input.superclass.constructor.call(this, config);
9002     
9003     this.addEvents({
9004         /**
9005          * @event focus
9006          * Fires when this field receives input focus.
9007          * @param {Roo.form.Field} this
9008          */
9009         focus : true,
9010         /**
9011          * @event blur
9012          * Fires when this field loses input focus.
9013          * @param {Roo.form.Field} this
9014          */
9015         blur : true,
9016         /**
9017          * @event specialkey
9018          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
9019          * {@link Roo.EventObject#getKey} to determine which key was pressed.
9020          * @param {Roo.form.Field} this
9021          * @param {Roo.EventObject} e The event object
9022          */
9023         specialkey : true,
9024         /**
9025          * @event change
9026          * Fires just before the field blurs if the field value has changed.
9027          * @param {Roo.form.Field} this
9028          * @param {Mixed} newValue The new value
9029          * @param {Mixed} oldValue The original value
9030          */
9031         change : true,
9032         /**
9033          * @event invalid
9034          * Fires after the field has been marked as invalid.
9035          * @param {Roo.form.Field} this
9036          * @param {String} msg The validation message
9037          */
9038         invalid : true,
9039         /**
9040          * @event valid
9041          * Fires after the field has been validated with no errors.
9042          * @param {Roo.form.Field} this
9043          */
9044         valid : true,
9045          /**
9046          * @event keyup
9047          * Fires after the key up
9048          * @param {Roo.form.Field} this
9049          * @param {Roo.EventObject}  e The event Object
9050          */
9051         keyup : true
9052     });
9053 };
9054
9055 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
9056      /**
9057      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
9058       automatic validation (defaults to "keyup").
9059      */
9060     validationEvent : "keyup",
9061      /**
9062      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
9063      */
9064     validateOnBlur : true,
9065     /**
9066      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
9067      */
9068     validationDelay : 250,
9069      /**
9070      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
9071      */
9072     focusClass : "x-form-focus",  // not needed???
9073     
9074        
9075     /**
9076      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9077      */
9078     invalidClass : "has-warning",
9079     
9080     /**
9081      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9082      */
9083     validClass : "has-success",
9084     
9085     /**
9086      * @cfg {Boolean} hasFeedback (true|false) default true
9087      */
9088     hasFeedback : true,
9089     
9090     /**
9091      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9092      */
9093     invalidFeedbackClass : "glyphicon-warning-sign",
9094     
9095     /**
9096      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9097      */
9098     validFeedbackClass : "glyphicon-ok",
9099     
9100     /**
9101      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
9102      */
9103     selectOnFocus : false,
9104     
9105      /**
9106      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
9107      */
9108     maskRe : null,
9109        /**
9110      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
9111      */
9112     vtype : null,
9113     
9114       /**
9115      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
9116      */
9117     disableKeyFilter : false,
9118     
9119        /**
9120      * @cfg {Boolean} disabled True to disable the field (defaults to false).
9121      */
9122     disabled : false,
9123      /**
9124      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
9125      */
9126     allowBlank : true,
9127     /**
9128      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9129      */
9130     blankText : "Please complete this mandatory field",
9131     
9132      /**
9133      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9134      */
9135     minLength : 0,
9136     /**
9137      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9138      */
9139     maxLength : Number.MAX_VALUE,
9140     /**
9141      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9142      */
9143     minLengthText : "The minimum length for this field is {0}",
9144     /**
9145      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9146      */
9147     maxLengthText : "The maximum length for this field is {0}",
9148   
9149     
9150     /**
9151      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9152      * If available, this function will be called only after the basic validators all return true, and will be passed the
9153      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9154      */
9155     validator : null,
9156     /**
9157      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9158      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9159      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
9160      */
9161     regex : null,
9162     /**
9163      * @cfg {String} regexText -- Depricated - use Invalid Text
9164      */
9165     regexText : "",
9166     
9167     /**
9168      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9169      */
9170     invalidText : "",
9171     
9172     
9173     
9174     autocomplete: false,
9175     
9176     
9177     fieldLabel : '',
9178     inputType : 'text',
9179     
9180     name : false,
9181     placeholder: false,
9182     before : false,
9183     after : false,
9184     size : false,
9185     hasFocus : false,
9186     preventMark: false,
9187     isFormField : true,
9188     value : '',
9189     labelWidth : 2,
9190     labelAlign : false,
9191     readOnly : false,
9192     align : false,
9193     formatedValue : false,
9194     forceFeedback : false,
9195     
9196     indicatorpos : 'left',
9197     
9198     labellg : 0,
9199     labelmd : 0,
9200     labelsm : 0,
9201     labelxs : 0,
9202     
9203     capture : '',
9204     accept : '',
9205     
9206     parentLabelAlign : function()
9207     {
9208         var parent = this;
9209         while (parent.parent()) {
9210             parent = parent.parent();
9211             if (typeof(parent.labelAlign) !='undefined') {
9212                 return parent.labelAlign;
9213             }
9214         }
9215         return 'left';
9216         
9217     },
9218     
9219     getAutoCreate : function()
9220     {
9221         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9222         
9223         var id = Roo.id();
9224         
9225         var cfg = {};
9226         
9227         if(this.inputType != 'hidden'){
9228             cfg.cls = 'form-group' //input-group
9229         }
9230         
9231         var input =  {
9232             tag: 'input',
9233             id : id,
9234             type : this.inputType,
9235             value : this.value,
9236             cls : 'form-control',
9237             placeholder : this.placeholder || '',
9238             autocomplete : this.autocomplete || 'new-password'
9239         };
9240         
9241         if(this.capture.length){
9242             input.capture = this.capture;
9243         }
9244         
9245         if(this.accept.length){
9246             input.accept = this.accept + "/*";
9247         }
9248         
9249         if(this.align){
9250             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9251         }
9252         
9253         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9254             input.maxLength = this.maxLength;
9255         }
9256         
9257         if (this.disabled) {
9258             input.disabled=true;
9259         }
9260         
9261         if (this.readOnly) {
9262             input.readonly=true;
9263         }
9264         
9265         if (this.name) {
9266             input.name = this.name;
9267         }
9268         
9269         if (this.size) {
9270             input.cls += ' input-' + this.size;
9271         }
9272         
9273         var settings=this;
9274         ['xs','sm','md','lg'].map(function(size){
9275             if (settings[size]) {
9276                 cfg.cls += ' col-' + size + '-' + settings[size];
9277             }
9278         });
9279         
9280         var inputblock = input;
9281         
9282         var feedback = {
9283             tag: 'span',
9284             cls: 'glyphicon form-control-feedback'
9285         };
9286             
9287         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9288             
9289             inputblock = {
9290                 cls : 'has-feedback',
9291                 cn :  [
9292                     input,
9293                     feedback
9294                 ] 
9295             };  
9296         }
9297         
9298         if (this.before || this.after) {
9299             
9300             inputblock = {
9301                 cls : 'input-group',
9302                 cn :  [] 
9303             };
9304             
9305             if (this.before && typeof(this.before) == 'string') {
9306                 
9307                 inputblock.cn.push({
9308                     tag :'span',
9309                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9310                     html : this.before
9311                 });
9312             }
9313             if (this.before && typeof(this.before) == 'object') {
9314                 this.before = Roo.factory(this.before);
9315                 
9316                 inputblock.cn.push({
9317                     tag :'span',
9318                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9319                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9320                 });
9321             }
9322             
9323             inputblock.cn.push(input);
9324             
9325             if (this.after && typeof(this.after) == 'string') {
9326                 inputblock.cn.push({
9327                     tag :'span',
9328                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9329                     html : this.after
9330                 });
9331             }
9332             if (this.after && typeof(this.after) == 'object') {
9333                 this.after = Roo.factory(this.after);
9334                 
9335                 inputblock.cn.push({
9336                     tag :'span',
9337                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
9338                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9339                 });
9340             }
9341             
9342             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9343                 inputblock.cls += ' has-feedback';
9344                 inputblock.cn.push(feedback);
9345             }
9346         };
9347         var indicator = {
9348             tag : 'i',
9349             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9350             tooltip : 'This field is required'
9351         };
9352         if (Roo.bootstrap.version == 4) {
9353             indicator = {
9354                 tag : 'i',
9355                 style : 'display-none'
9356             };
9357         }
9358         if (align ==='left' && this.fieldLabel.length) {
9359             
9360             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
9361             
9362             cfg.cn = [
9363                 indicator,
9364                 {
9365                     tag: 'label',
9366                     'for' :  id,
9367                     cls : 'control-label col-form-label',
9368                     html : this.fieldLabel
9369
9370                 },
9371                 {
9372                     cls : "", 
9373                     cn: [
9374                         inputblock
9375                     ]
9376                 }
9377             ];
9378             
9379             var labelCfg = cfg.cn[1];
9380             var contentCfg = cfg.cn[2];
9381             
9382             if(this.indicatorpos == 'right'){
9383                 cfg.cn = [
9384                     {
9385                         tag: 'label',
9386                         'for' :  id,
9387                         cls : 'control-label col-form-label',
9388                         cn : [
9389                             {
9390                                 tag : 'span',
9391                                 html : this.fieldLabel
9392                             },
9393                             indicator
9394                         ]
9395                     },
9396                     {
9397                         cls : "",
9398                         cn: [
9399                             inputblock
9400                         ]
9401                     }
9402
9403                 ];
9404                 
9405                 labelCfg = cfg.cn[0];
9406                 contentCfg = cfg.cn[1];
9407             
9408             }
9409             
9410             if(this.labelWidth > 12){
9411                 labelCfg.style = "width: " + this.labelWidth + 'px';
9412             }
9413             
9414             if(this.labelWidth < 13 && this.labelmd == 0){
9415                 this.labelmd = this.labelWidth;
9416             }
9417             
9418             if(this.labellg > 0){
9419                 labelCfg.cls += ' col-lg-' + this.labellg;
9420                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9421             }
9422             
9423             if(this.labelmd > 0){
9424                 labelCfg.cls += ' col-md-' + this.labelmd;
9425                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9426             }
9427             
9428             if(this.labelsm > 0){
9429                 labelCfg.cls += ' col-sm-' + this.labelsm;
9430                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9431             }
9432             
9433             if(this.labelxs > 0){
9434                 labelCfg.cls += ' col-xs-' + this.labelxs;
9435                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9436             }
9437             
9438             
9439         } else if ( this.fieldLabel.length) {
9440                 
9441             cfg.cn = [
9442                 {
9443                     tag : 'i',
9444                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9445                     tooltip : 'This field is required'
9446                 },
9447                 {
9448                     tag: 'label',
9449                    //cls : 'input-group-addon',
9450                     html : this.fieldLabel
9451
9452                 },
9453
9454                inputblock
9455
9456            ];
9457            
9458            if(this.indicatorpos == 'right'){
9459                 
9460                 cfg.cn = [
9461                     {
9462                         tag: 'label',
9463                        //cls : 'input-group-addon',
9464                         html : this.fieldLabel
9465
9466                     },
9467                     {
9468                         tag : 'i',
9469                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9470                         tooltip : 'This field is required'
9471                     },
9472
9473                    inputblock
9474
9475                ];
9476
9477             }
9478
9479         } else {
9480             
9481             cfg.cn = [
9482
9483                     inputblock
9484
9485             ];
9486                 
9487                 
9488         };
9489         
9490         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9491            cfg.cls += ' navbar-form';
9492         }
9493         
9494         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
9495             // on BS4 we do this only if not form 
9496             cfg.cls += ' navbar-form';
9497             cfg.tag = 'li';
9498         }
9499         
9500         return cfg;
9501         
9502     },
9503     /**
9504      * return the real input element.
9505      */
9506     inputEl: function ()
9507     {
9508         return this.el.select('input.form-control',true).first();
9509     },
9510     
9511     tooltipEl : function()
9512     {
9513         return this.inputEl();
9514     },
9515     
9516     indicatorEl : function()
9517     {
9518         if (Roo.bootstrap.version == 4) {
9519             return false; // not enabled in v4 yet.
9520         }
9521         
9522         var indicator = this.el.select('i.roo-required-indicator',true).first();
9523         
9524         if(!indicator){
9525             return false;
9526         }
9527         
9528         return indicator;
9529         
9530     },
9531     
9532     setDisabled : function(v)
9533     {
9534         var i  = this.inputEl().dom;
9535         if (!v) {
9536             i.removeAttribute('disabled');
9537             return;
9538             
9539         }
9540         i.setAttribute('disabled','true');
9541     },
9542     initEvents : function()
9543     {
9544           
9545         this.inputEl().on("keydown" , this.fireKey,  this);
9546         this.inputEl().on("focus", this.onFocus,  this);
9547         this.inputEl().on("blur", this.onBlur,  this);
9548         
9549         this.inputEl().relayEvent('keyup', this);
9550         
9551         this.indicator = this.indicatorEl();
9552         
9553         if(this.indicator){
9554             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9555         }
9556  
9557         // reference to original value for reset
9558         this.originalValue = this.getValue();
9559         //Roo.form.TextField.superclass.initEvents.call(this);
9560         if(this.validationEvent == 'keyup'){
9561             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9562             this.inputEl().on('keyup', this.filterValidation, this);
9563         }
9564         else if(this.validationEvent !== false){
9565             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9566         }
9567         
9568         if(this.selectOnFocus){
9569             this.on("focus", this.preFocus, this);
9570             
9571         }
9572         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9573             this.inputEl().on("keypress", this.filterKeys, this);
9574         } else {
9575             this.inputEl().relayEvent('keypress', this);
9576         }
9577        /* if(this.grow){
9578             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9579             this.el.on("click", this.autoSize,  this);
9580         }
9581         */
9582         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9583             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9584         }
9585         
9586         if (typeof(this.before) == 'object') {
9587             this.before.render(this.el.select('.roo-input-before',true).first());
9588         }
9589         if (typeof(this.after) == 'object') {
9590             this.after.render(this.el.select('.roo-input-after',true).first());
9591         }
9592         
9593         this.inputEl().on('change', this.onChange, this);
9594         
9595     },
9596     filterValidation : function(e){
9597         if(!e.isNavKeyPress()){
9598             this.validationTask.delay(this.validationDelay);
9599         }
9600     },
9601      /**
9602      * Validates the field value
9603      * @return {Boolean} True if the value is valid, else false
9604      */
9605     validate : function(){
9606         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9607         if(this.disabled || this.validateValue(this.getRawValue())){
9608             this.markValid();
9609             return true;
9610         }
9611         
9612         this.markInvalid();
9613         return false;
9614     },
9615     
9616     
9617     /**
9618      * Validates a value according to the field's validation rules and marks the field as invalid
9619      * if the validation fails
9620      * @param {Mixed} value The value to validate
9621      * @return {Boolean} True if the value is valid, else false
9622      */
9623     validateValue : function(value)
9624     {
9625         if(this.getVisibilityEl().hasClass('hidden')){
9626             return true;
9627         }
9628         
9629         if(value.length < 1)  { // if it's blank
9630             if(this.allowBlank){
9631                 return true;
9632             }
9633             return false;
9634         }
9635         
9636         if(value.length < this.minLength){
9637             return false;
9638         }
9639         if(value.length > this.maxLength){
9640             return false;
9641         }
9642         if(this.vtype){
9643             var vt = Roo.form.VTypes;
9644             if(!vt[this.vtype](value, this)){
9645                 return false;
9646             }
9647         }
9648         if(typeof this.validator == "function"){
9649             var msg = this.validator(value);
9650             if(msg !== true){
9651                 return false;
9652             }
9653             if (typeof(msg) == 'string') {
9654                 this.invalidText = msg;
9655             }
9656         }
9657         
9658         if(this.regex && !this.regex.test(value)){
9659             return false;
9660         }
9661         
9662         return true;
9663     },
9664     
9665      // private
9666     fireKey : function(e){
9667         //Roo.log('field ' + e.getKey());
9668         if(e.isNavKeyPress()){
9669             this.fireEvent("specialkey", this, e);
9670         }
9671     },
9672     focus : function (selectText){
9673         if(this.rendered){
9674             this.inputEl().focus();
9675             if(selectText === true){
9676                 this.inputEl().dom.select();
9677             }
9678         }
9679         return this;
9680     } ,
9681     
9682     onFocus : function(){
9683         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9684            // this.el.addClass(this.focusClass);
9685         }
9686         if(!this.hasFocus){
9687             this.hasFocus = true;
9688             this.startValue = this.getValue();
9689             this.fireEvent("focus", this);
9690         }
9691     },
9692     
9693     beforeBlur : Roo.emptyFn,
9694
9695     
9696     // private
9697     onBlur : function(){
9698         this.beforeBlur();
9699         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9700             //this.el.removeClass(this.focusClass);
9701         }
9702         this.hasFocus = false;
9703         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9704             this.validate();
9705         }
9706         var v = this.getValue();
9707         if(String(v) !== String(this.startValue)){
9708             this.fireEvent('change', this, v, this.startValue);
9709         }
9710         this.fireEvent("blur", this);
9711     },
9712     
9713     onChange : function(e)
9714     {
9715         var v = this.getValue();
9716         if(String(v) !== String(this.startValue)){
9717             this.fireEvent('change', this, v, this.startValue);
9718         }
9719         
9720     },
9721     
9722     /**
9723      * Resets the current field value to the originally loaded value and clears any validation messages
9724      */
9725     reset : function(){
9726         this.setValue(this.originalValue);
9727         this.validate();
9728     },
9729      /**
9730      * Returns the name of the field
9731      * @return {Mixed} name The name field
9732      */
9733     getName: function(){
9734         return this.name;
9735     },
9736      /**
9737      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9738      * @return {Mixed} value The field value
9739      */
9740     getValue : function(){
9741         
9742         var v = this.inputEl().getValue();
9743         
9744         return v;
9745     },
9746     /**
9747      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9748      * @return {Mixed} value The field value
9749      */
9750     getRawValue : function(){
9751         var v = this.inputEl().getValue();
9752         
9753         return v;
9754     },
9755     
9756     /**
9757      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9758      * @param {Mixed} value The value to set
9759      */
9760     setRawValue : function(v){
9761         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9762     },
9763     
9764     selectText : function(start, end){
9765         var v = this.getRawValue();
9766         if(v.length > 0){
9767             start = start === undefined ? 0 : start;
9768             end = end === undefined ? v.length : end;
9769             var d = this.inputEl().dom;
9770             if(d.setSelectionRange){
9771                 d.setSelectionRange(start, end);
9772             }else if(d.createTextRange){
9773                 var range = d.createTextRange();
9774                 range.moveStart("character", start);
9775                 range.moveEnd("character", v.length-end);
9776                 range.select();
9777             }
9778         }
9779     },
9780     
9781     /**
9782      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9783      * @param {Mixed} value The value to set
9784      */
9785     setValue : function(v){
9786         this.value = v;
9787         if(this.rendered){
9788             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9789             this.validate();
9790         }
9791     },
9792     
9793     /*
9794     processValue : function(value){
9795         if(this.stripCharsRe){
9796             var newValue = value.replace(this.stripCharsRe, '');
9797             if(newValue !== value){
9798                 this.setRawValue(newValue);
9799                 return newValue;
9800             }
9801         }
9802         return value;
9803     },
9804   */
9805     preFocus : function(){
9806         
9807         if(this.selectOnFocus){
9808             this.inputEl().dom.select();
9809         }
9810     },
9811     filterKeys : function(e){
9812         var k = e.getKey();
9813         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9814             return;
9815         }
9816         var c = e.getCharCode(), cc = String.fromCharCode(c);
9817         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9818             return;
9819         }
9820         if(!this.maskRe.test(cc)){
9821             e.stopEvent();
9822         }
9823     },
9824      /**
9825      * Clear any invalid styles/messages for this field
9826      */
9827     clearInvalid : function(){
9828         
9829         if(!this.el || this.preventMark){ // not rendered
9830             return;
9831         }
9832         
9833         
9834         this.el.removeClass([this.invalidClass, 'is-invalid']);
9835         
9836         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9837             
9838             var feedback = this.el.select('.form-control-feedback', true).first();
9839             
9840             if(feedback){
9841                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9842             }
9843             
9844         }
9845         
9846         if(this.indicator){
9847             this.indicator.removeClass('visible');
9848             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9849         }
9850         
9851         this.fireEvent('valid', this);
9852     },
9853     
9854      /**
9855      * Mark this field as valid
9856      */
9857     markValid : function()
9858     {
9859         if(!this.el  || this.preventMark){ // not rendered...
9860             return;
9861         }
9862         
9863         this.el.removeClass([this.invalidClass, this.validClass]);
9864         this.inputEl().removeClass(['is-valid', 'is-invalid']);
9865
9866         var feedback = this.el.select('.form-control-feedback', true).first();
9867             
9868         if(feedback){
9869             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9870         }
9871         
9872         if(this.indicator){
9873             this.indicator.removeClass('visible');
9874             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9875         }
9876         
9877         if(this.disabled){
9878             return;
9879         }
9880         
9881         if(this.allowBlank && !this.getRawValue().length){
9882             return;
9883         }
9884         if (Roo.bootstrap.version == 3) {
9885             this.el.addClass(this.validClass);
9886         } else {
9887             this.inputEl().addClass('is-valid');
9888         }
9889
9890         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9891             
9892             var feedback = this.el.select('.form-control-feedback', true).first();
9893             
9894             if(feedback){
9895                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9896                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9897             }
9898             
9899         }
9900         
9901         this.fireEvent('valid', this);
9902     },
9903     
9904      /**
9905      * Mark this field as invalid
9906      * @param {String} msg The validation message
9907      */
9908     markInvalid : function(msg)
9909     {
9910         if(!this.el  || this.preventMark){ // not rendered
9911             return;
9912         }
9913         
9914         this.el.removeClass([this.invalidClass, this.validClass]);
9915         this.inputEl().removeClass(['is-valid', 'is-invalid']);
9916         
9917         var feedback = this.el.select('.form-control-feedback', true).first();
9918             
9919         if(feedback){
9920             this.el.select('.form-control-feedback', true).first().removeClass(
9921                     [this.invalidFeedbackClass, this.validFeedbackClass]);
9922         }
9923
9924         if(this.disabled){
9925             return;
9926         }
9927         
9928         if(this.allowBlank && !this.getRawValue().length){
9929             return;
9930         }
9931         
9932         if(this.indicator){
9933             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9934             this.indicator.addClass('visible');
9935         }
9936         if (Roo.bootstrap.version == 3) {
9937             this.el.addClass(this.invalidClass);
9938         } else {
9939             this.inputEl().addClass('is-invalid');
9940         }
9941         
9942         
9943         
9944         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9945             
9946             var feedback = this.el.select('.form-control-feedback', true).first();
9947             
9948             if(feedback){
9949                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9950                 
9951                 if(this.getValue().length || this.forceFeedback){
9952                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9953                 }
9954                 
9955             }
9956             
9957         }
9958         
9959         this.fireEvent('invalid', this, msg);
9960     },
9961     // private
9962     SafariOnKeyDown : function(event)
9963     {
9964         // this is a workaround for a password hang bug on chrome/ webkit.
9965         if (this.inputEl().dom.type != 'password') {
9966             return;
9967         }
9968         
9969         var isSelectAll = false;
9970         
9971         if(this.inputEl().dom.selectionEnd > 0){
9972             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9973         }
9974         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9975             event.preventDefault();
9976             this.setValue('');
9977             return;
9978         }
9979         
9980         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9981             
9982             event.preventDefault();
9983             // this is very hacky as keydown always get's upper case.
9984             //
9985             var cc = String.fromCharCode(event.getCharCode());
9986             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9987             
9988         }
9989     },
9990     adjustWidth : function(tag, w){
9991         tag = tag.toLowerCase();
9992         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9993             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9994                 if(tag == 'input'){
9995                     return w + 2;
9996                 }
9997                 if(tag == 'textarea'){
9998                     return w-2;
9999                 }
10000             }else if(Roo.isOpera){
10001                 if(tag == 'input'){
10002                     return w + 2;
10003                 }
10004                 if(tag == 'textarea'){
10005                     return w-2;
10006                 }
10007             }
10008         }
10009         return w;
10010     },
10011     
10012     setFieldLabel : function(v)
10013     {
10014         if(!this.rendered){
10015             return;
10016         }
10017         
10018         if(this.indicatorEl()){
10019             var ar = this.el.select('label > span',true);
10020             
10021             if (ar.elements.length) {
10022                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10023                 this.fieldLabel = v;
10024                 return;
10025             }
10026             
10027             var br = this.el.select('label',true);
10028             
10029             if(br.elements.length) {
10030                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10031                 this.fieldLabel = v;
10032                 return;
10033             }
10034             
10035             Roo.log('Cannot Found any of label > span || label in input');
10036             return;
10037         }
10038         
10039         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10040         this.fieldLabel = v;
10041         
10042         
10043     }
10044 });
10045
10046  
10047 /*
10048  * - LGPL
10049  *
10050  * Input
10051  * 
10052  */
10053
10054 /**
10055  * @class Roo.bootstrap.TextArea
10056  * @extends Roo.bootstrap.Input
10057  * Bootstrap TextArea class
10058  * @cfg {Number} cols Specifies the visible width of a text area
10059  * @cfg {Number} rows Specifies the visible number of lines in a text area
10060  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
10061  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
10062  * @cfg {string} html text
10063  * 
10064  * @constructor
10065  * Create a new TextArea
10066  * @param {Object} config The config object
10067  */
10068
10069 Roo.bootstrap.TextArea = function(config){
10070     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
10071    
10072 };
10073
10074 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
10075      
10076     cols : false,
10077     rows : 5,
10078     readOnly : false,
10079     warp : 'soft',
10080     resize : false,
10081     value: false,
10082     html: false,
10083     
10084     getAutoCreate : function(){
10085         
10086         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10087         
10088         var id = Roo.id();
10089         
10090         var cfg = {};
10091         
10092         if(this.inputType != 'hidden'){
10093             cfg.cls = 'form-group' //input-group
10094         }
10095         
10096         var input =  {
10097             tag: 'textarea',
10098             id : id,
10099             warp : this.warp,
10100             rows : this.rows,
10101             value : this.value || '',
10102             html: this.html || '',
10103             cls : 'form-control',
10104             placeholder : this.placeholder || '' 
10105             
10106         };
10107         
10108         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10109             input.maxLength = this.maxLength;
10110         }
10111         
10112         if(this.resize){
10113             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
10114         }
10115         
10116         if(this.cols){
10117             input.cols = this.cols;
10118         }
10119         
10120         if (this.readOnly) {
10121             input.readonly = true;
10122         }
10123         
10124         if (this.name) {
10125             input.name = this.name;
10126         }
10127         
10128         if (this.size) {
10129             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
10130         }
10131         
10132         var settings=this;
10133         ['xs','sm','md','lg'].map(function(size){
10134             if (settings[size]) {
10135                 cfg.cls += ' col-' + size + '-' + settings[size];
10136             }
10137         });
10138         
10139         var inputblock = input;
10140         
10141         if(this.hasFeedback && !this.allowBlank){
10142             
10143             var feedback = {
10144                 tag: 'span',
10145                 cls: 'glyphicon form-control-feedback'
10146             };
10147
10148             inputblock = {
10149                 cls : 'has-feedback',
10150                 cn :  [
10151                     input,
10152                     feedback
10153                 ] 
10154             };  
10155         }
10156         
10157         
10158         if (this.before || this.after) {
10159             
10160             inputblock = {
10161                 cls : 'input-group',
10162                 cn :  [] 
10163             };
10164             if (this.before) {
10165                 inputblock.cn.push({
10166                     tag :'span',
10167                     cls : 'input-group-addon',
10168                     html : this.before
10169                 });
10170             }
10171             
10172             inputblock.cn.push(input);
10173             
10174             if(this.hasFeedback && !this.allowBlank){
10175                 inputblock.cls += ' has-feedback';
10176                 inputblock.cn.push(feedback);
10177             }
10178             
10179             if (this.after) {
10180                 inputblock.cn.push({
10181                     tag :'span',
10182                     cls : 'input-group-addon',
10183                     html : this.after
10184                 });
10185             }
10186             
10187         }
10188         
10189         if (align ==='left' && this.fieldLabel.length) {
10190             cfg.cn = [
10191                 {
10192                     tag: 'label',
10193                     'for' :  id,
10194                     cls : 'control-label',
10195                     html : this.fieldLabel
10196                 },
10197                 {
10198                     cls : "",
10199                     cn: [
10200                         inputblock
10201                     ]
10202                 }
10203
10204             ];
10205             
10206             if(this.labelWidth > 12){
10207                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10208             }
10209
10210             if(this.labelWidth < 13 && this.labelmd == 0){
10211                 this.labelmd = this.labelWidth;
10212             }
10213
10214             if(this.labellg > 0){
10215                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10216                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10217             }
10218
10219             if(this.labelmd > 0){
10220                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10221                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10222             }
10223
10224             if(this.labelsm > 0){
10225                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10226                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10227             }
10228
10229             if(this.labelxs > 0){
10230                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10231                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10232             }
10233             
10234         } else if ( this.fieldLabel.length) {
10235             cfg.cn = [
10236
10237                {
10238                    tag: 'label',
10239                    //cls : 'input-group-addon',
10240                    html : this.fieldLabel
10241
10242                },
10243
10244                inputblock
10245
10246            ];
10247
10248         } else {
10249
10250             cfg.cn = [
10251
10252                 inputblock
10253
10254             ];
10255                 
10256         }
10257         
10258         if (this.disabled) {
10259             input.disabled=true;
10260         }
10261         
10262         return cfg;
10263         
10264     },
10265     /**
10266      * return the real textarea element.
10267      */
10268     inputEl: function ()
10269     {
10270         return this.el.select('textarea.form-control',true).first();
10271     },
10272     
10273     /**
10274      * Clear any invalid styles/messages for this field
10275      */
10276     clearInvalid : function()
10277     {
10278         
10279         if(!this.el || this.preventMark){ // not rendered
10280             return;
10281         }
10282         
10283         var label = this.el.select('label', true).first();
10284         var icon = this.el.select('i.fa-star', true).first();
10285         
10286         if(label && icon){
10287             icon.remove();
10288         }
10289         this.el.removeClass( this.validClass);
10290         this.inputEl().removeClass('is-invalid');
10291          
10292         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10293             
10294             var feedback = this.el.select('.form-control-feedback', true).first();
10295             
10296             if(feedback){
10297                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10298             }
10299             
10300         }
10301         
10302         this.fireEvent('valid', this);
10303     },
10304     
10305      /**
10306      * Mark this field as valid
10307      */
10308     markValid : function()
10309     {
10310         if(!this.el  || this.preventMark){ // not rendered
10311             return;
10312         }
10313         
10314         this.el.removeClass([this.invalidClass, this.validClass]);
10315         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10316         
10317         var feedback = this.el.select('.form-control-feedback', true).first();
10318             
10319         if(feedback){
10320             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10321         }
10322
10323         if(this.disabled || this.allowBlank){
10324             return;
10325         }
10326         
10327         var label = this.el.select('label', true).first();
10328         var icon = this.el.select('i.fa-star', true).first();
10329         
10330         if(label && icon){
10331             icon.remove();
10332         }
10333         if (Roo.bootstrap.version == 3) {
10334             this.el.addClass(this.validClass);
10335         } else {
10336             this.inputEl().addClass('is-valid');
10337         }
10338         
10339         
10340         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10341             
10342             var feedback = this.el.select('.form-control-feedback', true).first();
10343             
10344             if(feedback){
10345                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10346                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10347             }
10348             
10349         }
10350         
10351         this.fireEvent('valid', this);
10352     },
10353     
10354      /**
10355      * Mark this field as invalid
10356      * @param {String} msg The validation message
10357      */
10358     markInvalid : function(msg)
10359     {
10360         if(!this.el  || this.preventMark){ // not rendered
10361             return;
10362         }
10363         
10364         this.el.removeClass([this.invalidClass, this.validClass]);
10365         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10366         
10367         var feedback = this.el.select('.form-control-feedback', true).first();
10368             
10369         if(feedback){
10370             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10371         }
10372
10373         if(this.disabled || this.allowBlank){
10374             return;
10375         }
10376         
10377         var label = this.el.select('label', true).first();
10378         var icon = this.el.select('i.fa-star', true).first();
10379         
10380         if(!this.getValue().length && label && !icon){
10381             this.el.createChild({
10382                 tag : 'i',
10383                 cls : 'text-danger fa fa-lg fa-star',
10384                 tooltip : 'This field is required',
10385                 style : 'margin-right:5px;'
10386             }, label, true);
10387         }
10388         
10389         if (Roo.bootstrap.version == 3) {
10390             this.el.addClass(this.invalidClass);
10391         } else {
10392             this.inputEl().addClass('is-invalid');
10393         }
10394         
10395         // fixme ... this may be depricated need to test..
10396         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10397             
10398             var feedback = this.el.select('.form-control-feedback', true).first();
10399             
10400             if(feedback){
10401                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10402                 
10403                 if(this.getValue().length || this.forceFeedback){
10404                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10405                 }
10406                 
10407             }
10408             
10409         }
10410         
10411         this.fireEvent('invalid', this, msg);
10412     }
10413 });
10414
10415  
10416 /*
10417  * - LGPL
10418  *
10419  * trigger field - base class for combo..
10420  * 
10421  */
10422  
10423 /**
10424  * @class Roo.bootstrap.TriggerField
10425  * @extends Roo.bootstrap.Input
10426  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10427  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10428  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10429  * for which you can provide a custom implementation.  For example:
10430  * <pre><code>
10431 var trigger = new Roo.bootstrap.TriggerField();
10432 trigger.onTriggerClick = myTriggerFn;
10433 trigger.applyTo('my-field');
10434 </code></pre>
10435  *
10436  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10437  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10438  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10439  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10440  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10441
10442  * @constructor
10443  * Create a new TriggerField.
10444  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10445  * to the base TextField)
10446  */
10447 Roo.bootstrap.TriggerField = function(config){
10448     this.mimicing = false;
10449     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10450 };
10451
10452 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10453     /**
10454      * @cfg {String} triggerClass A CSS class to apply to the trigger
10455      */
10456      /**
10457      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10458      */
10459     hideTrigger:false,
10460
10461     /**
10462      * @cfg {Boolean} removable (true|false) special filter default false
10463      */
10464     removable : false,
10465     
10466     /** @cfg {Boolean} grow @hide */
10467     /** @cfg {Number} growMin @hide */
10468     /** @cfg {Number} growMax @hide */
10469
10470     /**
10471      * @hide 
10472      * @method
10473      */
10474     autoSize: Roo.emptyFn,
10475     // private
10476     monitorTab : true,
10477     // private
10478     deferHeight : true,
10479
10480     
10481     actionMode : 'wrap',
10482     
10483     caret : false,
10484     
10485     
10486     getAutoCreate : function(){
10487        
10488         var align = this.labelAlign || this.parentLabelAlign();
10489         
10490         var id = Roo.id();
10491         
10492         var cfg = {
10493             cls: 'form-group' //input-group
10494         };
10495         
10496         
10497         var input =  {
10498             tag: 'input',
10499             id : id,
10500             type : this.inputType,
10501             cls : 'form-control',
10502             autocomplete: 'new-password',
10503             placeholder : this.placeholder || '' 
10504             
10505         };
10506         if (this.name) {
10507             input.name = this.name;
10508         }
10509         if (this.size) {
10510             input.cls += ' input-' + this.size;
10511         }
10512         
10513         if (this.disabled) {
10514             input.disabled=true;
10515         }
10516         
10517         var inputblock = input;
10518         
10519         if(this.hasFeedback && !this.allowBlank){
10520             
10521             var feedback = {
10522                 tag: 'span',
10523                 cls: 'glyphicon form-control-feedback'
10524             };
10525             
10526             if(this.removable && !this.editable && !this.tickable){
10527                 inputblock = {
10528                     cls : 'has-feedback',
10529                     cn :  [
10530                         inputblock,
10531                         {
10532                             tag: 'button',
10533                             html : 'x',
10534                             cls : 'roo-combo-removable-btn close'
10535                         },
10536                         feedback
10537                     ] 
10538                 };
10539             } else {
10540                 inputblock = {
10541                     cls : 'has-feedback',
10542                     cn :  [
10543                         inputblock,
10544                         feedback
10545                     ] 
10546                 };
10547             }
10548
10549         } else {
10550             if(this.removable && !this.editable && !this.tickable){
10551                 inputblock = {
10552                     cls : 'roo-removable',
10553                     cn :  [
10554                         inputblock,
10555                         {
10556                             tag: 'button',
10557                             html : 'x',
10558                             cls : 'roo-combo-removable-btn close'
10559                         }
10560                     ] 
10561                 };
10562             }
10563         }
10564         
10565         if (this.before || this.after) {
10566             
10567             inputblock = {
10568                 cls : 'input-group',
10569                 cn :  [] 
10570             };
10571             if (this.before) {
10572                 inputblock.cn.push({
10573                     tag :'span',
10574                     cls : 'input-group-addon input-group-prepend input-group-text',
10575                     html : this.before
10576                 });
10577             }
10578             
10579             inputblock.cn.push(input);
10580             
10581             if(this.hasFeedback && !this.allowBlank){
10582                 inputblock.cls += ' has-feedback';
10583                 inputblock.cn.push(feedback);
10584             }
10585             
10586             if (this.after) {
10587                 inputblock.cn.push({
10588                     tag :'span',
10589                     cls : 'input-group-addon input-group-append input-group-text',
10590                     html : this.after
10591                 });
10592             }
10593             
10594         };
10595         
10596       
10597         
10598         var ibwrap = inputblock;
10599         
10600         if(this.multiple){
10601             ibwrap = {
10602                 tag: 'ul',
10603                 cls: 'roo-select2-choices',
10604                 cn:[
10605                     {
10606                         tag: 'li',
10607                         cls: 'roo-select2-search-field',
10608                         cn: [
10609
10610                             inputblock
10611                         ]
10612                     }
10613                 ]
10614             };
10615                 
10616         }
10617         
10618         var combobox = {
10619             cls: 'roo-select2-container input-group',
10620             cn: [
10621                  {
10622                     tag: 'input',
10623                     type : 'hidden',
10624                     cls: 'form-hidden-field'
10625                 },
10626                 ibwrap
10627             ]
10628         };
10629         
10630         if(!this.multiple && this.showToggleBtn){
10631             
10632             var caret = {
10633                         tag: 'span',
10634                         cls: 'caret'
10635              };
10636             if (this.caret != false) {
10637                 caret = {
10638                      tag: 'i',
10639                      cls: 'fa fa-' + this.caret
10640                 };
10641                 
10642             }
10643             
10644             combobox.cn.push({
10645                 tag :'span',
10646                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10647                 cn : [
10648                     caret,
10649                     {
10650                         tag: 'span',
10651                         cls: 'combobox-clear',
10652                         cn  : [
10653                             {
10654                                 tag : 'i',
10655                                 cls: 'icon-remove'
10656                             }
10657                         ]
10658                     }
10659                 ]
10660
10661             })
10662         }
10663         
10664         if(this.multiple){
10665             combobox.cls += ' roo-select2-container-multi';
10666         }
10667          var indicator = {
10668             tag : 'i',
10669             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10670             tooltip : 'This field is required'
10671         };
10672         if (Roo.bootstrap.version == 4) {
10673             indicator = {
10674                 tag : 'i',
10675                 style : 'display:none'
10676             };
10677         }
10678         
10679         
10680         if (align ==='left' && this.fieldLabel.length) {
10681             
10682             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10683
10684             cfg.cn = [
10685                 indicator,
10686                 {
10687                     tag: 'label',
10688                     'for' :  id,
10689                     cls : 'control-label',
10690                     html : this.fieldLabel
10691
10692                 },
10693                 {
10694                     cls : "", 
10695                     cn: [
10696                         combobox
10697                     ]
10698                 }
10699
10700             ];
10701             
10702             var labelCfg = cfg.cn[1];
10703             var contentCfg = cfg.cn[2];
10704             
10705             if(this.indicatorpos == 'right'){
10706                 cfg.cn = [
10707                     {
10708                         tag: 'label',
10709                         'for' :  id,
10710                         cls : 'control-label',
10711                         cn : [
10712                             {
10713                                 tag : 'span',
10714                                 html : this.fieldLabel
10715                             },
10716                             indicator
10717                         ]
10718                     },
10719                     {
10720                         cls : "", 
10721                         cn: [
10722                             combobox
10723                         ]
10724                     }
10725
10726                 ];
10727                 
10728                 labelCfg = cfg.cn[0];
10729                 contentCfg = cfg.cn[1];
10730             }
10731             
10732             if(this.labelWidth > 12){
10733                 labelCfg.style = "width: " + this.labelWidth + 'px';
10734             }
10735             
10736             if(this.labelWidth < 13 && this.labelmd == 0){
10737                 this.labelmd = this.labelWidth;
10738             }
10739             
10740             if(this.labellg > 0){
10741                 labelCfg.cls += ' col-lg-' + this.labellg;
10742                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10743             }
10744             
10745             if(this.labelmd > 0){
10746                 labelCfg.cls += ' col-md-' + this.labelmd;
10747                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10748             }
10749             
10750             if(this.labelsm > 0){
10751                 labelCfg.cls += ' col-sm-' + this.labelsm;
10752                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10753             }
10754             
10755             if(this.labelxs > 0){
10756                 labelCfg.cls += ' col-xs-' + this.labelxs;
10757                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10758             }
10759             
10760         } else if ( this.fieldLabel.length) {
10761 //                Roo.log(" label");
10762             cfg.cn = [
10763                 indicator,
10764                {
10765                    tag: 'label',
10766                    //cls : 'input-group-addon',
10767                    html : this.fieldLabel
10768
10769                },
10770
10771                combobox
10772
10773             ];
10774             
10775             if(this.indicatorpos == 'right'){
10776                 
10777                 cfg.cn = [
10778                     {
10779                        tag: 'label',
10780                        cn : [
10781                            {
10782                                tag : 'span',
10783                                html : this.fieldLabel
10784                            },
10785                            indicator
10786                        ]
10787
10788                     },
10789                     combobox
10790
10791                 ];
10792
10793             }
10794
10795         } else {
10796             
10797 //                Roo.log(" no label && no align");
10798                 cfg = combobox
10799                      
10800                 
10801         }
10802         
10803         var settings=this;
10804         ['xs','sm','md','lg'].map(function(size){
10805             if (settings[size]) {
10806                 cfg.cls += ' col-' + size + '-' + settings[size];
10807             }
10808         });
10809         
10810         return cfg;
10811         
10812     },
10813     
10814     
10815     
10816     // private
10817     onResize : function(w, h){
10818 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10819 //        if(typeof w == 'number'){
10820 //            var x = w - this.trigger.getWidth();
10821 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10822 //            this.trigger.setStyle('left', x+'px');
10823 //        }
10824     },
10825
10826     // private
10827     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10828
10829     // private
10830     getResizeEl : function(){
10831         return this.inputEl();
10832     },
10833
10834     // private
10835     getPositionEl : function(){
10836         return this.inputEl();
10837     },
10838
10839     // private
10840     alignErrorIcon : function(){
10841         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10842     },
10843
10844     // private
10845     initEvents : function(){
10846         
10847         this.createList();
10848         
10849         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10850         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10851         if(!this.multiple && this.showToggleBtn){
10852             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10853             if(this.hideTrigger){
10854                 this.trigger.setDisplayed(false);
10855             }
10856             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10857         }
10858         
10859         if(this.multiple){
10860             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10861         }
10862         
10863         if(this.removable && !this.editable && !this.tickable){
10864             var close = this.closeTriggerEl();
10865             
10866             if(close){
10867                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10868                 close.on('click', this.removeBtnClick, this, close);
10869             }
10870         }
10871         
10872         //this.trigger.addClassOnOver('x-form-trigger-over');
10873         //this.trigger.addClassOnClick('x-form-trigger-click');
10874         
10875         //if(!this.width){
10876         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10877         //}
10878     },
10879     
10880     closeTriggerEl : function()
10881     {
10882         var close = this.el.select('.roo-combo-removable-btn', true).first();
10883         return close ? close : false;
10884     },
10885     
10886     removeBtnClick : function(e, h, el)
10887     {
10888         e.preventDefault();
10889         
10890         if(this.fireEvent("remove", this) !== false){
10891             this.reset();
10892             this.fireEvent("afterremove", this)
10893         }
10894     },
10895     
10896     createList : function()
10897     {
10898         this.list = Roo.get(document.body).createChild({
10899             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10900             cls: 'typeahead typeahead-long dropdown-menu',
10901             style: 'display:none'
10902         });
10903         
10904         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10905         
10906     },
10907
10908     // private
10909     initTrigger : function(){
10910        
10911     },
10912
10913     // private
10914     onDestroy : function(){
10915         if(this.trigger){
10916             this.trigger.removeAllListeners();
10917           //  this.trigger.remove();
10918         }
10919         //if(this.wrap){
10920         //    this.wrap.remove();
10921         //}
10922         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10923     },
10924
10925     // private
10926     onFocus : function(){
10927         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10928         /*
10929         if(!this.mimicing){
10930             this.wrap.addClass('x-trigger-wrap-focus');
10931             this.mimicing = true;
10932             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10933             if(this.monitorTab){
10934                 this.el.on("keydown", this.checkTab, this);
10935             }
10936         }
10937         */
10938     },
10939
10940     // private
10941     checkTab : function(e){
10942         if(e.getKey() == e.TAB){
10943             this.triggerBlur();
10944         }
10945     },
10946
10947     // private
10948     onBlur : function(){
10949         // do nothing
10950     },
10951
10952     // private
10953     mimicBlur : function(e, t){
10954         /*
10955         if(!this.wrap.contains(t) && this.validateBlur()){
10956             this.triggerBlur();
10957         }
10958         */
10959     },
10960
10961     // private
10962     triggerBlur : function(){
10963         this.mimicing = false;
10964         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10965         if(this.monitorTab){
10966             this.el.un("keydown", this.checkTab, this);
10967         }
10968         //this.wrap.removeClass('x-trigger-wrap-focus');
10969         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10970     },
10971
10972     // private
10973     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10974     validateBlur : function(e, t){
10975         return true;
10976     },
10977
10978     // private
10979     onDisable : function(){
10980         this.inputEl().dom.disabled = true;
10981         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10982         //if(this.wrap){
10983         //    this.wrap.addClass('x-item-disabled');
10984         //}
10985     },
10986
10987     // private
10988     onEnable : function(){
10989         this.inputEl().dom.disabled = false;
10990         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10991         //if(this.wrap){
10992         //    this.el.removeClass('x-item-disabled');
10993         //}
10994     },
10995
10996     // private
10997     onShow : function(){
10998         var ae = this.getActionEl();
10999         
11000         if(ae){
11001             ae.dom.style.display = '';
11002             ae.dom.style.visibility = 'visible';
11003         }
11004     },
11005
11006     // private
11007     
11008     onHide : function(){
11009         var ae = this.getActionEl();
11010         ae.dom.style.display = 'none';
11011     },
11012
11013     /**
11014      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
11015      * by an implementing function.
11016      * @method
11017      * @param {EventObject} e
11018      */
11019     onTriggerClick : Roo.emptyFn
11020 });
11021  /*
11022  * Based on:
11023  * Ext JS Library 1.1.1
11024  * Copyright(c) 2006-2007, Ext JS, LLC.
11025  *
11026  * Originally Released Under LGPL - original licence link has changed is not relivant.
11027  *
11028  * Fork - LGPL
11029  * <script type="text/javascript">
11030  */
11031
11032
11033 /**
11034  * @class Roo.data.SortTypes
11035  * @singleton
11036  * Defines the default sorting (casting?) comparison functions used when sorting data.
11037  */
11038 Roo.data.SortTypes = {
11039     /**
11040      * Default sort that does nothing
11041      * @param {Mixed} s The value being converted
11042      * @return {Mixed} The comparison value
11043      */
11044     none : function(s){
11045         return s;
11046     },
11047     
11048     /**
11049      * The regular expression used to strip tags
11050      * @type {RegExp}
11051      * @property
11052      */
11053     stripTagsRE : /<\/?[^>]+>/gi,
11054     
11055     /**
11056      * Strips all HTML tags to sort on text only
11057      * @param {Mixed} s The value being converted
11058      * @return {String} The comparison value
11059      */
11060     asText : function(s){
11061         return String(s).replace(this.stripTagsRE, "");
11062     },
11063     
11064     /**
11065      * Strips all HTML tags to sort on text only - Case insensitive
11066      * @param {Mixed} s The value being converted
11067      * @return {String} The comparison value
11068      */
11069     asUCText : function(s){
11070         return String(s).toUpperCase().replace(this.stripTagsRE, "");
11071     },
11072     
11073     /**
11074      * Case insensitive string
11075      * @param {Mixed} s The value being converted
11076      * @return {String} The comparison value
11077      */
11078     asUCString : function(s) {
11079         return String(s).toUpperCase();
11080     },
11081     
11082     /**
11083      * Date sorting
11084      * @param {Mixed} s The value being converted
11085      * @return {Number} The comparison value
11086      */
11087     asDate : function(s) {
11088         if(!s){
11089             return 0;
11090         }
11091         if(s instanceof Date){
11092             return s.getTime();
11093         }
11094         return Date.parse(String(s));
11095     },
11096     
11097     /**
11098      * Float sorting
11099      * @param {Mixed} s The value being converted
11100      * @return {Float} The comparison value
11101      */
11102     asFloat : function(s) {
11103         var val = parseFloat(String(s).replace(/,/g, ""));
11104         if(isNaN(val)) {
11105             val = 0;
11106         }
11107         return val;
11108     },
11109     
11110     /**
11111      * Integer sorting
11112      * @param {Mixed} s The value being converted
11113      * @return {Number} The comparison value
11114      */
11115     asInt : function(s) {
11116         var val = parseInt(String(s).replace(/,/g, ""));
11117         if(isNaN(val)) {
11118             val = 0;
11119         }
11120         return val;
11121     }
11122 };/*
11123  * Based on:
11124  * Ext JS Library 1.1.1
11125  * Copyright(c) 2006-2007, Ext JS, LLC.
11126  *
11127  * Originally Released Under LGPL - original licence link has changed is not relivant.
11128  *
11129  * Fork - LGPL
11130  * <script type="text/javascript">
11131  */
11132
11133 /**
11134 * @class Roo.data.Record
11135  * Instances of this class encapsulate both record <em>definition</em> information, and record
11136  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
11137  * to access Records cached in an {@link Roo.data.Store} object.<br>
11138  * <p>
11139  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
11140  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
11141  * objects.<br>
11142  * <p>
11143  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
11144  * @constructor
11145  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
11146  * {@link #create}. The parameters are the same.
11147  * @param {Array} data An associative Array of data values keyed by the field name.
11148  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
11149  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
11150  * not specified an integer id is generated.
11151  */
11152 Roo.data.Record = function(data, id){
11153     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11154     this.data = data;
11155 };
11156
11157 /**
11158  * Generate a constructor for a specific record layout.
11159  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11160  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11161  * Each field definition object may contain the following properties: <ul>
11162  * <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,
11163  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11164  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11165  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11166  * is being used, then this is a string containing the javascript expression to reference the data relative to 
11167  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11168  * to the data item relative to the record element. If the mapping expression is the same as the field name,
11169  * this may be omitted.</p></li>
11170  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11171  * <ul><li>auto (Default, implies no conversion)</li>
11172  * <li>string</li>
11173  * <li>int</li>
11174  * <li>float</li>
11175  * <li>boolean</li>
11176  * <li>date</li></ul></p></li>
11177  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11178  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11179  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11180  * by the Reader into an object that will be stored in the Record. It is passed the
11181  * following parameters:<ul>
11182  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11183  * </ul></p></li>
11184  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11185  * </ul>
11186  * <br>usage:<br><pre><code>
11187 var TopicRecord = Roo.data.Record.create(
11188     {name: 'title', mapping: 'topic_title'},
11189     {name: 'author', mapping: 'username'},
11190     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11191     {name: 'lastPost', mapping: 'post_time', type: 'date'},
11192     {name: 'lastPoster', mapping: 'user2'},
11193     {name: 'excerpt', mapping: 'post_text'}
11194 );
11195
11196 var myNewRecord = new TopicRecord({
11197     title: 'Do my job please',
11198     author: 'noobie',
11199     totalPosts: 1,
11200     lastPost: new Date(),
11201     lastPoster: 'Animal',
11202     excerpt: 'No way dude!'
11203 });
11204 myStore.add(myNewRecord);
11205 </code></pre>
11206  * @method create
11207  * @static
11208  */
11209 Roo.data.Record.create = function(o){
11210     var f = function(){
11211         f.superclass.constructor.apply(this, arguments);
11212     };
11213     Roo.extend(f, Roo.data.Record);
11214     var p = f.prototype;
11215     p.fields = new Roo.util.MixedCollection(false, function(field){
11216         return field.name;
11217     });
11218     for(var i = 0, len = o.length; i < len; i++){
11219         p.fields.add(new Roo.data.Field(o[i]));
11220     }
11221     f.getField = function(name){
11222         return p.fields.get(name);  
11223     };
11224     return f;
11225 };
11226
11227 Roo.data.Record.AUTO_ID = 1000;
11228 Roo.data.Record.EDIT = 'edit';
11229 Roo.data.Record.REJECT = 'reject';
11230 Roo.data.Record.COMMIT = 'commit';
11231
11232 Roo.data.Record.prototype = {
11233     /**
11234      * Readonly flag - true if this record has been modified.
11235      * @type Boolean
11236      */
11237     dirty : false,
11238     editing : false,
11239     error: null,
11240     modified: null,
11241
11242     // private
11243     join : function(store){
11244         this.store = store;
11245     },
11246
11247     /**
11248      * Set the named field to the specified value.
11249      * @param {String} name The name of the field to set.
11250      * @param {Object} value The value to set the field to.
11251      */
11252     set : function(name, value){
11253         if(this.data[name] == value){
11254             return;
11255         }
11256         this.dirty = true;
11257         if(!this.modified){
11258             this.modified = {};
11259         }
11260         if(typeof this.modified[name] == 'undefined'){
11261             this.modified[name] = this.data[name];
11262         }
11263         this.data[name] = value;
11264         if(!this.editing && this.store){
11265             this.store.afterEdit(this);
11266         }       
11267     },
11268
11269     /**
11270      * Get the value of the named field.
11271      * @param {String} name The name of the field to get the value of.
11272      * @return {Object} The value of the field.
11273      */
11274     get : function(name){
11275         return this.data[name]; 
11276     },
11277
11278     // private
11279     beginEdit : function(){
11280         this.editing = true;
11281         this.modified = {}; 
11282     },
11283
11284     // private
11285     cancelEdit : function(){
11286         this.editing = false;
11287         delete this.modified;
11288     },
11289
11290     // private
11291     endEdit : function(){
11292         this.editing = false;
11293         if(this.dirty && this.store){
11294             this.store.afterEdit(this);
11295         }
11296     },
11297
11298     /**
11299      * Usually called by the {@link Roo.data.Store} which owns the Record.
11300      * Rejects all changes made to the Record since either creation, or the last commit operation.
11301      * Modified fields are reverted to their original values.
11302      * <p>
11303      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11304      * of reject operations.
11305      */
11306     reject : function(){
11307         var m = this.modified;
11308         for(var n in m){
11309             if(typeof m[n] != "function"){
11310                 this.data[n] = m[n];
11311             }
11312         }
11313         this.dirty = false;
11314         delete this.modified;
11315         this.editing = false;
11316         if(this.store){
11317             this.store.afterReject(this);
11318         }
11319     },
11320
11321     /**
11322      * Usually called by the {@link Roo.data.Store} which owns the Record.
11323      * Commits all changes made to the Record since either creation, or the last commit operation.
11324      * <p>
11325      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11326      * of commit operations.
11327      */
11328     commit : function(){
11329         this.dirty = false;
11330         delete this.modified;
11331         this.editing = false;
11332         if(this.store){
11333             this.store.afterCommit(this);
11334         }
11335     },
11336
11337     // private
11338     hasError : function(){
11339         return this.error != null;
11340     },
11341
11342     // private
11343     clearError : function(){
11344         this.error = null;
11345     },
11346
11347     /**
11348      * Creates a copy of this record.
11349      * @param {String} id (optional) A new record id if you don't want to use this record's id
11350      * @return {Record}
11351      */
11352     copy : function(newId) {
11353         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11354     }
11355 };/*
11356  * Based on:
11357  * Ext JS Library 1.1.1
11358  * Copyright(c) 2006-2007, Ext JS, LLC.
11359  *
11360  * Originally Released Under LGPL - original licence link has changed is not relivant.
11361  *
11362  * Fork - LGPL
11363  * <script type="text/javascript">
11364  */
11365
11366
11367
11368 /**
11369  * @class Roo.data.Store
11370  * @extends Roo.util.Observable
11371  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11372  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11373  * <p>
11374  * 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
11375  * has no knowledge of the format of the data returned by the Proxy.<br>
11376  * <p>
11377  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11378  * instances from the data object. These records are cached and made available through accessor functions.
11379  * @constructor
11380  * Creates a new Store.
11381  * @param {Object} config A config object containing the objects needed for the Store to access data,
11382  * and read the data into Records.
11383  */
11384 Roo.data.Store = function(config){
11385     this.data = new Roo.util.MixedCollection(false);
11386     this.data.getKey = function(o){
11387         return o.id;
11388     };
11389     this.baseParams = {};
11390     // private
11391     this.paramNames = {
11392         "start" : "start",
11393         "limit" : "limit",
11394         "sort" : "sort",
11395         "dir" : "dir",
11396         "multisort" : "_multisort"
11397     };
11398
11399     if(config && config.data){
11400         this.inlineData = config.data;
11401         delete config.data;
11402     }
11403
11404     Roo.apply(this, config);
11405     
11406     if(this.reader){ // reader passed
11407         this.reader = Roo.factory(this.reader, Roo.data);
11408         this.reader.xmodule = this.xmodule || false;
11409         if(!this.recordType){
11410             this.recordType = this.reader.recordType;
11411         }
11412         if(this.reader.onMetaChange){
11413             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11414         }
11415     }
11416
11417     if(this.recordType){
11418         this.fields = this.recordType.prototype.fields;
11419     }
11420     this.modified = [];
11421
11422     this.addEvents({
11423         /**
11424          * @event datachanged
11425          * Fires when the data cache has changed, and a widget which is using this Store
11426          * as a Record cache should refresh its view.
11427          * @param {Store} this
11428          */
11429         datachanged : true,
11430         /**
11431          * @event metachange
11432          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11433          * @param {Store} this
11434          * @param {Object} meta The JSON metadata
11435          */
11436         metachange : true,
11437         /**
11438          * @event add
11439          * Fires when Records have been added to the Store
11440          * @param {Store} this
11441          * @param {Roo.data.Record[]} records The array of Records added
11442          * @param {Number} index The index at which the record(s) were added
11443          */
11444         add : true,
11445         /**
11446          * @event remove
11447          * Fires when a Record has been removed from the Store
11448          * @param {Store} this
11449          * @param {Roo.data.Record} record The Record that was removed
11450          * @param {Number} index The index at which the record was removed
11451          */
11452         remove : true,
11453         /**
11454          * @event update
11455          * Fires when a Record has been updated
11456          * @param {Store} this
11457          * @param {Roo.data.Record} record The Record that was updated
11458          * @param {String} operation The update operation being performed.  Value may be one of:
11459          * <pre><code>
11460  Roo.data.Record.EDIT
11461  Roo.data.Record.REJECT
11462  Roo.data.Record.COMMIT
11463          * </code></pre>
11464          */
11465         update : true,
11466         /**
11467          * @event clear
11468          * Fires when the data cache has been cleared.
11469          * @param {Store} this
11470          */
11471         clear : true,
11472         /**
11473          * @event beforeload
11474          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11475          * the load action will be canceled.
11476          * @param {Store} this
11477          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11478          */
11479         beforeload : true,
11480         /**
11481          * @event beforeloadadd
11482          * Fires after a new set of Records has been loaded.
11483          * @param {Store} this
11484          * @param {Roo.data.Record[]} records The Records that were loaded
11485          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11486          */
11487         beforeloadadd : true,
11488         /**
11489          * @event load
11490          * Fires after a new set of Records has been loaded, before they are added to the store.
11491          * @param {Store} this
11492          * @param {Roo.data.Record[]} records The Records that were loaded
11493          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11494          * @params {Object} return from reader
11495          */
11496         load : true,
11497         /**
11498          * @event loadexception
11499          * Fires if an exception occurs in the Proxy during loading.
11500          * Called with the signature of the Proxy's "loadexception" event.
11501          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11502          * 
11503          * @param {Proxy} 
11504          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11505          * @param {Object} load options 
11506          * @param {Object} jsonData from your request (normally this contains the Exception)
11507          */
11508         loadexception : true
11509     });
11510     
11511     if(this.proxy){
11512         this.proxy = Roo.factory(this.proxy, Roo.data);
11513         this.proxy.xmodule = this.xmodule || false;
11514         this.relayEvents(this.proxy,  ["loadexception"]);
11515     }
11516     this.sortToggle = {};
11517     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11518
11519     Roo.data.Store.superclass.constructor.call(this);
11520
11521     if(this.inlineData){
11522         this.loadData(this.inlineData);
11523         delete this.inlineData;
11524     }
11525 };
11526
11527 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11528      /**
11529     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11530     * without a remote query - used by combo/forms at present.
11531     */
11532     
11533     /**
11534     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11535     */
11536     /**
11537     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11538     */
11539     /**
11540     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11541     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11542     */
11543     /**
11544     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11545     * on any HTTP request
11546     */
11547     /**
11548     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11549     */
11550     /**
11551     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11552     */
11553     multiSort: false,
11554     /**
11555     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11556     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11557     */
11558     remoteSort : false,
11559
11560     /**
11561     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11562      * loaded or when a record is removed. (defaults to false).
11563     */
11564     pruneModifiedRecords : false,
11565
11566     // private
11567     lastOptions : null,
11568
11569     /**
11570      * Add Records to the Store and fires the add event.
11571      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11572      */
11573     add : function(records){
11574         records = [].concat(records);
11575         for(var i = 0, len = records.length; i < len; i++){
11576             records[i].join(this);
11577         }
11578         var index = this.data.length;
11579         this.data.addAll(records);
11580         this.fireEvent("add", this, records, index);
11581     },
11582
11583     /**
11584      * Remove a Record from the Store and fires the remove event.
11585      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11586      */
11587     remove : function(record){
11588         var index = this.data.indexOf(record);
11589         this.data.removeAt(index);
11590  
11591         if(this.pruneModifiedRecords){
11592             this.modified.remove(record);
11593         }
11594         this.fireEvent("remove", this, record, index);
11595     },
11596
11597     /**
11598      * Remove all Records from the Store and fires the clear event.
11599      */
11600     removeAll : function(){
11601         this.data.clear();
11602         if(this.pruneModifiedRecords){
11603             this.modified = [];
11604         }
11605         this.fireEvent("clear", this);
11606     },
11607
11608     /**
11609      * Inserts Records to the Store at the given index and fires the add event.
11610      * @param {Number} index The start index at which to insert the passed Records.
11611      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11612      */
11613     insert : function(index, records){
11614         records = [].concat(records);
11615         for(var i = 0, len = records.length; i < len; i++){
11616             this.data.insert(index, records[i]);
11617             records[i].join(this);
11618         }
11619         this.fireEvent("add", this, records, index);
11620     },
11621
11622     /**
11623      * Get the index within the cache of the passed Record.
11624      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11625      * @return {Number} The index of the passed Record. Returns -1 if not found.
11626      */
11627     indexOf : function(record){
11628         return this.data.indexOf(record);
11629     },
11630
11631     /**
11632      * Get the index within the cache of the Record with the passed id.
11633      * @param {String} id The id of the Record to find.
11634      * @return {Number} The index of the Record. Returns -1 if not found.
11635      */
11636     indexOfId : function(id){
11637         return this.data.indexOfKey(id);
11638     },
11639
11640     /**
11641      * Get the Record with the specified id.
11642      * @param {String} id The id of the Record to find.
11643      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11644      */
11645     getById : function(id){
11646         return this.data.key(id);
11647     },
11648
11649     /**
11650      * Get the Record at the specified index.
11651      * @param {Number} index The index of the Record to find.
11652      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11653      */
11654     getAt : function(index){
11655         return this.data.itemAt(index);
11656     },
11657
11658     /**
11659      * Returns a range of Records between specified indices.
11660      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11661      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11662      * @return {Roo.data.Record[]} An array of Records
11663      */
11664     getRange : function(start, end){
11665         return this.data.getRange(start, end);
11666     },
11667
11668     // private
11669     storeOptions : function(o){
11670         o = Roo.apply({}, o);
11671         delete o.callback;
11672         delete o.scope;
11673         this.lastOptions = o;
11674     },
11675
11676     /**
11677      * Loads the Record cache from the configured Proxy using the configured Reader.
11678      * <p>
11679      * If using remote paging, then the first load call must specify the <em>start</em>
11680      * and <em>limit</em> properties in the options.params property to establish the initial
11681      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11682      * <p>
11683      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11684      * and this call will return before the new data has been loaded. Perform any post-processing
11685      * in a callback function, or in a "load" event handler.</strong>
11686      * <p>
11687      * @param {Object} options An object containing properties which control loading options:<ul>
11688      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11689      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11690      * passed the following arguments:<ul>
11691      * <li>r : Roo.data.Record[]</li>
11692      * <li>options: Options object from the load call</li>
11693      * <li>success: Boolean success indicator</li></ul></li>
11694      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11695      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11696      * </ul>
11697      */
11698     load : function(options){
11699         options = options || {};
11700         if(this.fireEvent("beforeload", this, options) !== false){
11701             this.storeOptions(options);
11702             var p = Roo.apply(options.params || {}, this.baseParams);
11703             // if meta was not loaded from remote source.. try requesting it.
11704             if (!this.reader.metaFromRemote) {
11705                 p._requestMeta = 1;
11706             }
11707             if(this.sortInfo && this.remoteSort){
11708                 var pn = this.paramNames;
11709                 p[pn["sort"]] = this.sortInfo.field;
11710                 p[pn["dir"]] = this.sortInfo.direction;
11711             }
11712             if (this.multiSort) {
11713                 var pn = this.paramNames;
11714                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11715             }
11716             
11717             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11718         }
11719     },
11720
11721     /**
11722      * Reloads the Record cache from the configured Proxy using the configured Reader and
11723      * the options from the last load operation performed.
11724      * @param {Object} options (optional) An object containing properties which may override the options
11725      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11726      * the most recently used options are reused).
11727      */
11728     reload : function(options){
11729         this.load(Roo.applyIf(options||{}, this.lastOptions));
11730     },
11731
11732     // private
11733     // Called as a callback by the Reader during a load operation.
11734     loadRecords : function(o, options, success){
11735         if(!o || success === false){
11736             if(success !== false){
11737                 this.fireEvent("load", this, [], options, o);
11738             }
11739             if(options.callback){
11740                 options.callback.call(options.scope || this, [], options, false);
11741             }
11742             return;
11743         }
11744         // if data returned failure - throw an exception.
11745         if (o.success === false) {
11746             // show a message if no listener is registered.
11747             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11748                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11749             }
11750             // loadmask wil be hooked into this..
11751             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11752             return;
11753         }
11754         var r = o.records, t = o.totalRecords || r.length;
11755         
11756         this.fireEvent("beforeloadadd", this, r, options, o);
11757         
11758         if(!options || options.add !== true){
11759             if(this.pruneModifiedRecords){
11760                 this.modified = [];
11761             }
11762             for(var i = 0, len = r.length; i < len; i++){
11763                 r[i].join(this);
11764             }
11765             if(this.snapshot){
11766                 this.data = this.snapshot;
11767                 delete this.snapshot;
11768             }
11769             this.data.clear();
11770             this.data.addAll(r);
11771             this.totalLength = t;
11772             this.applySort();
11773             this.fireEvent("datachanged", this);
11774         }else{
11775             this.totalLength = Math.max(t, this.data.length+r.length);
11776             this.add(r);
11777         }
11778         
11779         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11780                 
11781             var e = new Roo.data.Record({});
11782
11783             e.set(this.parent.displayField, this.parent.emptyTitle);
11784             e.set(this.parent.valueField, '');
11785
11786             this.insert(0, e);
11787         }
11788             
11789         this.fireEvent("load", this, r, options, o);
11790         if(options.callback){
11791             options.callback.call(options.scope || this, r, options, true);
11792         }
11793     },
11794
11795
11796     /**
11797      * Loads data from a passed data block. A Reader which understands the format of the data
11798      * must have been configured in the constructor.
11799      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11800      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11801      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11802      */
11803     loadData : function(o, append){
11804         var r = this.reader.readRecords(o);
11805         this.loadRecords(r, {add: append}, true);
11806     },
11807
11808     /**
11809      * Gets the number of cached records.
11810      * <p>
11811      * <em>If using paging, this may not be the total size of the dataset. If the data object
11812      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11813      * the data set size</em>
11814      */
11815     getCount : function(){
11816         return this.data.length || 0;
11817     },
11818
11819     /**
11820      * Gets the total number of records in the dataset as returned by the server.
11821      * <p>
11822      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11823      * the dataset size</em>
11824      */
11825     getTotalCount : function(){
11826         return this.totalLength || 0;
11827     },
11828
11829     /**
11830      * Returns the sort state of the Store as an object with two properties:
11831      * <pre><code>
11832  field {String} The name of the field by which the Records are sorted
11833  direction {String} The sort order, "ASC" or "DESC"
11834      * </code></pre>
11835      */
11836     getSortState : function(){
11837         return this.sortInfo;
11838     },
11839
11840     // private
11841     applySort : function(){
11842         if(this.sortInfo && !this.remoteSort){
11843             var s = this.sortInfo, f = s.field;
11844             var st = this.fields.get(f).sortType;
11845             var fn = function(r1, r2){
11846                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11847                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11848             };
11849             this.data.sort(s.direction, fn);
11850             if(this.snapshot && this.snapshot != this.data){
11851                 this.snapshot.sort(s.direction, fn);
11852             }
11853         }
11854     },
11855
11856     /**
11857      * Sets the default sort column and order to be used by the next load operation.
11858      * @param {String} fieldName The name of the field to sort by.
11859      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11860      */
11861     setDefaultSort : function(field, dir){
11862         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11863     },
11864
11865     /**
11866      * Sort the Records.
11867      * If remote sorting is used, the sort is performed on the server, and the cache is
11868      * reloaded. If local sorting is used, the cache is sorted internally.
11869      * @param {String} fieldName The name of the field to sort by.
11870      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11871      */
11872     sort : function(fieldName, dir){
11873         var f = this.fields.get(fieldName);
11874         if(!dir){
11875             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11876             
11877             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11878                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11879             }else{
11880                 dir = f.sortDir;
11881             }
11882         }
11883         this.sortToggle[f.name] = dir;
11884         this.sortInfo = {field: f.name, direction: dir};
11885         if(!this.remoteSort){
11886             this.applySort();
11887             this.fireEvent("datachanged", this);
11888         }else{
11889             this.load(this.lastOptions);
11890         }
11891     },
11892
11893     /**
11894      * Calls the specified function for each of the Records in the cache.
11895      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11896      * Returning <em>false</em> aborts and exits the iteration.
11897      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11898      */
11899     each : function(fn, scope){
11900         this.data.each(fn, scope);
11901     },
11902
11903     /**
11904      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11905      * (e.g., during paging).
11906      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11907      */
11908     getModifiedRecords : function(){
11909         return this.modified;
11910     },
11911
11912     // private
11913     createFilterFn : function(property, value, anyMatch){
11914         if(!value.exec){ // not a regex
11915             value = String(value);
11916             if(value.length == 0){
11917                 return false;
11918             }
11919             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11920         }
11921         return function(r){
11922             return value.test(r.data[property]);
11923         };
11924     },
11925
11926     /**
11927      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11928      * @param {String} property A field on your records
11929      * @param {Number} start The record index to start at (defaults to 0)
11930      * @param {Number} end The last record index to include (defaults to length - 1)
11931      * @return {Number} The sum
11932      */
11933     sum : function(property, start, end){
11934         var rs = this.data.items, v = 0;
11935         start = start || 0;
11936         end = (end || end === 0) ? end : rs.length-1;
11937
11938         for(var i = start; i <= end; i++){
11939             v += (rs[i].data[property] || 0);
11940         }
11941         return v;
11942     },
11943
11944     /**
11945      * Filter the records by a specified property.
11946      * @param {String} field A field on your records
11947      * @param {String/RegExp} value Either a string that the field
11948      * should start with or a RegExp to test against the field
11949      * @param {Boolean} anyMatch True to match any part not just the beginning
11950      */
11951     filter : function(property, value, anyMatch){
11952         var fn = this.createFilterFn(property, value, anyMatch);
11953         return fn ? this.filterBy(fn) : this.clearFilter();
11954     },
11955
11956     /**
11957      * Filter by a function. The specified function will be called with each
11958      * record in this data source. If the function returns true the record is included,
11959      * otherwise it is filtered.
11960      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11961      * @param {Object} scope (optional) The scope of the function (defaults to this)
11962      */
11963     filterBy : function(fn, scope){
11964         this.snapshot = this.snapshot || this.data;
11965         this.data = this.queryBy(fn, scope||this);
11966         this.fireEvent("datachanged", this);
11967     },
11968
11969     /**
11970      * Query the records by a specified property.
11971      * @param {String} field A field on your records
11972      * @param {String/RegExp} value Either a string that the field
11973      * should start with or a RegExp to test against the field
11974      * @param {Boolean} anyMatch True to match any part not just the beginning
11975      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11976      */
11977     query : function(property, value, anyMatch){
11978         var fn = this.createFilterFn(property, value, anyMatch);
11979         return fn ? this.queryBy(fn) : this.data.clone();
11980     },
11981
11982     /**
11983      * Query by a function. The specified function will be called with each
11984      * record in this data source. If the function returns true the record is included
11985      * in the results.
11986      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11987      * @param {Object} scope (optional) The scope of the function (defaults to this)
11988       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11989      **/
11990     queryBy : function(fn, scope){
11991         var data = this.snapshot || this.data;
11992         return data.filterBy(fn, scope||this);
11993     },
11994
11995     /**
11996      * Collects unique values for a particular dataIndex from this store.
11997      * @param {String} dataIndex The property to collect
11998      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11999      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
12000      * @return {Array} An array of the unique values
12001      **/
12002     collect : function(dataIndex, allowNull, bypassFilter){
12003         var d = (bypassFilter === true && this.snapshot) ?
12004                 this.snapshot.items : this.data.items;
12005         var v, sv, r = [], l = {};
12006         for(var i = 0, len = d.length; i < len; i++){
12007             v = d[i].data[dataIndex];
12008             sv = String(v);
12009             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
12010                 l[sv] = true;
12011                 r[r.length] = v;
12012             }
12013         }
12014         return r;
12015     },
12016
12017     /**
12018      * Revert to a view of the Record cache with no filtering applied.
12019      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
12020      */
12021     clearFilter : function(suppressEvent){
12022         if(this.snapshot && this.snapshot != this.data){
12023             this.data = this.snapshot;
12024             delete this.snapshot;
12025             if(suppressEvent !== true){
12026                 this.fireEvent("datachanged", this);
12027             }
12028         }
12029     },
12030
12031     // private
12032     afterEdit : function(record){
12033         if(this.modified.indexOf(record) == -1){
12034             this.modified.push(record);
12035         }
12036         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
12037     },
12038     
12039     // private
12040     afterReject : function(record){
12041         this.modified.remove(record);
12042         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
12043     },
12044
12045     // private
12046     afterCommit : function(record){
12047         this.modified.remove(record);
12048         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
12049     },
12050
12051     /**
12052      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
12053      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
12054      */
12055     commitChanges : function(){
12056         var m = this.modified.slice(0);
12057         this.modified = [];
12058         for(var i = 0, len = m.length; i < len; i++){
12059             m[i].commit();
12060         }
12061     },
12062
12063     /**
12064      * Cancel outstanding changes on all changed records.
12065      */
12066     rejectChanges : function(){
12067         var m = this.modified.slice(0);
12068         this.modified = [];
12069         for(var i = 0, len = m.length; i < len; i++){
12070             m[i].reject();
12071         }
12072     },
12073
12074     onMetaChange : function(meta, rtype, o){
12075         this.recordType = rtype;
12076         this.fields = rtype.prototype.fields;
12077         delete this.snapshot;
12078         this.sortInfo = meta.sortInfo || this.sortInfo;
12079         this.modified = [];
12080         this.fireEvent('metachange', this, this.reader.meta);
12081     },
12082     
12083     moveIndex : function(data, type)
12084     {
12085         var index = this.indexOf(data);
12086         
12087         var newIndex = index + type;
12088         
12089         this.remove(data);
12090         
12091         this.insert(newIndex, data);
12092         
12093     }
12094 });/*
12095  * Based on:
12096  * Ext JS Library 1.1.1
12097  * Copyright(c) 2006-2007, Ext JS, LLC.
12098  *
12099  * Originally Released Under LGPL - original licence link has changed is not relivant.
12100  *
12101  * Fork - LGPL
12102  * <script type="text/javascript">
12103  */
12104
12105 /**
12106  * @class Roo.data.SimpleStore
12107  * @extends Roo.data.Store
12108  * Small helper class to make creating Stores from Array data easier.
12109  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
12110  * @cfg {Array} fields An array of field definition objects, or field name strings.
12111  * @cfg {Array} data The multi-dimensional array of data
12112  * @constructor
12113  * @param {Object} config
12114  */
12115 Roo.data.SimpleStore = function(config){
12116     Roo.data.SimpleStore.superclass.constructor.call(this, {
12117         isLocal : true,
12118         reader: new Roo.data.ArrayReader({
12119                 id: config.id
12120             },
12121             Roo.data.Record.create(config.fields)
12122         ),
12123         proxy : new Roo.data.MemoryProxy(config.data)
12124     });
12125     this.load();
12126 };
12127 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
12128  * Based on:
12129  * Ext JS Library 1.1.1
12130  * Copyright(c) 2006-2007, Ext JS, LLC.
12131  *
12132  * Originally Released Under LGPL - original licence link has changed is not relivant.
12133  *
12134  * Fork - LGPL
12135  * <script type="text/javascript">
12136  */
12137
12138 /**
12139 /**
12140  * @extends Roo.data.Store
12141  * @class Roo.data.JsonStore
12142  * Small helper class to make creating Stores for JSON data easier. <br/>
12143 <pre><code>
12144 var store = new Roo.data.JsonStore({
12145     url: 'get-images.php',
12146     root: 'images',
12147     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
12148 });
12149 </code></pre>
12150  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12151  * JsonReader and HttpProxy (unless inline data is provided).</b>
12152  * @cfg {Array} fields An array of field definition objects, or field name strings.
12153  * @constructor
12154  * @param {Object} config
12155  */
12156 Roo.data.JsonStore = function(c){
12157     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12158         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12159         reader: new Roo.data.JsonReader(c, c.fields)
12160     }));
12161 };
12162 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12163  * Based on:
12164  * Ext JS Library 1.1.1
12165  * Copyright(c) 2006-2007, Ext JS, LLC.
12166  *
12167  * Originally Released Under LGPL - original licence link has changed is not relivant.
12168  *
12169  * Fork - LGPL
12170  * <script type="text/javascript">
12171  */
12172
12173  
12174 Roo.data.Field = function(config){
12175     if(typeof config == "string"){
12176         config = {name: config};
12177     }
12178     Roo.apply(this, config);
12179     
12180     if(!this.type){
12181         this.type = "auto";
12182     }
12183     
12184     var st = Roo.data.SortTypes;
12185     // named sortTypes are supported, here we look them up
12186     if(typeof this.sortType == "string"){
12187         this.sortType = st[this.sortType];
12188     }
12189     
12190     // set default sortType for strings and dates
12191     if(!this.sortType){
12192         switch(this.type){
12193             case "string":
12194                 this.sortType = st.asUCString;
12195                 break;
12196             case "date":
12197                 this.sortType = st.asDate;
12198                 break;
12199             default:
12200                 this.sortType = st.none;
12201         }
12202     }
12203
12204     // define once
12205     var stripRe = /[\$,%]/g;
12206
12207     // prebuilt conversion function for this field, instead of
12208     // switching every time we're reading a value
12209     if(!this.convert){
12210         var cv, dateFormat = this.dateFormat;
12211         switch(this.type){
12212             case "":
12213             case "auto":
12214             case undefined:
12215                 cv = function(v){ return v; };
12216                 break;
12217             case "string":
12218                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12219                 break;
12220             case "int":
12221                 cv = function(v){
12222                     return v !== undefined && v !== null && v !== '' ?
12223                            parseInt(String(v).replace(stripRe, ""), 10) : '';
12224                     };
12225                 break;
12226             case "float":
12227                 cv = function(v){
12228                     return v !== undefined && v !== null && v !== '' ?
12229                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
12230                     };
12231                 break;
12232             case "bool":
12233             case "boolean":
12234                 cv = function(v){ return v === true || v === "true" || v == 1; };
12235                 break;
12236             case "date":
12237                 cv = function(v){
12238                     if(!v){
12239                         return '';
12240                     }
12241                     if(v instanceof Date){
12242                         return v;
12243                     }
12244                     if(dateFormat){
12245                         if(dateFormat == "timestamp"){
12246                             return new Date(v*1000);
12247                         }
12248                         return Date.parseDate(v, dateFormat);
12249                     }
12250                     var parsed = Date.parse(v);
12251                     return parsed ? new Date(parsed) : null;
12252                 };
12253              break;
12254             
12255         }
12256         this.convert = cv;
12257     }
12258 };
12259
12260 Roo.data.Field.prototype = {
12261     dateFormat: null,
12262     defaultValue: "",
12263     mapping: null,
12264     sortType : null,
12265     sortDir : "ASC"
12266 };/*
12267  * Based on:
12268  * Ext JS Library 1.1.1
12269  * Copyright(c) 2006-2007, Ext JS, LLC.
12270  *
12271  * Originally Released Under LGPL - original licence link has changed is not relivant.
12272  *
12273  * Fork - LGPL
12274  * <script type="text/javascript">
12275  */
12276  
12277 // Base class for reading structured data from a data source.  This class is intended to be
12278 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12279
12280 /**
12281  * @class Roo.data.DataReader
12282  * Base class for reading structured data from a data source.  This class is intended to be
12283  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12284  */
12285
12286 Roo.data.DataReader = function(meta, recordType){
12287     
12288     this.meta = meta;
12289     
12290     this.recordType = recordType instanceof Array ? 
12291         Roo.data.Record.create(recordType) : recordType;
12292 };
12293
12294 Roo.data.DataReader.prototype = {
12295      /**
12296      * Create an empty record
12297      * @param {Object} data (optional) - overlay some values
12298      * @return {Roo.data.Record} record created.
12299      */
12300     newRow :  function(d) {
12301         var da =  {};
12302         this.recordType.prototype.fields.each(function(c) {
12303             switch( c.type) {
12304                 case 'int' : da[c.name] = 0; break;
12305                 case 'date' : da[c.name] = new Date(); break;
12306                 case 'float' : da[c.name] = 0.0; break;
12307                 case 'boolean' : da[c.name] = false; break;
12308                 default : da[c.name] = ""; break;
12309             }
12310             
12311         });
12312         return new this.recordType(Roo.apply(da, d));
12313     }
12314     
12315 };/*
12316  * Based on:
12317  * Ext JS Library 1.1.1
12318  * Copyright(c) 2006-2007, Ext JS, LLC.
12319  *
12320  * Originally Released Under LGPL - original licence link has changed is not relivant.
12321  *
12322  * Fork - LGPL
12323  * <script type="text/javascript">
12324  */
12325
12326 /**
12327  * @class Roo.data.DataProxy
12328  * @extends Roo.data.Observable
12329  * This class is an abstract base class for implementations which provide retrieval of
12330  * unformatted data objects.<br>
12331  * <p>
12332  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12333  * (of the appropriate type which knows how to parse the data object) to provide a block of
12334  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12335  * <p>
12336  * Custom implementations must implement the load method as described in
12337  * {@link Roo.data.HttpProxy#load}.
12338  */
12339 Roo.data.DataProxy = function(){
12340     this.addEvents({
12341         /**
12342          * @event beforeload
12343          * Fires before a network request is made to retrieve a data object.
12344          * @param {Object} This DataProxy object.
12345          * @param {Object} params The params parameter to the load function.
12346          */
12347         beforeload : true,
12348         /**
12349          * @event load
12350          * Fires before the load method's callback is called.
12351          * @param {Object} This DataProxy object.
12352          * @param {Object} o The data object.
12353          * @param {Object} arg The callback argument object passed to the load function.
12354          */
12355         load : true,
12356         /**
12357          * @event loadexception
12358          * Fires if an Exception occurs during data retrieval.
12359          * @param {Object} This DataProxy object.
12360          * @param {Object} o The data object.
12361          * @param {Object} arg The callback argument object passed to the load function.
12362          * @param {Object} e The Exception.
12363          */
12364         loadexception : true
12365     });
12366     Roo.data.DataProxy.superclass.constructor.call(this);
12367 };
12368
12369 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12370
12371     /**
12372      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12373      */
12374 /*
12375  * Based on:
12376  * Ext JS Library 1.1.1
12377  * Copyright(c) 2006-2007, Ext JS, LLC.
12378  *
12379  * Originally Released Under LGPL - original licence link has changed is not relivant.
12380  *
12381  * Fork - LGPL
12382  * <script type="text/javascript">
12383  */
12384 /**
12385  * @class Roo.data.MemoryProxy
12386  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12387  * to the Reader when its load method is called.
12388  * @constructor
12389  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12390  */
12391 Roo.data.MemoryProxy = function(data){
12392     if (data.data) {
12393         data = data.data;
12394     }
12395     Roo.data.MemoryProxy.superclass.constructor.call(this);
12396     this.data = data;
12397 };
12398
12399 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12400     
12401     /**
12402      * Load data from the requested source (in this case an in-memory
12403      * data object passed to the constructor), read the data object into
12404      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12405      * process that block using the passed callback.
12406      * @param {Object} params This parameter is not used by the MemoryProxy class.
12407      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12408      * object into a block of Roo.data.Records.
12409      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12410      * The function must be passed <ul>
12411      * <li>The Record block object</li>
12412      * <li>The "arg" argument from the load function</li>
12413      * <li>A boolean success indicator</li>
12414      * </ul>
12415      * @param {Object} scope The scope in which to call the callback
12416      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12417      */
12418     load : function(params, reader, callback, scope, arg){
12419         params = params || {};
12420         var result;
12421         try {
12422             result = reader.readRecords(params.data ? params.data :this.data);
12423         }catch(e){
12424             this.fireEvent("loadexception", this, arg, null, e);
12425             callback.call(scope, null, arg, false);
12426             return;
12427         }
12428         callback.call(scope, result, arg, true);
12429     },
12430     
12431     // private
12432     update : function(params, records){
12433         
12434     }
12435 });/*
12436  * Based on:
12437  * Ext JS Library 1.1.1
12438  * Copyright(c) 2006-2007, Ext JS, LLC.
12439  *
12440  * Originally Released Under LGPL - original licence link has changed is not relivant.
12441  *
12442  * Fork - LGPL
12443  * <script type="text/javascript">
12444  */
12445 /**
12446  * @class Roo.data.HttpProxy
12447  * @extends Roo.data.DataProxy
12448  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12449  * configured to reference a certain URL.<br><br>
12450  * <p>
12451  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12452  * from which the running page was served.<br><br>
12453  * <p>
12454  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12455  * <p>
12456  * Be aware that to enable the browser to parse an XML document, the server must set
12457  * the Content-Type header in the HTTP response to "text/xml".
12458  * @constructor
12459  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12460  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12461  * will be used to make the request.
12462  */
12463 Roo.data.HttpProxy = function(conn){
12464     Roo.data.HttpProxy.superclass.constructor.call(this);
12465     // is conn a conn config or a real conn?
12466     this.conn = conn;
12467     this.useAjax = !conn || !conn.events;
12468   
12469 };
12470
12471 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12472     // thse are take from connection...
12473     
12474     /**
12475      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12476      */
12477     /**
12478      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12479      * extra parameters to each request made by this object. (defaults to undefined)
12480      */
12481     /**
12482      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12483      *  to each request made by this object. (defaults to undefined)
12484      */
12485     /**
12486      * @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)
12487      */
12488     /**
12489      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12490      */
12491      /**
12492      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12493      * @type Boolean
12494      */
12495   
12496
12497     /**
12498      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12499      * @type Boolean
12500      */
12501     /**
12502      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12503      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12504      * a finer-grained basis than the DataProxy events.
12505      */
12506     getConnection : function(){
12507         return this.useAjax ? Roo.Ajax : this.conn;
12508     },
12509
12510     /**
12511      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12512      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12513      * process that block using the passed callback.
12514      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12515      * for the request to the remote server.
12516      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12517      * object into a block of Roo.data.Records.
12518      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12519      * The function must be passed <ul>
12520      * <li>The Record block object</li>
12521      * <li>The "arg" argument from the load function</li>
12522      * <li>A boolean success indicator</li>
12523      * </ul>
12524      * @param {Object} scope The scope in which to call the callback
12525      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12526      */
12527     load : function(params, reader, callback, scope, arg){
12528         if(this.fireEvent("beforeload", this, params) !== false){
12529             var  o = {
12530                 params : params || {},
12531                 request: {
12532                     callback : callback,
12533                     scope : scope,
12534                     arg : arg
12535                 },
12536                 reader: reader,
12537                 callback : this.loadResponse,
12538                 scope: this
12539             };
12540             if(this.useAjax){
12541                 Roo.applyIf(o, this.conn);
12542                 if(this.activeRequest){
12543                     Roo.Ajax.abort(this.activeRequest);
12544                 }
12545                 this.activeRequest = Roo.Ajax.request(o);
12546             }else{
12547                 this.conn.request(o);
12548             }
12549         }else{
12550             callback.call(scope||this, null, arg, false);
12551         }
12552     },
12553
12554     // private
12555     loadResponse : function(o, success, response){
12556         delete this.activeRequest;
12557         if(!success){
12558             this.fireEvent("loadexception", this, o, response);
12559             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12560             return;
12561         }
12562         var result;
12563         try {
12564             result = o.reader.read(response);
12565         }catch(e){
12566             this.fireEvent("loadexception", this, o, response, e);
12567             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12568             return;
12569         }
12570         
12571         this.fireEvent("load", this, o, o.request.arg);
12572         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12573     },
12574
12575     // private
12576     update : function(dataSet){
12577
12578     },
12579
12580     // private
12581     updateResponse : function(dataSet){
12582
12583     }
12584 });/*
12585  * Based on:
12586  * Ext JS Library 1.1.1
12587  * Copyright(c) 2006-2007, Ext JS, LLC.
12588  *
12589  * Originally Released Under LGPL - original licence link has changed is not relivant.
12590  *
12591  * Fork - LGPL
12592  * <script type="text/javascript">
12593  */
12594
12595 /**
12596  * @class Roo.data.ScriptTagProxy
12597  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12598  * other than the originating domain of the running page.<br><br>
12599  * <p>
12600  * <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
12601  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12602  * <p>
12603  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12604  * source code that is used as the source inside a &lt;script> tag.<br><br>
12605  * <p>
12606  * In order for the browser to process the returned data, the server must wrap the data object
12607  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12608  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12609  * depending on whether the callback name was passed:
12610  * <p>
12611  * <pre><code>
12612 boolean scriptTag = false;
12613 String cb = request.getParameter("callback");
12614 if (cb != null) {
12615     scriptTag = true;
12616     response.setContentType("text/javascript");
12617 } else {
12618     response.setContentType("application/x-json");
12619 }
12620 Writer out = response.getWriter();
12621 if (scriptTag) {
12622     out.write(cb + "(");
12623 }
12624 out.print(dataBlock.toJsonString());
12625 if (scriptTag) {
12626     out.write(");");
12627 }
12628 </pre></code>
12629  *
12630  * @constructor
12631  * @param {Object} config A configuration object.
12632  */
12633 Roo.data.ScriptTagProxy = function(config){
12634     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12635     Roo.apply(this, config);
12636     this.head = document.getElementsByTagName("head")[0];
12637 };
12638
12639 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12640
12641 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12642     /**
12643      * @cfg {String} url The URL from which to request the data object.
12644      */
12645     /**
12646      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12647      */
12648     timeout : 30000,
12649     /**
12650      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12651      * the server the name of the callback function set up by the load call to process the returned data object.
12652      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12653      * javascript output which calls this named function passing the data object as its only parameter.
12654      */
12655     callbackParam : "callback",
12656     /**
12657      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12658      * name to the request.
12659      */
12660     nocache : true,
12661
12662     /**
12663      * Load data from the configured URL, read the data object into
12664      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12665      * process that block using the passed callback.
12666      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12667      * for the request to the remote server.
12668      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12669      * object into a block of Roo.data.Records.
12670      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12671      * The function must be passed <ul>
12672      * <li>The Record block object</li>
12673      * <li>The "arg" argument from the load function</li>
12674      * <li>A boolean success indicator</li>
12675      * </ul>
12676      * @param {Object} scope The scope in which to call the callback
12677      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12678      */
12679     load : function(params, reader, callback, scope, arg){
12680         if(this.fireEvent("beforeload", this, params) !== false){
12681
12682             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12683
12684             var url = this.url;
12685             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12686             if(this.nocache){
12687                 url += "&_dc=" + (new Date().getTime());
12688             }
12689             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12690             var trans = {
12691                 id : transId,
12692                 cb : "stcCallback"+transId,
12693                 scriptId : "stcScript"+transId,
12694                 params : params,
12695                 arg : arg,
12696                 url : url,
12697                 callback : callback,
12698                 scope : scope,
12699                 reader : reader
12700             };
12701             var conn = this;
12702
12703             window[trans.cb] = function(o){
12704                 conn.handleResponse(o, trans);
12705             };
12706
12707             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12708
12709             if(this.autoAbort !== false){
12710                 this.abort();
12711             }
12712
12713             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12714
12715             var script = document.createElement("script");
12716             script.setAttribute("src", url);
12717             script.setAttribute("type", "text/javascript");
12718             script.setAttribute("id", trans.scriptId);
12719             this.head.appendChild(script);
12720
12721             this.trans = trans;
12722         }else{
12723             callback.call(scope||this, null, arg, false);
12724         }
12725     },
12726
12727     // private
12728     isLoading : function(){
12729         return this.trans ? true : false;
12730     },
12731
12732     /**
12733      * Abort the current server request.
12734      */
12735     abort : function(){
12736         if(this.isLoading()){
12737             this.destroyTrans(this.trans);
12738         }
12739     },
12740
12741     // private
12742     destroyTrans : function(trans, isLoaded){
12743         this.head.removeChild(document.getElementById(trans.scriptId));
12744         clearTimeout(trans.timeoutId);
12745         if(isLoaded){
12746             window[trans.cb] = undefined;
12747             try{
12748                 delete window[trans.cb];
12749             }catch(e){}
12750         }else{
12751             // if hasn't been loaded, wait for load to remove it to prevent script error
12752             window[trans.cb] = function(){
12753                 window[trans.cb] = undefined;
12754                 try{
12755                     delete window[trans.cb];
12756                 }catch(e){}
12757             };
12758         }
12759     },
12760
12761     // private
12762     handleResponse : function(o, trans){
12763         this.trans = false;
12764         this.destroyTrans(trans, true);
12765         var result;
12766         try {
12767             result = trans.reader.readRecords(o);
12768         }catch(e){
12769             this.fireEvent("loadexception", this, o, trans.arg, e);
12770             trans.callback.call(trans.scope||window, null, trans.arg, false);
12771             return;
12772         }
12773         this.fireEvent("load", this, o, trans.arg);
12774         trans.callback.call(trans.scope||window, result, trans.arg, true);
12775     },
12776
12777     // private
12778     handleFailure : function(trans){
12779         this.trans = false;
12780         this.destroyTrans(trans, false);
12781         this.fireEvent("loadexception", this, null, trans.arg);
12782         trans.callback.call(trans.scope||window, null, trans.arg, false);
12783     }
12784 });/*
12785  * Based on:
12786  * Ext JS Library 1.1.1
12787  * Copyright(c) 2006-2007, Ext JS, LLC.
12788  *
12789  * Originally Released Under LGPL - original licence link has changed is not relivant.
12790  *
12791  * Fork - LGPL
12792  * <script type="text/javascript">
12793  */
12794
12795 /**
12796  * @class Roo.data.JsonReader
12797  * @extends Roo.data.DataReader
12798  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12799  * based on mappings in a provided Roo.data.Record constructor.
12800  * 
12801  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12802  * in the reply previously. 
12803  * 
12804  * <p>
12805  * Example code:
12806  * <pre><code>
12807 var RecordDef = Roo.data.Record.create([
12808     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12809     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12810 ]);
12811 var myReader = new Roo.data.JsonReader({
12812     totalProperty: "results",    // The property which contains the total dataset size (optional)
12813     root: "rows",                // The property which contains an Array of row objects
12814     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12815 }, RecordDef);
12816 </code></pre>
12817  * <p>
12818  * This would consume a JSON file like this:
12819  * <pre><code>
12820 { 'results': 2, 'rows': [
12821     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12822     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12823 }
12824 </code></pre>
12825  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12826  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12827  * paged from the remote server.
12828  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12829  * @cfg {String} root name of the property which contains the Array of row objects.
12830  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12831  * @cfg {Array} fields Array of field definition objects
12832  * @constructor
12833  * Create a new JsonReader
12834  * @param {Object} meta Metadata configuration options
12835  * @param {Object} recordType Either an Array of field definition objects,
12836  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12837  */
12838 Roo.data.JsonReader = function(meta, recordType){
12839     
12840     meta = meta || {};
12841     // set some defaults:
12842     Roo.applyIf(meta, {
12843         totalProperty: 'total',
12844         successProperty : 'success',
12845         root : 'data',
12846         id : 'id'
12847     });
12848     
12849     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12850 };
12851 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12852     
12853     /**
12854      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12855      * Used by Store query builder to append _requestMeta to params.
12856      * 
12857      */
12858     metaFromRemote : false,
12859     /**
12860      * This method is only used by a DataProxy which has retrieved data from a remote server.
12861      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12862      * @return {Object} data A data block which is used by an Roo.data.Store object as
12863      * a cache of Roo.data.Records.
12864      */
12865     read : function(response){
12866         var json = response.responseText;
12867        
12868         var o = /* eval:var:o */ eval("("+json+")");
12869         if(!o) {
12870             throw {message: "JsonReader.read: Json object not found"};
12871         }
12872         
12873         if(o.metaData){
12874             
12875             delete this.ef;
12876             this.metaFromRemote = true;
12877             this.meta = o.metaData;
12878             this.recordType = Roo.data.Record.create(o.metaData.fields);
12879             this.onMetaChange(this.meta, this.recordType, o);
12880         }
12881         return this.readRecords(o);
12882     },
12883
12884     // private function a store will implement
12885     onMetaChange : function(meta, recordType, o){
12886
12887     },
12888
12889     /**
12890          * @ignore
12891          */
12892     simpleAccess: function(obj, subsc) {
12893         return obj[subsc];
12894     },
12895
12896         /**
12897          * @ignore
12898          */
12899     getJsonAccessor: function(){
12900         var re = /[\[\.]/;
12901         return function(expr) {
12902             try {
12903                 return(re.test(expr))
12904                     ? new Function("obj", "return obj." + expr)
12905                     : function(obj){
12906                         return obj[expr];
12907                     };
12908             } catch(e){}
12909             return Roo.emptyFn;
12910         };
12911     }(),
12912
12913     /**
12914      * Create a data block containing Roo.data.Records from an XML document.
12915      * @param {Object} o An object which contains an Array of row objects in the property specified
12916      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12917      * which contains the total size of the dataset.
12918      * @return {Object} data A data block which is used by an Roo.data.Store object as
12919      * a cache of Roo.data.Records.
12920      */
12921     readRecords : function(o){
12922         /**
12923          * After any data loads, the raw JSON data is available for further custom processing.
12924          * @type Object
12925          */
12926         this.o = o;
12927         var s = this.meta, Record = this.recordType,
12928             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12929
12930 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12931         if (!this.ef) {
12932             if(s.totalProperty) {
12933                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12934                 }
12935                 if(s.successProperty) {
12936                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12937                 }
12938                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12939                 if (s.id) {
12940                         var g = this.getJsonAccessor(s.id);
12941                         this.getId = function(rec) {
12942                                 var r = g(rec);  
12943                                 return (r === undefined || r === "") ? null : r;
12944                         };
12945                 } else {
12946                         this.getId = function(){return null;};
12947                 }
12948             this.ef = [];
12949             for(var jj = 0; jj < fl; jj++){
12950                 f = fi[jj];
12951                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12952                 this.ef[jj] = this.getJsonAccessor(map);
12953             }
12954         }
12955
12956         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12957         if(s.totalProperty){
12958             var vt = parseInt(this.getTotal(o), 10);
12959             if(!isNaN(vt)){
12960                 totalRecords = vt;
12961             }
12962         }
12963         if(s.successProperty){
12964             var vs = this.getSuccess(o);
12965             if(vs === false || vs === 'false'){
12966                 success = false;
12967             }
12968         }
12969         var records = [];
12970         for(var i = 0; i < c; i++){
12971                 var n = root[i];
12972             var values = {};
12973             var id = this.getId(n);
12974             for(var j = 0; j < fl; j++){
12975                 f = fi[j];
12976             var v = this.ef[j](n);
12977             if (!f.convert) {
12978                 Roo.log('missing convert for ' + f.name);
12979                 Roo.log(f);
12980                 continue;
12981             }
12982             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12983             }
12984             var record = new Record(values, id);
12985             record.json = n;
12986             records[i] = record;
12987         }
12988         return {
12989             raw : o,
12990             success : success,
12991             records : records,
12992             totalRecords : totalRecords
12993         };
12994     }
12995 });/*
12996  * Based on:
12997  * Ext JS Library 1.1.1
12998  * Copyright(c) 2006-2007, Ext JS, LLC.
12999  *
13000  * Originally Released Under LGPL - original licence link has changed is not relivant.
13001  *
13002  * Fork - LGPL
13003  * <script type="text/javascript">
13004  */
13005
13006 /**
13007  * @class Roo.data.ArrayReader
13008  * @extends Roo.data.DataReader
13009  * Data reader class to create an Array of Roo.data.Record objects from an Array.
13010  * Each element of that Array represents a row of data fields. The
13011  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
13012  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
13013  * <p>
13014  * Example code:.
13015  * <pre><code>
13016 var RecordDef = Roo.data.Record.create([
13017     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
13018     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
13019 ]);
13020 var myReader = new Roo.data.ArrayReader({
13021     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
13022 }, RecordDef);
13023 </code></pre>
13024  * <p>
13025  * This would consume an Array like this:
13026  * <pre><code>
13027 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
13028   </code></pre>
13029  
13030  * @constructor
13031  * Create a new JsonReader
13032  * @param {Object} meta Metadata configuration options.
13033  * @param {Object|Array} recordType Either an Array of field definition objects
13034  * 
13035  * @cfg {Array} fields Array of field definition objects
13036  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
13037  * as specified to {@link Roo.data.Record#create},
13038  * or an {@link Roo.data.Record} object
13039  *
13040  * 
13041  * created using {@link Roo.data.Record#create}.
13042  */
13043 Roo.data.ArrayReader = function(meta, recordType){
13044     
13045      
13046     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
13047 };
13048
13049 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
13050     /**
13051      * Create a data block containing Roo.data.Records from an XML document.
13052      * @param {Object} o An Array of row objects which represents the dataset.
13053      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
13054      * a cache of Roo.data.Records.
13055      */
13056     readRecords : function(o){
13057         var sid = this.meta ? this.meta.id : null;
13058         var recordType = this.recordType, fields = recordType.prototype.fields;
13059         var records = [];
13060         var root = o;
13061             for(var i = 0; i < root.length; i++){
13062                     var n = root[i];
13063                 var values = {};
13064                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
13065                 for(var j = 0, jlen = fields.length; j < jlen; j++){
13066                 var f = fields.items[j];
13067                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
13068                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
13069                 v = f.convert(v);
13070                 values[f.name] = v;
13071             }
13072                 var record = new recordType(values, id);
13073                 record.json = n;
13074                 records[records.length] = record;
13075             }
13076             return {
13077                 records : records,
13078                 totalRecords : records.length
13079             };
13080     }
13081 });/*
13082  * - LGPL
13083  * * 
13084  */
13085
13086 /**
13087  * @class Roo.bootstrap.ComboBox
13088  * @extends Roo.bootstrap.TriggerField
13089  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
13090  * @cfg {Boolean} append (true|false) default false
13091  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
13092  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
13093  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
13094  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
13095  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
13096  * @cfg {Boolean} animate default true
13097  * @cfg {Boolean} emptyResultText only for touch device
13098  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
13099  * @cfg {String} emptyTitle default ''
13100  * @constructor
13101  * Create a new ComboBox.
13102  * @param {Object} config Configuration options
13103  */
13104 Roo.bootstrap.ComboBox = function(config){
13105     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
13106     this.addEvents({
13107         /**
13108          * @event expand
13109          * Fires when the dropdown list is expanded
13110         * @param {Roo.bootstrap.ComboBox} combo This combo box
13111         */
13112         'expand' : true,
13113         /**
13114          * @event collapse
13115          * Fires when the dropdown list is collapsed
13116         * @param {Roo.bootstrap.ComboBox} combo This combo box
13117         */
13118         'collapse' : true,
13119         /**
13120          * @event beforeselect
13121          * Fires before a list item is selected. Return false to cancel the selection.
13122         * @param {Roo.bootstrap.ComboBox} combo This combo box
13123         * @param {Roo.data.Record} record The data record returned from the underlying store
13124         * @param {Number} index The index of the selected item in the dropdown list
13125         */
13126         'beforeselect' : true,
13127         /**
13128          * @event select
13129          * Fires when a list item is selected
13130         * @param {Roo.bootstrap.ComboBox} combo This combo box
13131         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
13132         * @param {Number} index The index of the selected item in the dropdown list
13133         */
13134         'select' : true,
13135         /**
13136          * @event beforequery
13137          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
13138          * The event object passed has these properties:
13139         * @param {Roo.bootstrap.ComboBox} combo This combo box
13140         * @param {String} query The query
13141         * @param {Boolean} forceAll true to force "all" query
13142         * @param {Boolean} cancel true to cancel the query
13143         * @param {Object} e The query event object
13144         */
13145         'beforequery': true,
13146          /**
13147          * @event add
13148          * Fires when the 'add' icon is pressed (add a listener to enable add button)
13149         * @param {Roo.bootstrap.ComboBox} combo This combo box
13150         */
13151         'add' : true,
13152         /**
13153          * @event edit
13154          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
13155         * @param {Roo.bootstrap.ComboBox} combo This combo box
13156         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13157         */
13158         'edit' : true,
13159         /**
13160          * @event remove
13161          * Fires when the remove value from the combobox array
13162         * @param {Roo.bootstrap.ComboBox} combo This combo box
13163         */
13164         'remove' : true,
13165         /**
13166          * @event afterremove
13167          * Fires when the remove value from the combobox array
13168         * @param {Roo.bootstrap.ComboBox} combo This combo box
13169         */
13170         'afterremove' : true,
13171         /**
13172          * @event specialfilter
13173          * Fires when specialfilter
13174             * @param {Roo.bootstrap.ComboBox} combo This combo box
13175             */
13176         'specialfilter' : true,
13177         /**
13178          * @event tick
13179          * Fires when tick the element
13180             * @param {Roo.bootstrap.ComboBox} combo This combo box
13181             */
13182         'tick' : true,
13183         /**
13184          * @event touchviewdisplay
13185          * Fires when touch view require special display (default is using displayField)
13186             * @param {Roo.bootstrap.ComboBox} combo This combo box
13187             * @param {Object} cfg set html .
13188             */
13189         'touchviewdisplay' : true
13190         
13191     });
13192     
13193     this.item = [];
13194     this.tickItems = [];
13195     
13196     this.selectedIndex = -1;
13197     if(this.mode == 'local'){
13198         if(config.queryDelay === undefined){
13199             this.queryDelay = 10;
13200         }
13201         if(config.minChars === undefined){
13202             this.minChars = 0;
13203         }
13204     }
13205 };
13206
13207 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13208      
13209     /**
13210      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13211      * rendering into an Roo.Editor, defaults to false)
13212      */
13213     /**
13214      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13215      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13216      */
13217     /**
13218      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13219      */
13220     /**
13221      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13222      * the dropdown list (defaults to undefined, with no header element)
13223      */
13224
13225      /**
13226      * @cfg {String/Roo.Template} tpl The template to use to render the output
13227      */
13228      
13229      /**
13230      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13231      */
13232     listWidth: undefined,
13233     /**
13234      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13235      * mode = 'remote' or 'text' if mode = 'local')
13236      */
13237     displayField: undefined,
13238     
13239     /**
13240      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13241      * mode = 'remote' or 'value' if mode = 'local'). 
13242      * Note: use of a valueField requires the user make a selection
13243      * in order for a value to be mapped.
13244      */
13245     valueField: undefined,
13246     /**
13247      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13248      */
13249     modalTitle : '',
13250     
13251     /**
13252      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13253      * field's data value (defaults to the underlying DOM element's name)
13254      */
13255     hiddenName: undefined,
13256     /**
13257      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13258      */
13259     listClass: '',
13260     /**
13261      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13262      */
13263     selectedClass: 'active',
13264     
13265     /**
13266      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13267      */
13268     shadow:'sides',
13269     /**
13270      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13271      * anchor positions (defaults to 'tl-bl')
13272      */
13273     listAlign: 'tl-bl?',
13274     /**
13275      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13276      */
13277     maxHeight: 300,
13278     /**
13279      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13280      * query specified by the allQuery config option (defaults to 'query')
13281      */
13282     triggerAction: 'query',
13283     /**
13284      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13285      * (defaults to 4, does not apply if editable = false)
13286      */
13287     minChars : 4,
13288     /**
13289      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13290      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13291      */
13292     typeAhead: false,
13293     /**
13294      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13295      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13296      */
13297     queryDelay: 500,
13298     /**
13299      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13300      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13301      */
13302     pageSize: 0,
13303     /**
13304      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13305      * when editable = true (defaults to false)
13306      */
13307     selectOnFocus:false,
13308     /**
13309      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13310      */
13311     queryParam: 'query',
13312     /**
13313      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13314      * when mode = 'remote' (defaults to 'Loading...')
13315      */
13316     loadingText: 'Loading...',
13317     /**
13318      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13319      */
13320     resizable: false,
13321     /**
13322      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13323      */
13324     handleHeight : 8,
13325     /**
13326      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13327      * traditional select (defaults to true)
13328      */
13329     editable: true,
13330     /**
13331      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13332      */
13333     allQuery: '',
13334     /**
13335      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13336      */
13337     mode: 'remote',
13338     /**
13339      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13340      * listWidth has a higher value)
13341      */
13342     minListWidth : 70,
13343     /**
13344      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13345      * allow the user to set arbitrary text into the field (defaults to false)
13346      */
13347     forceSelection:false,
13348     /**
13349      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13350      * if typeAhead = true (defaults to 250)
13351      */
13352     typeAheadDelay : 250,
13353     /**
13354      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13355      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13356      */
13357     valueNotFoundText : undefined,
13358     /**
13359      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13360      */
13361     blockFocus : false,
13362     
13363     /**
13364      * @cfg {Boolean} disableClear Disable showing of clear button.
13365      */
13366     disableClear : false,
13367     /**
13368      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13369      */
13370     alwaysQuery : false,
13371     
13372     /**
13373      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13374      */
13375     multiple : false,
13376     
13377     /**
13378      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
13379      */
13380     invalidClass : "has-warning",
13381     
13382     /**
13383      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
13384      */
13385     validClass : "has-success",
13386     
13387     /**
13388      * @cfg {Boolean} specialFilter (true|false) special filter default false
13389      */
13390     specialFilter : false,
13391     
13392     /**
13393      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13394      */
13395     mobileTouchView : true,
13396     
13397     /**
13398      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13399      */
13400     useNativeIOS : false,
13401     
13402     /**
13403      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13404      */
13405     mobile_restrict_height : false,
13406     
13407     ios_options : false,
13408     
13409     //private
13410     addicon : false,
13411     editicon: false,
13412     
13413     page: 0,
13414     hasQuery: false,
13415     append: false,
13416     loadNext: false,
13417     autoFocus : true,
13418     tickable : false,
13419     btnPosition : 'right',
13420     triggerList : true,
13421     showToggleBtn : true,
13422     animate : true,
13423     emptyResultText: 'Empty',
13424     triggerText : 'Select',
13425     emptyTitle : '',
13426     
13427     // element that contains real text value.. (when hidden is used..)
13428     
13429     getAutoCreate : function()
13430     {   
13431         var cfg = false;
13432         //render
13433         /*
13434          * Render classic select for iso
13435          */
13436         
13437         if(Roo.isIOS && this.useNativeIOS){
13438             cfg = this.getAutoCreateNativeIOS();
13439             return cfg;
13440         }
13441         
13442         /*
13443          * Touch Devices
13444          */
13445         
13446         if(Roo.isTouch && this.mobileTouchView){
13447             cfg = this.getAutoCreateTouchView();
13448             return cfg;;
13449         }
13450         
13451         /*
13452          *  Normal ComboBox
13453          */
13454         if(!this.tickable){
13455             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13456             return cfg;
13457         }
13458         
13459         /*
13460          *  ComboBox with tickable selections
13461          */
13462              
13463         var align = this.labelAlign || this.parentLabelAlign();
13464         
13465         cfg = {
13466             cls : 'form-group roo-combobox-tickable' //input-group
13467         };
13468         
13469         var btn_text_select = '';
13470         var btn_text_done = '';
13471         var btn_text_cancel = '';
13472         
13473         if (this.btn_text_show) {
13474             btn_text_select = 'Select';
13475             btn_text_done = 'Done';
13476             btn_text_cancel = 'Cancel'; 
13477         }
13478         
13479         var buttons = {
13480             tag : 'div',
13481             cls : 'tickable-buttons',
13482             cn : [
13483                 {
13484                     tag : 'button',
13485                     type : 'button',
13486                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13487                     //html : this.triggerText
13488                     html: btn_text_select
13489                 },
13490                 {
13491                     tag : 'button',
13492                     type : 'button',
13493                     name : 'ok',
13494                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13495                     //html : 'Done'
13496                     html: btn_text_done
13497                 },
13498                 {
13499                     tag : 'button',
13500                     type : 'button',
13501                     name : 'cancel',
13502                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13503                     //html : 'Cancel'
13504                     html: btn_text_cancel
13505                 }
13506             ]
13507         };
13508         
13509         if(this.editable){
13510             buttons.cn.unshift({
13511                 tag: 'input',
13512                 cls: 'roo-select2-search-field-input'
13513             });
13514         }
13515         
13516         var _this = this;
13517         
13518         Roo.each(buttons.cn, function(c){
13519             if (_this.size) {
13520                 c.cls += ' btn-' + _this.size;
13521             }
13522
13523             if (_this.disabled) {
13524                 c.disabled = true;
13525             }
13526         });
13527         
13528         var box = {
13529             tag: 'div',
13530             style : 'display: contents',
13531             cn: [
13532                 {
13533                     tag: 'input',
13534                     type : 'hidden',
13535                     cls: 'form-hidden-field'
13536                 },
13537                 {
13538                     tag: 'ul',
13539                     cls: 'roo-select2-choices',
13540                     cn:[
13541                         {
13542                             tag: 'li',
13543                             cls: 'roo-select2-search-field',
13544                             cn: [
13545                                 buttons
13546                             ]
13547                         }
13548                     ]
13549                 }
13550             ]
13551         };
13552         
13553         var combobox = {
13554             cls: 'roo-select2-container input-group roo-select2-container-multi',
13555             cn: [
13556                 
13557                 box
13558 //                {
13559 //                    tag: 'ul',
13560 //                    cls: 'typeahead typeahead-long dropdown-menu',
13561 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13562 //                }
13563             ]
13564         };
13565         
13566         if(this.hasFeedback && !this.allowBlank){
13567             
13568             var feedback = {
13569                 tag: 'span',
13570                 cls: 'glyphicon form-control-feedback'
13571             };
13572
13573             combobox.cn.push(feedback);
13574         }
13575         
13576         var indicator = {
13577             tag : 'i',
13578             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13579             tooltip : 'This field is required'
13580         };
13581         if (Roo.bootstrap.version == 4) {
13582             indicator = {
13583                 tag : 'i',
13584                 style : 'display:none'
13585             };
13586         }
13587         if (align ==='left' && this.fieldLabel.length) {
13588             
13589             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13590             
13591             cfg.cn = [
13592                 indicator,
13593                 {
13594                     tag: 'label',
13595                     'for' :  id,
13596                     cls : 'control-label col-form-label',
13597                     html : this.fieldLabel
13598
13599                 },
13600                 {
13601                     cls : "", 
13602                     cn: [
13603                         combobox
13604                     ]
13605                 }
13606
13607             ];
13608             
13609             var labelCfg = cfg.cn[1];
13610             var contentCfg = cfg.cn[2];
13611             
13612
13613             if(this.indicatorpos == 'right'){
13614                 
13615                 cfg.cn = [
13616                     {
13617                         tag: 'label',
13618                         'for' :  id,
13619                         cls : 'control-label col-form-label',
13620                         cn : [
13621                             {
13622                                 tag : 'span',
13623                                 html : this.fieldLabel
13624                             },
13625                             indicator
13626                         ]
13627                     },
13628                     {
13629                         cls : "",
13630                         cn: [
13631                             combobox
13632                         ]
13633                     }
13634
13635                 ];
13636                 
13637                 
13638                 
13639                 labelCfg = cfg.cn[0];
13640                 contentCfg = cfg.cn[1];
13641             
13642             }
13643             
13644             if(this.labelWidth > 12){
13645                 labelCfg.style = "width: " + this.labelWidth + 'px';
13646             }
13647             
13648             if(this.labelWidth < 13 && this.labelmd == 0){
13649                 this.labelmd = this.labelWidth;
13650             }
13651             
13652             if(this.labellg > 0){
13653                 labelCfg.cls += ' col-lg-' + this.labellg;
13654                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13655             }
13656             
13657             if(this.labelmd > 0){
13658                 labelCfg.cls += ' col-md-' + this.labelmd;
13659                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13660             }
13661             
13662             if(this.labelsm > 0){
13663                 labelCfg.cls += ' col-sm-' + this.labelsm;
13664                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13665             }
13666             
13667             if(this.labelxs > 0){
13668                 labelCfg.cls += ' col-xs-' + this.labelxs;
13669                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13670             }
13671                 
13672                 
13673         } else if ( this.fieldLabel.length) {
13674 //                Roo.log(" label");
13675                  cfg.cn = [
13676                    indicator,
13677                     {
13678                         tag: 'label',
13679                         //cls : 'input-group-addon',
13680                         html : this.fieldLabel
13681                     },
13682                     combobox
13683                 ];
13684                 
13685                 if(this.indicatorpos == 'right'){
13686                     cfg.cn = [
13687                         {
13688                             tag: 'label',
13689                             //cls : 'input-group-addon',
13690                             html : this.fieldLabel
13691                         },
13692                         indicator,
13693                         combobox
13694                     ];
13695                     
13696                 }
13697
13698         } else {
13699             
13700 //                Roo.log(" no label && no align");
13701                 cfg = combobox
13702                      
13703                 
13704         }
13705          
13706         var settings=this;
13707         ['xs','sm','md','lg'].map(function(size){
13708             if (settings[size]) {
13709                 cfg.cls += ' col-' + size + '-' + settings[size];
13710             }
13711         });
13712         
13713         return cfg;
13714         
13715     },
13716     
13717     _initEventsCalled : false,
13718     
13719     // private
13720     initEvents: function()
13721     {   
13722         if (this._initEventsCalled) { // as we call render... prevent looping...
13723             return;
13724         }
13725         this._initEventsCalled = true;
13726         
13727         if (!this.store) {
13728             throw "can not find store for combo";
13729         }
13730         
13731         this.indicator = this.indicatorEl();
13732         
13733         this.store = Roo.factory(this.store, Roo.data);
13734         this.store.parent = this;
13735         
13736         // if we are building from html. then this element is so complex, that we can not really
13737         // use the rendered HTML.
13738         // so we have to trash and replace the previous code.
13739         if (Roo.XComponent.build_from_html) {
13740             // remove this element....
13741             var e = this.el.dom, k=0;
13742             while (e ) { e = e.previousSibling;  ++k;}
13743
13744             this.el.remove();
13745             
13746             this.el=false;
13747             this.rendered = false;
13748             
13749             this.render(this.parent().getChildContainer(true), k);
13750         }
13751         
13752         if(Roo.isIOS && this.useNativeIOS){
13753             this.initIOSView();
13754             return;
13755         }
13756         
13757         /*
13758          * Touch Devices
13759          */
13760         
13761         if(Roo.isTouch && this.mobileTouchView){
13762             this.initTouchView();
13763             return;
13764         }
13765         
13766         if(this.tickable){
13767             this.initTickableEvents();
13768             return;
13769         }
13770         
13771         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13772         
13773         if(this.hiddenName){
13774             
13775             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13776             
13777             this.hiddenField.dom.value =
13778                 this.hiddenValue !== undefined ? this.hiddenValue :
13779                 this.value !== undefined ? this.value : '';
13780
13781             // prevent input submission
13782             this.el.dom.removeAttribute('name');
13783             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13784              
13785              
13786         }
13787         //if(Roo.isGecko){
13788         //    this.el.dom.setAttribute('autocomplete', 'off');
13789         //}
13790         
13791         var cls = 'x-combo-list';
13792         
13793         //this.list = new Roo.Layer({
13794         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13795         //});
13796         
13797         var _this = this;
13798         
13799         (function(){
13800             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13801             _this.list.setWidth(lw);
13802         }).defer(100);
13803         
13804         this.list.on('mouseover', this.onViewOver, this);
13805         this.list.on('mousemove', this.onViewMove, this);
13806         this.list.on('scroll', this.onViewScroll, this);
13807         
13808         /*
13809         this.list.swallowEvent('mousewheel');
13810         this.assetHeight = 0;
13811
13812         if(this.title){
13813             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13814             this.assetHeight += this.header.getHeight();
13815         }
13816
13817         this.innerList = this.list.createChild({cls:cls+'-inner'});
13818         this.innerList.on('mouseover', this.onViewOver, this);
13819         this.innerList.on('mousemove', this.onViewMove, this);
13820         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13821         
13822         if(this.allowBlank && !this.pageSize && !this.disableClear){
13823             this.footer = this.list.createChild({cls:cls+'-ft'});
13824             this.pageTb = new Roo.Toolbar(this.footer);
13825            
13826         }
13827         if(this.pageSize){
13828             this.footer = this.list.createChild({cls:cls+'-ft'});
13829             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13830                     {pageSize: this.pageSize});
13831             
13832         }
13833         
13834         if (this.pageTb && this.allowBlank && !this.disableClear) {
13835             var _this = this;
13836             this.pageTb.add(new Roo.Toolbar.Fill(), {
13837                 cls: 'x-btn-icon x-btn-clear',
13838                 text: '&#160;',
13839                 handler: function()
13840                 {
13841                     _this.collapse();
13842                     _this.clearValue();
13843                     _this.onSelect(false, -1);
13844                 }
13845             });
13846         }
13847         if (this.footer) {
13848             this.assetHeight += this.footer.getHeight();
13849         }
13850         */
13851             
13852         if(!this.tpl){
13853             this.tpl = Roo.bootstrap.version == 4 ?
13854                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
13855                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13856         }
13857
13858         this.view = new Roo.View(this.list, this.tpl, {
13859             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13860         });
13861         //this.view.wrapEl.setDisplayed(false);
13862         this.view.on('click', this.onViewClick, this);
13863         
13864         
13865         this.store.on('beforeload', this.onBeforeLoad, this);
13866         this.store.on('load', this.onLoad, this);
13867         this.store.on('loadexception', this.onLoadException, this);
13868         /*
13869         if(this.resizable){
13870             this.resizer = new Roo.Resizable(this.list,  {
13871                pinned:true, handles:'se'
13872             });
13873             this.resizer.on('resize', function(r, w, h){
13874                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13875                 this.listWidth = w;
13876                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13877                 this.restrictHeight();
13878             }, this);
13879             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13880         }
13881         */
13882         if(!this.editable){
13883             this.editable = true;
13884             this.setEditable(false);
13885         }
13886         
13887         /*
13888         
13889         if (typeof(this.events.add.listeners) != 'undefined') {
13890             
13891             this.addicon = this.wrap.createChild(
13892                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13893        
13894             this.addicon.on('click', function(e) {
13895                 this.fireEvent('add', this);
13896             }, this);
13897         }
13898         if (typeof(this.events.edit.listeners) != 'undefined') {
13899             
13900             this.editicon = this.wrap.createChild(
13901                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13902             if (this.addicon) {
13903                 this.editicon.setStyle('margin-left', '40px');
13904             }
13905             this.editicon.on('click', function(e) {
13906                 
13907                 // we fire even  if inothing is selected..
13908                 this.fireEvent('edit', this, this.lastData );
13909                 
13910             }, this);
13911         }
13912         */
13913         
13914         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13915             "up" : function(e){
13916                 this.inKeyMode = true;
13917                 this.selectPrev();
13918             },
13919
13920             "down" : function(e){
13921                 if(!this.isExpanded()){
13922                     this.onTriggerClick();
13923                 }else{
13924                     this.inKeyMode = true;
13925                     this.selectNext();
13926                 }
13927             },
13928
13929             "enter" : function(e){
13930 //                this.onViewClick();
13931                 //return true;
13932                 this.collapse();
13933                 
13934                 if(this.fireEvent("specialkey", this, e)){
13935                     this.onViewClick(false);
13936                 }
13937                 
13938                 return true;
13939             },
13940
13941             "esc" : function(e){
13942                 this.collapse();
13943             },
13944
13945             "tab" : function(e){
13946                 this.collapse();
13947                 
13948                 if(this.fireEvent("specialkey", this, e)){
13949                     this.onViewClick(false);
13950                 }
13951                 
13952                 return true;
13953             },
13954
13955             scope : this,
13956
13957             doRelay : function(foo, bar, hname){
13958                 if(hname == 'down' || this.scope.isExpanded()){
13959                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13960                 }
13961                 return true;
13962             },
13963
13964             forceKeyDown: true
13965         });
13966         
13967         
13968         this.queryDelay = Math.max(this.queryDelay || 10,
13969                 this.mode == 'local' ? 10 : 250);
13970         
13971         
13972         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13973         
13974         if(this.typeAhead){
13975             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13976         }
13977         if(this.editable !== false){
13978             this.inputEl().on("keyup", this.onKeyUp, this);
13979         }
13980         if(this.forceSelection){
13981             this.inputEl().on('blur', this.doForce, this);
13982         }
13983         
13984         if(this.multiple){
13985             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13986             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13987         }
13988     },
13989     
13990     initTickableEvents: function()
13991     {   
13992         this.createList();
13993         
13994         if(this.hiddenName){
13995             
13996             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13997             
13998             this.hiddenField.dom.value =
13999                 this.hiddenValue !== undefined ? this.hiddenValue :
14000                 this.value !== undefined ? this.value : '';
14001
14002             // prevent input submission
14003             this.el.dom.removeAttribute('name');
14004             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14005              
14006              
14007         }
14008         
14009 //        this.list = this.el.select('ul.dropdown-menu',true).first();
14010         
14011         this.choices = this.el.select('ul.roo-select2-choices', true).first();
14012         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14013         if(this.triggerList){
14014             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
14015         }
14016          
14017         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
14018         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
14019         
14020         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
14021         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
14022         
14023         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
14024         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
14025         
14026         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
14027         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
14028         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
14029         
14030         this.okBtn.hide();
14031         this.cancelBtn.hide();
14032         
14033         var _this = this;
14034         
14035         (function(){
14036             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
14037             _this.list.setWidth(lw);
14038         }).defer(100);
14039         
14040         this.list.on('mouseover', this.onViewOver, this);
14041         this.list.on('mousemove', this.onViewMove, this);
14042         
14043         this.list.on('scroll', this.onViewScroll, this);
14044         
14045         if(!this.tpl){
14046             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
14047                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
14048         }
14049
14050         this.view = new Roo.View(this.list, this.tpl, {
14051             singleSelect:true,
14052             tickable:true,
14053             parent:this,
14054             store: this.store,
14055             selectedClass: this.selectedClass
14056         });
14057         
14058         //this.view.wrapEl.setDisplayed(false);
14059         this.view.on('click', this.onViewClick, this);
14060         
14061         
14062         
14063         this.store.on('beforeload', this.onBeforeLoad, this);
14064         this.store.on('load', this.onLoad, this);
14065         this.store.on('loadexception', this.onLoadException, this);
14066         
14067         if(this.editable){
14068             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
14069                 "up" : function(e){
14070                     this.inKeyMode = true;
14071                     this.selectPrev();
14072                 },
14073
14074                 "down" : function(e){
14075                     this.inKeyMode = true;
14076                     this.selectNext();
14077                 },
14078
14079                 "enter" : function(e){
14080                     if(this.fireEvent("specialkey", this, e)){
14081                         this.onViewClick(false);
14082                     }
14083                     
14084                     return true;
14085                 },
14086
14087                 "esc" : function(e){
14088                     this.onTickableFooterButtonClick(e, false, false);
14089                 },
14090
14091                 "tab" : function(e){
14092                     this.fireEvent("specialkey", this, e);
14093                     
14094                     this.onTickableFooterButtonClick(e, false, false);
14095                     
14096                     return true;
14097                 },
14098
14099                 scope : this,
14100
14101                 doRelay : function(e, fn, key){
14102                     if(this.scope.isExpanded()){
14103                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14104                     }
14105                     return true;
14106                 },
14107
14108                 forceKeyDown: true
14109             });
14110         }
14111         
14112         this.queryDelay = Math.max(this.queryDelay || 10,
14113                 this.mode == 'local' ? 10 : 250);
14114         
14115         
14116         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14117         
14118         if(this.typeAhead){
14119             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14120         }
14121         
14122         if(this.editable !== false){
14123             this.tickableInputEl().on("keyup", this.onKeyUp, this);
14124         }
14125         
14126         this.indicator = this.indicatorEl();
14127         
14128         if(this.indicator){
14129             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
14130             this.indicator.hide();
14131         }
14132         
14133     },
14134
14135     onDestroy : function(){
14136         if(this.view){
14137             this.view.setStore(null);
14138             this.view.el.removeAllListeners();
14139             this.view.el.remove();
14140             this.view.purgeListeners();
14141         }
14142         if(this.list){
14143             this.list.dom.innerHTML  = '';
14144         }
14145         
14146         if(this.store){
14147             this.store.un('beforeload', this.onBeforeLoad, this);
14148             this.store.un('load', this.onLoad, this);
14149             this.store.un('loadexception', this.onLoadException, this);
14150         }
14151         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
14152     },
14153
14154     // private
14155     fireKey : function(e){
14156         if(e.isNavKeyPress() && !this.list.isVisible()){
14157             this.fireEvent("specialkey", this, e);
14158         }
14159     },
14160
14161     // private
14162     onResize: function(w, h){
14163 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14164 //        
14165 //        if(typeof w != 'number'){
14166 //            // we do not handle it!?!?
14167 //            return;
14168 //        }
14169 //        var tw = this.trigger.getWidth();
14170 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
14171 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
14172 //        var x = w - tw;
14173 //        this.inputEl().setWidth( this.adjustWidth('input', x));
14174 //            
14175 //        //this.trigger.setStyle('left', x+'px');
14176 //        
14177 //        if(this.list && this.listWidth === undefined){
14178 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14179 //            this.list.setWidth(lw);
14180 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14181 //        }
14182         
14183     
14184         
14185     },
14186
14187     /**
14188      * Allow or prevent the user from directly editing the field text.  If false is passed,
14189      * the user will only be able to select from the items defined in the dropdown list.  This method
14190      * is the runtime equivalent of setting the 'editable' config option at config time.
14191      * @param {Boolean} value True to allow the user to directly edit the field text
14192      */
14193     setEditable : function(value){
14194         if(value == this.editable){
14195             return;
14196         }
14197         this.editable = value;
14198         if(!value){
14199             this.inputEl().dom.setAttribute('readOnly', true);
14200             this.inputEl().on('mousedown', this.onTriggerClick,  this);
14201             this.inputEl().addClass('x-combo-noedit');
14202         }else{
14203             this.inputEl().dom.setAttribute('readOnly', false);
14204             this.inputEl().un('mousedown', this.onTriggerClick,  this);
14205             this.inputEl().removeClass('x-combo-noedit');
14206         }
14207     },
14208
14209     // private
14210     
14211     onBeforeLoad : function(combo,opts){
14212         if(!this.hasFocus){
14213             return;
14214         }
14215          if (!opts.add) {
14216             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14217          }
14218         this.restrictHeight();
14219         this.selectedIndex = -1;
14220     },
14221
14222     // private
14223     onLoad : function(){
14224         
14225         this.hasQuery = false;
14226         
14227         if(!this.hasFocus){
14228             return;
14229         }
14230         
14231         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14232             this.loading.hide();
14233         }
14234         
14235         if(this.store.getCount() > 0){
14236             
14237             this.expand();
14238             this.restrictHeight();
14239             if(this.lastQuery == this.allQuery){
14240                 if(this.editable && !this.tickable){
14241                     this.inputEl().dom.select();
14242                 }
14243                 
14244                 if(
14245                     !this.selectByValue(this.value, true) &&
14246                     this.autoFocus && 
14247                     (
14248                         !this.store.lastOptions ||
14249                         typeof(this.store.lastOptions.add) == 'undefined' || 
14250                         this.store.lastOptions.add != true
14251                     )
14252                 ){
14253                     this.select(0, true);
14254                 }
14255             }else{
14256                 if(this.autoFocus){
14257                     this.selectNext();
14258                 }
14259                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14260                     this.taTask.delay(this.typeAheadDelay);
14261                 }
14262             }
14263         }else{
14264             this.onEmptyResults();
14265         }
14266         
14267         //this.el.focus();
14268     },
14269     // private
14270     onLoadException : function()
14271     {
14272         this.hasQuery = false;
14273         
14274         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14275             this.loading.hide();
14276         }
14277         
14278         if(this.tickable && this.editable){
14279             return;
14280         }
14281         
14282         this.collapse();
14283         // only causes errors at present
14284         //Roo.log(this.store.reader.jsonData);
14285         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14286             // fixme
14287             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14288         //}
14289         
14290         
14291     },
14292     // private
14293     onTypeAhead : function(){
14294         if(this.store.getCount() > 0){
14295             var r = this.store.getAt(0);
14296             var newValue = r.data[this.displayField];
14297             var len = newValue.length;
14298             var selStart = this.getRawValue().length;
14299             
14300             if(selStart != len){
14301                 this.setRawValue(newValue);
14302                 this.selectText(selStart, newValue.length);
14303             }
14304         }
14305     },
14306
14307     // private
14308     onSelect : function(record, index){
14309         
14310         if(this.fireEvent('beforeselect', this, record, index) !== false){
14311         
14312             this.setFromData(index > -1 ? record.data : false);
14313             
14314             this.collapse();
14315             this.fireEvent('select', this, record, index);
14316         }
14317     },
14318
14319     /**
14320      * Returns the currently selected field value or empty string if no value is set.
14321      * @return {String} value The selected value
14322      */
14323     getValue : function()
14324     {
14325         if(Roo.isIOS && this.useNativeIOS){
14326             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14327         }
14328         
14329         if(this.multiple){
14330             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14331         }
14332         
14333         if(this.valueField){
14334             return typeof this.value != 'undefined' ? this.value : '';
14335         }else{
14336             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14337         }
14338     },
14339     
14340     getRawValue : function()
14341     {
14342         if(Roo.isIOS && this.useNativeIOS){
14343             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14344         }
14345         
14346         var v = this.inputEl().getValue();
14347         
14348         return v;
14349     },
14350
14351     /**
14352      * Clears any text/value currently set in the field
14353      */
14354     clearValue : function(){
14355         
14356         if(this.hiddenField){
14357             this.hiddenField.dom.value = '';
14358         }
14359         this.value = '';
14360         this.setRawValue('');
14361         this.lastSelectionText = '';
14362         this.lastData = false;
14363         
14364         var close = this.closeTriggerEl();
14365         
14366         if(close){
14367             close.hide();
14368         }
14369         
14370         this.validate();
14371         
14372     },
14373
14374     /**
14375      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14376      * will be displayed in the field.  If the value does not match the data value of an existing item,
14377      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14378      * Otherwise the field will be blank (although the value will still be set).
14379      * @param {String} value The value to match
14380      */
14381     setValue : function(v)
14382     {
14383         if(Roo.isIOS && this.useNativeIOS){
14384             this.setIOSValue(v);
14385             return;
14386         }
14387         
14388         if(this.multiple){
14389             this.syncValue();
14390             return;
14391         }
14392         
14393         var text = v;
14394         if(this.valueField){
14395             var r = this.findRecord(this.valueField, v);
14396             if(r){
14397                 text = r.data[this.displayField];
14398             }else if(this.valueNotFoundText !== undefined){
14399                 text = this.valueNotFoundText;
14400             }
14401         }
14402         this.lastSelectionText = text;
14403         if(this.hiddenField){
14404             this.hiddenField.dom.value = v;
14405         }
14406         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14407         this.value = v;
14408         
14409         var close = this.closeTriggerEl();
14410         
14411         if(close){
14412             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14413         }
14414         
14415         this.validate();
14416     },
14417     /**
14418      * @property {Object} the last set data for the element
14419      */
14420     
14421     lastData : false,
14422     /**
14423      * Sets the value of the field based on a object which is related to the record format for the store.
14424      * @param {Object} value the value to set as. or false on reset?
14425      */
14426     setFromData : function(o){
14427         
14428         if(this.multiple){
14429             this.addItem(o);
14430             return;
14431         }
14432             
14433         var dv = ''; // display value
14434         var vv = ''; // value value..
14435         this.lastData = o;
14436         if (this.displayField) {
14437             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14438         } else {
14439             // this is an error condition!!!
14440             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14441         }
14442         
14443         if(this.valueField){
14444             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14445         }
14446         
14447         var close = this.closeTriggerEl();
14448         
14449         if(close){
14450             if(dv.length || vv * 1 > 0){
14451                 close.show() ;
14452                 this.blockFocus=true;
14453             } else {
14454                 close.hide();
14455             }             
14456         }
14457         
14458         if(this.hiddenField){
14459             this.hiddenField.dom.value = vv;
14460             
14461             this.lastSelectionText = dv;
14462             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14463             this.value = vv;
14464             return;
14465         }
14466         // no hidden field.. - we store the value in 'value', but still display
14467         // display field!!!!
14468         this.lastSelectionText = dv;
14469         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14470         this.value = vv;
14471         
14472         
14473         
14474     },
14475     // private
14476     reset : function(){
14477         // overridden so that last data is reset..
14478         
14479         if(this.multiple){
14480             this.clearItem();
14481             return;
14482         }
14483         
14484         this.setValue(this.originalValue);
14485         //this.clearInvalid();
14486         this.lastData = false;
14487         if (this.view) {
14488             this.view.clearSelections();
14489         }
14490         
14491         this.validate();
14492     },
14493     // private
14494     findRecord : function(prop, value){
14495         var record;
14496         if(this.store.getCount() > 0){
14497             this.store.each(function(r){
14498                 if(r.data[prop] == value){
14499                     record = r;
14500                     return false;
14501                 }
14502                 return true;
14503             });
14504         }
14505         return record;
14506     },
14507     
14508     getName: function()
14509     {
14510         // returns hidden if it's set..
14511         if (!this.rendered) {return ''};
14512         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14513         
14514     },
14515     // private
14516     onViewMove : function(e, t){
14517         this.inKeyMode = false;
14518     },
14519
14520     // private
14521     onViewOver : function(e, t){
14522         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14523             return;
14524         }
14525         var item = this.view.findItemFromChild(t);
14526         
14527         if(item){
14528             var index = this.view.indexOf(item);
14529             this.select(index, false);
14530         }
14531     },
14532
14533     // private
14534     onViewClick : function(view, doFocus, el, e)
14535     {
14536         var index = this.view.getSelectedIndexes()[0];
14537         
14538         var r = this.store.getAt(index);
14539         
14540         if(this.tickable){
14541             
14542             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14543                 return;
14544             }
14545             
14546             var rm = false;
14547             var _this = this;
14548             
14549             Roo.each(this.tickItems, function(v,k){
14550                 
14551                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14552                     Roo.log(v);
14553                     _this.tickItems.splice(k, 1);
14554                     
14555                     if(typeof(e) == 'undefined' && view == false){
14556                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14557                     }
14558                     
14559                     rm = true;
14560                     return;
14561                 }
14562             });
14563             
14564             if(rm){
14565                 return;
14566             }
14567             
14568             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14569                 this.tickItems.push(r.data);
14570             }
14571             
14572             if(typeof(e) == 'undefined' && view == false){
14573                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14574             }
14575                     
14576             return;
14577         }
14578         
14579         if(r){
14580             this.onSelect(r, index);
14581         }
14582         if(doFocus !== false && !this.blockFocus){
14583             this.inputEl().focus();
14584         }
14585     },
14586
14587     // private
14588     restrictHeight : function(){
14589         //this.innerList.dom.style.height = '';
14590         //var inner = this.innerList.dom;
14591         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14592         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14593         //this.list.beginUpdate();
14594         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14595         this.list.alignTo(this.inputEl(), this.listAlign);
14596         this.list.alignTo(this.inputEl(), this.listAlign);
14597         //this.list.endUpdate();
14598     },
14599
14600     // private
14601     onEmptyResults : function(){
14602         
14603         if(this.tickable && this.editable){
14604             this.hasFocus = false;
14605             this.restrictHeight();
14606             return;
14607         }
14608         
14609         this.collapse();
14610     },
14611
14612     /**
14613      * Returns true if the dropdown list is expanded, else false.
14614      */
14615     isExpanded : function(){
14616         return this.list.isVisible();
14617     },
14618
14619     /**
14620      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14621      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14622      * @param {String} value The data value of the item to select
14623      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14624      * selected item if it is not currently in view (defaults to true)
14625      * @return {Boolean} True if the value matched an item in the list, else false
14626      */
14627     selectByValue : function(v, scrollIntoView){
14628         if(v !== undefined && v !== null){
14629             var r = this.findRecord(this.valueField || this.displayField, v);
14630             if(r){
14631                 this.select(this.store.indexOf(r), scrollIntoView);
14632                 return true;
14633             }
14634         }
14635         return false;
14636     },
14637
14638     /**
14639      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14640      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14641      * @param {Number} index The zero-based index of the list item to select
14642      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14643      * selected item if it is not currently in view (defaults to true)
14644      */
14645     select : function(index, scrollIntoView){
14646         this.selectedIndex = index;
14647         this.view.select(index);
14648         if(scrollIntoView !== false){
14649             var el = this.view.getNode(index);
14650             /*
14651              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14652              */
14653             if(el){
14654                 this.list.scrollChildIntoView(el, false);
14655             }
14656         }
14657     },
14658
14659     // private
14660     selectNext : function(){
14661         var ct = this.store.getCount();
14662         if(ct > 0){
14663             if(this.selectedIndex == -1){
14664                 this.select(0);
14665             }else if(this.selectedIndex < ct-1){
14666                 this.select(this.selectedIndex+1);
14667             }
14668         }
14669     },
14670
14671     // private
14672     selectPrev : function(){
14673         var ct = this.store.getCount();
14674         if(ct > 0){
14675             if(this.selectedIndex == -1){
14676                 this.select(0);
14677             }else if(this.selectedIndex != 0){
14678                 this.select(this.selectedIndex-1);
14679             }
14680         }
14681     },
14682
14683     // private
14684     onKeyUp : function(e){
14685         if(this.editable !== false && !e.isSpecialKey()){
14686             this.lastKey = e.getKey();
14687             this.dqTask.delay(this.queryDelay);
14688         }
14689     },
14690
14691     // private
14692     validateBlur : function(){
14693         return !this.list || !this.list.isVisible();   
14694     },
14695
14696     // private
14697     initQuery : function(){
14698         
14699         var v = this.getRawValue();
14700         
14701         if(this.tickable && this.editable){
14702             v = this.tickableInputEl().getValue();
14703         }
14704         
14705         this.doQuery(v);
14706     },
14707
14708     // private
14709     doForce : function(){
14710         if(this.inputEl().dom.value.length > 0){
14711             this.inputEl().dom.value =
14712                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14713              
14714         }
14715     },
14716
14717     /**
14718      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14719      * query allowing the query action to be canceled if needed.
14720      * @param {String} query The SQL query to execute
14721      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14722      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14723      * saved in the current store (defaults to false)
14724      */
14725     doQuery : function(q, forceAll){
14726         
14727         if(q === undefined || q === null){
14728             q = '';
14729         }
14730         var qe = {
14731             query: q,
14732             forceAll: forceAll,
14733             combo: this,
14734             cancel:false
14735         };
14736         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14737             return false;
14738         }
14739         q = qe.query;
14740         
14741         forceAll = qe.forceAll;
14742         if(forceAll === true || (q.length >= this.minChars)){
14743             
14744             this.hasQuery = true;
14745             
14746             if(this.lastQuery != q || this.alwaysQuery){
14747                 this.lastQuery = q;
14748                 if(this.mode == 'local'){
14749                     this.selectedIndex = -1;
14750                     if(forceAll){
14751                         this.store.clearFilter();
14752                     }else{
14753                         
14754                         if(this.specialFilter){
14755                             this.fireEvent('specialfilter', this);
14756                             this.onLoad();
14757                             return;
14758                         }
14759                         
14760                         this.store.filter(this.displayField, q);
14761                     }
14762                     
14763                     this.store.fireEvent("datachanged", this.store);
14764                     
14765                     this.onLoad();
14766                     
14767                     
14768                 }else{
14769                     
14770                     this.store.baseParams[this.queryParam] = q;
14771                     
14772                     var options = {params : this.getParams(q)};
14773                     
14774                     if(this.loadNext){
14775                         options.add = true;
14776                         options.params.start = this.page * this.pageSize;
14777                     }
14778                     
14779                     this.store.load(options);
14780                     
14781                     /*
14782                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14783                      *  we should expand the list on onLoad
14784                      *  so command out it
14785                      */
14786 //                    this.expand();
14787                 }
14788             }else{
14789                 this.selectedIndex = -1;
14790                 this.onLoad();   
14791             }
14792         }
14793         
14794         this.loadNext = false;
14795     },
14796     
14797     // private
14798     getParams : function(q){
14799         var p = {};
14800         //p[this.queryParam] = q;
14801         
14802         if(this.pageSize){
14803             p.start = 0;
14804             p.limit = this.pageSize;
14805         }
14806         return p;
14807     },
14808
14809     /**
14810      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14811      */
14812     collapse : function(){
14813         if(!this.isExpanded()){
14814             return;
14815         }
14816         
14817         this.list.hide();
14818         
14819         this.hasFocus = false;
14820         
14821         if(this.tickable){
14822             this.okBtn.hide();
14823             this.cancelBtn.hide();
14824             this.trigger.show();
14825             
14826             if(this.editable){
14827                 this.tickableInputEl().dom.value = '';
14828                 this.tickableInputEl().blur();
14829             }
14830             
14831         }
14832         
14833         Roo.get(document).un('mousedown', this.collapseIf, this);
14834         Roo.get(document).un('mousewheel', this.collapseIf, this);
14835         if (!this.editable) {
14836             Roo.get(document).un('keydown', this.listKeyPress, this);
14837         }
14838         this.fireEvent('collapse', this);
14839         
14840         this.validate();
14841     },
14842
14843     // private
14844     collapseIf : function(e){
14845         var in_combo  = e.within(this.el);
14846         var in_list =  e.within(this.list);
14847         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14848         
14849         if (in_combo || in_list || is_list) {
14850             //e.stopPropagation();
14851             return;
14852         }
14853         
14854         if(this.tickable){
14855             this.onTickableFooterButtonClick(e, false, false);
14856         }
14857
14858         this.collapse();
14859         
14860     },
14861
14862     /**
14863      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14864      */
14865     expand : function(){
14866        
14867         if(this.isExpanded() || !this.hasFocus){
14868             return;
14869         }
14870         
14871         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14872         this.list.setWidth(lw);
14873         
14874         Roo.log('expand');
14875         
14876         this.list.show();
14877         
14878         this.restrictHeight();
14879         
14880         if(this.tickable){
14881             
14882             this.tickItems = Roo.apply([], this.item);
14883             
14884             this.okBtn.show();
14885             this.cancelBtn.show();
14886             this.trigger.hide();
14887             
14888             if(this.editable){
14889                 this.tickableInputEl().focus();
14890             }
14891             
14892         }
14893         
14894         Roo.get(document).on('mousedown', this.collapseIf, this);
14895         Roo.get(document).on('mousewheel', this.collapseIf, this);
14896         if (!this.editable) {
14897             Roo.get(document).on('keydown', this.listKeyPress, this);
14898         }
14899         
14900         this.fireEvent('expand', this);
14901     },
14902
14903     // private
14904     // Implements the default empty TriggerField.onTriggerClick function
14905     onTriggerClick : function(e)
14906     {
14907         Roo.log('trigger click');
14908         
14909         if(this.disabled || !this.triggerList){
14910             return;
14911         }
14912         
14913         this.page = 0;
14914         this.loadNext = false;
14915         
14916         if(this.isExpanded()){
14917             this.collapse();
14918             if (!this.blockFocus) {
14919                 this.inputEl().focus();
14920             }
14921             
14922         }else {
14923             this.hasFocus = true;
14924             if(this.triggerAction == 'all') {
14925                 this.doQuery(this.allQuery, true);
14926             } else {
14927                 this.doQuery(this.getRawValue());
14928             }
14929             if (!this.blockFocus) {
14930                 this.inputEl().focus();
14931             }
14932         }
14933     },
14934     
14935     onTickableTriggerClick : function(e)
14936     {
14937         if(this.disabled){
14938             return;
14939         }
14940         
14941         this.page = 0;
14942         this.loadNext = false;
14943         this.hasFocus = true;
14944         
14945         if(this.triggerAction == 'all') {
14946             this.doQuery(this.allQuery, true);
14947         } else {
14948             this.doQuery(this.getRawValue());
14949         }
14950     },
14951     
14952     onSearchFieldClick : function(e)
14953     {
14954         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14955             this.onTickableFooterButtonClick(e, false, false);
14956             return;
14957         }
14958         
14959         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14960             return;
14961         }
14962         
14963         this.page = 0;
14964         this.loadNext = false;
14965         this.hasFocus = true;
14966         
14967         if(this.triggerAction == 'all') {
14968             this.doQuery(this.allQuery, true);
14969         } else {
14970             this.doQuery(this.getRawValue());
14971         }
14972     },
14973     
14974     listKeyPress : function(e)
14975     {
14976         //Roo.log('listkeypress');
14977         // scroll to first matching element based on key pres..
14978         if (e.isSpecialKey()) {
14979             return false;
14980         }
14981         var k = String.fromCharCode(e.getKey()).toUpperCase();
14982         //Roo.log(k);
14983         var match  = false;
14984         var csel = this.view.getSelectedNodes();
14985         var cselitem = false;
14986         if (csel.length) {
14987             var ix = this.view.indexOf(csel[0]);
14988             cselitem  = this.store.getAt(ix);
14989             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14990                 cselitem = false;
14991             }
14992             
14993         }
14994         
14995         this.store.each(function(v) { 
14996             if (cselitem) {
14997                 // start at existing selection.
14998                 if (cselitem.id == v.id) {
14999                     cselitem = false;
15000                 }
15001                 return true;
15002             }
15003                 
15004             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
15005                 match = this.store.indexOf(v);
15006                 return false;
15007             }
15008             return true;
15009         }, this);
15010         
15011         if (match === false) {
15012             return true; // no more action?
15013         }
15014         // scroll to?
15015         this.view.select(match);
15016         var sn = Roo.get(this.view.getSelectedNodes()[0]);
15017         sn.scrollIntoView(sn.dom.parentNode, false);
15018     },
15019     
15020     onViewScroll : function(e, t){
15021         
15022         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){
15023             return;
15024         }
15025         
15026         this.hasQuery = true;
15027         
15028         this.loading = this.list.select('.loading', true).first();
15029         
15030         if(this.loading === null){
15031             this.list.createChild({
15032                 tag: 'div',
15033                 cls: 'loading roo-select2-more-results roo-select2-active',
15034                 html: 'Loading more results...'
15035             });
15036             
15037             this.loading = this.list.select('.loading', true).first();
15038             
15039             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
15040             
15041             this.loading.hide();
15042         }
15043         
15044         this.loading.show();
15045         
15046         var _combo = this;
15047         
15048         this.page++;
15049         this.loadNext = true;
15050         
15051         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
15052         
15053         return;
15054     },
15055     
15056     addItem : function(o)
15057     {   
15058         var dv = ''; // display value
15059         
15060         if (this.displayField) {
15061             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
15062         } else {
15063             // this is an error condition!!!
15064             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
15065         }
15066         
15067         if(!dv.length){
15068             return;
15069         }
15070         
15071         var choice = this.choices.createChild({
15072             tag: 'li',
15073             cls: 'roo-select2-search-choice',
15074             cn: [
15075                 {
15076                     tag: 'div',
15077                     html: dv
15078                 },
15079                 {
15080                     tag: 'a',
15081                     href: '#',
15082                     cls: 'roo-select2-search-choice-close fa fa-times',
15083                     tabindex: '-1'
15084                 }
15085             ]
15086             
15087         }, this.searchField);
15088         
15089         var close = choice.select('a.roo-select2-search-choice-close', true).first();
15090         
15091         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
15092         
15093         this.item.push(o);
15094         
15095         this.lastData = o;
15096         
15097         this.syncValue();
15098         
15099         this.inputEl().dom.value = '';
15100         
15101         this.validate();
15102     },
15103     
15104     onRemoveItem : function(e, _self, o)
15105     {
15106         e.preventDefault();
15107         
15108         this.lastItem = Roo.apply([], this.item);
15109         
15110         var index = this.item.indexOf(o.data) * 1;
15111         
15112         if( index < 0){
15113             Roo.log('not this item?!');
15114             return;
15115         }
15116         
15117         this.item.splice(index, 1);
15118         o.item.remove();
15119         
15120         this.syncValue();
15121         
15122         this.fireEvent('remove', this, e);
15123         
15124         this.validate();
15125         
15126     },
15127     
15128     syncValue : function()
15129     {
15130         if(!this.item.length){
15131             this.clearValue();
15132             return;
15133         }
15134             
15135         var value = [];
15136         var _this = this;
15137         Roo.each(this.item, function(i){
15138             if(_this.valueField){
15139                 value.push(i[_this.valueField]);
15140                 return;
15141             }
15142
15143             value.push(i);
15144         });
15145
15146         this.value = value.join(',');
15147
15148         if(this.hiddenField){
15149             this.hiddenField.dom.value = this.value;
15150         }
15151         
15152         this.store.fireEvent("datachanged", this.store);
15153         
15154         this.validate();
15155     },
15156     
15157     clearItem : function()
15158     {
15159         if(!this.multiple){
15160             return;
15161         }
15162         
15163         this.item = [];
15164         
15165         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15166            c.remove();
15167         });
15168         
15169         this.syncValue();
15170         
15171         this.validate();
15172         
15173         if(this.tickable && !Roo.isTouch){
15174             this.view.refresh();
15175         }
15176     },
15177     
15178     inputEl: function ()
15179     {
15180         if(Roo.isIOS && this.useNativeIOS){
15181             return this.el.select('select.roo-ios-select', true).first();
15182         }
15183         
15184         if(Roo.isTouch && this.mobileTouchView){
15185             return this.el.select('input.form-control',true).first();
15186         }
15187         
15188         if(this.tickable){
15189             return this.searchField;
15190         }
15191         
15192         return this.el.select('input.form-control',true).first();
15193     },
15194     
15195     onTickableFooterButtonClick : function(e, btn, el)
15196     {
15197         e.preventDefault();
15198         
15199         this.lastItem = Roo.apply([], this.item);
15200         
15201         if(btn && btn.name == 'cancel'){
15202             this.tickItems = Roo.apply([], this.item);
15203             this.collapse();
15204             return;
15205         }
15206         
15207         this.clearItem();
15208         
15209         var _this = this;
15210         
15211         Roo.each(this.tickItems, function(o){
15212             _this.addItem(o);
15213         });
15214         
15215         this.collapse();
15216         
15217     },
15218     
15219     validate : function()
15220     {
15221         if(this.getVisibilityEl().hasClass('hidden')){
15222             return true;
15223         }
15224         
15225         var v = this.getRawValue();
15226         
15227         if(this.multiple){
15228             v = this.getValue();
15229         }
15230         
15231         if(this.disabled || this.allowBlank || v.length){
15232             this.markValid();
15233             return true;
15234         }
15235         
15236         this.markInvalid();
15237         return false;
15238     },
15239     
15240     tickableInputEl : function()
15241     {
15242         if(!this.tickable || !this.editable){
15243             return this.inputEl();
15244         }
15245         
15246         return this.inputEl().select('.roo-select2-search-field-input', true).first();
15247     },
15248     
15249     
15250     getAutoCreateTouchView : function()
15251     {
15252         var id = Roo.id();
15253         
15254         var cfg = {
15255             cls: 'form-group' //input-group
15256         };
15257         
15258         var input =  {
15259             tag: 'input',
15260             id : id,
15261             type : this.inputType,
15262             cls : 'form-control x-combo-noedit',
15263             autocomplete: 'new-password',
15264             placeholder : this.placeholder || '',
15265             readonly : true
15266         };
15267         
15268         if (this.name) {
15269             input.name = this.name;
15270         }
15271         
15272         if (this.size) {
15273             input.cls += ' input-' + this.size;
15274         }
15275         
15276         if (this.disabled) {
15277             input.disabled = true;
15278         }
15279         
15280         var inputblock = {
15281             cls : '',
15282             cn : [
15283                 input
15284             ]
15285         };
15286         
15287         if(this.before){
15288             inputblock.cls += ' input-group';
15289             
15290             inputblock.cn.unshift({
15291                 tag :'span',
15292                 cls : 'input-group-addon input-group-prepend input-group-text',
15293                 html : this.before
15294             });
15295         }
15296         
15297         if(this.removable && !this.multiple){
15298             inputblock.cls += ' roo-removable';
15299             
15300             inputblock.cn.push({
15301                 tag: 'button',
15302                 html : 'x',
15303                 cls : 'roo-combo-removable-btn close'
15304             });
15305         }
15306
15307         if(this.hasFeedback && !this.allowBlank){
15308             
15309             inputblock.cls += ' has-feedback';
15310             
15311             inputblock.cn.push({
15312                 tag: 'span',
15313                 cls: 'glyphicon form-control-feedback'
15314             });
15315             
15316         }
15317         
15318         if (this.after) {
15319             
15320             inputblock.cls += (this.before) ? '' : ' input-group';
15321             
15322             inputblock.cn.push({
15323                 tag :'span',
15324                 cls : 'input-group-addon input-group-append input-group-text',
15325                 html : this.after
15326             });
15327         }
15328
15329         
15330         var ibwrap = inputblock;
15331         
15332         if(this.multiple){
15333             ibwrap = {
15334                 tag: 'ul',
15335                 cls: 'roo-select2-choices',
15336                 cn:[
15337                     {
15338                         tag: 'li',
15339                         cls: 'roo-select2-search-field',
15340                         cn: [
15341
15342                             inputblock
15343                         ]
15344                     }
15345                 ]
15346             };
15347         
15348             
15349         }
15350         
15351         var combobox = {
15352             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15353             cn: [
15354                 {
15355                     tag: 'input',
15356                     type : 'hidden',
15357                     cls: 'form-hidden-field'
15358                 },
15359                 ibwrap
15360             ]
15361         };
15362         
15363         if(!this.multiple && this.showToggleBtn){
15364             
15365             var caret = {
15366                         tag: 'span',
15367                         cls: 'caret'
15368             };
15369             
15370             if (this.caret != false) {
15371                 caret = {
15372                      tag: 'i',
15373                      cls: 'fa fa-' + this.caret
15374                 };
15375                 
15376             }
15377             
15378             combobox.cn.push({
15379                 tag :'span',
15380                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15381                 cn : [
15382                     caret,
15383                     {
15384                         tag: 'span',
15385                         cls: 'combobox-clear',
15386                         cn  : [
15387                             {
15388                                 tag : 'i',
15389                                 cls: 'icon-remove'
15390                             }
15391                         ]
15392                     }
15393                 ]
15394
15395             })
15396         }
15397         
15398         if(this.multiple){
15399             combobox.cls += ' roo-select2-container-multi';
15400         }
15401         
15402         var align = this.labelAlign || this.parentLabelAlign();
15403         
15404         if (align ==='left' && this.fieldLabel.length) {
15405
15406             cfg.cn = [
15407                 {
15408                    tag : 'i',
15409                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15410                    tooltip : 'This field is required'
15411                 },
15412                 {
15413                     tag: 'label',
15414                     cls : 'control-label col-form-label',
15415                     html : this.fieldLabel
15416
15417                 },
15418                 {
15419                     cls : '', 
15420                     cn: [
15421                         combobox
15422                     ]
15423                 }
15424             ];
15425             
15426             var labelCfg = cfg.cn[1];
15427             var contentCfg = cfg.cn[2];
15428             
15429
15430             if(this.indicatorpos == 'right'){
15431                 cfg.cn = [
15432                     {
15433                         tag: 'label',
15434                         'for' :  id,
15435                         cls : 'control-label col-form-label',
15436                         cn : [
15437                             {
15438                                 tag : 'span',
15439                                 html : this.fieldLabel
15440                             },
15441                             {
15442                                 tag : 'i',
15443                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15444                                 tooltip : 'This field is required'
15445                             }
15446                         ]
15447                     },
15448                     {
15449                         cls : "",
15450                         cn: [
15451                             combobox
15452                         ]
15453                     }
15454
15455                 ];
15456                 
15457                 labelCfg = cfg.cn[0];
15458                 contentCfg = cfg.cn[1];
15459             }
15460             
15461            
15462             
15463             if(this.labelWidth > 12){
15464                 labelCfg.style = "width: " + this.labelWidth + 'px';
15465             }
15466             
15467             if(this.labelWidth < 13 && this.labelmd == 0){
15468                 this.labelmd = this.labelWidth;
15469             }
15470             
15471             if(this.labellg > 0){
15472                 labelCfg.cls += ' col-lg-' + this.labellg;
15473                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15474             }
15475             
15476             if(this.labelmd > 0){
15477                 labelCfg.cls += ' col-md-' + this.labelmd;
15478                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15479             }
15480             
15481             if(this.labelsm > 0){
15482                 labelCfg.cls += ' col-sm-' + this.labelsm;
15483                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15484             }
15485             
15486             if(this.labelxs > 0){
15487                 labelCfg.cls += ' col-xs-' + this.labelxs;
15488                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15489             }
15490                 
15491                 
15492         } else if ( this.fieldLabel.length) {
15493             cfg.cn = [
15494                 {
15495                    tag : 'i',
15496                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15497                    tooltip : 'This field is required'
15498                 },
15499                 {
15500                     tag: 'label',
15501                     cls : 'control-label',
15502                     html : this.fieldLabel
15503
15504                 },
15505                 {
15506                     cls : '', 
15507                     cn: [
15508                         combobox
15509                     ]
15510                 }
15511             ];
15512             
15513             if(this.indicatorpos == 'right'){
15514                 cfg.cn = [
15515                     {
15516                         tag: 'label',
15517                         cls : 'control-label',
15518                         html : this.fieldLabel,
15519                         cn : [
15520                             {
15521                                tag : 'i',
15522                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15523                                tooltip : 'This field is required'
15524                             }
15525                         ]
15526                     },
15527                     {
15528                         cls : '', 
15529                         cn: [
15530                             combobox
15531                         ]
15532                     }
15533                 ];
15534             }
15535         } else {
15536             cfg.cn = combobox;    
15537         }
15538         
15539         
15540         var settings = this;
15541         
15542         ['xs','sm','md','lg'].map(function(size){
15543             if (settings[size]) {
15544                 cfg.cls += ' col-' + size + '-' + settings[size];
15545             }
15546         });
15547         
15548         return cfg;
15549     },
15550     
15551     initTouchView : function()
15552     {
15553         this.renderTouchView();
15554         
15555         this.touchViewEl.on('scroll', function(){
15556             this.el.dom.scrollTop = 0;
15557         }, this);
15558         
15559         this.originalValue = this.getValue();
15560         
15561         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15562         
15563         this.inputEl().on("click", this.showTouchView, this);
15564         if (this.triggerEl) {
15565             this.triggerEl.on("click", this.showTouchView, this);
15566         }
15567         
15568         
15569         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15570         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15571         
15572         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15573         
15574         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15575         this.store.on('load', this.onTouchViewLoad, this);
15576         this.store.on('loadexception', this.onTouchViewLoadException, this);
15577         
15578         if(this.hiddenName){
15579             
15580             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15581             
15582             this.hiddenField.dom.value =
15583                 this.hiddenValue !== undefined ? this.hiddenValue :
15584                 this.value !== undefined ? this.value : '';
15585         
15586             this.el.dom.removeAttribute('name');
15587             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15588         }
15589         
15590         if(this.multiple){
15591             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15592             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15593         }
15594         
15595         if(this.removable && !this.multiple){
15596             var close = this.closeTriggerEl();
15597             if(close){
15598                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15599                 close.on('click', this.removeBtnClick, this, close);
15600             }
15601         }
15602         /*
15603          * fix the bug in Safari iOS8
15604          */
15605         this.inputEl().on("focus", function(e){
15606             document.activeElement.blur();
15607         }, this);
15608         
15609         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15610         
15611         return;
15612         
15613         
15614     },
15615     
15616     renderTouchView : function()
15617     {
15618         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15619         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15620         
15621         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15622         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15623         
15624         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15625         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15626         this.touchViewBodyEl.setStyle('overflow', 'auto');
15627         
15628         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15629         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15630         
15631         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15632         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15633         
15634     },
15635     
15636     showTouchView : function()
15637     {
15638         if(this.disabled){
15639             return;
15640         }
15641         
15642         this.touchViewHeaderEl.hide();
15643
15644         if(this.modalTitle.length){
15645             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15646             this.touchViewHeaderEl.show();
15647         }
15648
15649         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15650         this.touchViewEl.show();
15651
15652         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15653         
15654         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15655         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15656
15657         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15658
15659         if(this.modalTitle.length){
15660             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15661         }
15662         
15663         this.touchViewBodyEl.setHeight(bodyHeight);
15664
15665         if(this.animate){
15666             var _this = this;
15667             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15668         }else{
15669             this.touchViewEl.addClass('in');
15670         }
15671         
15672         if(this._touchViewMask){
15673             Roo.get(document.body).addClass("x-body-masked");
15674             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15675             this._touchViewMask.setStyle('z-index', 10000);
15676             this._touchViewMask.addClass('show');
15677         }
15678         
15679         this.doTouchViewQuery();
15680         
15681     },
15682     
15683     hideTouchView : function()
15684     {
15685         this.touchViewEl.removeClass('in');
15686
15687         if(this.animate){
15688             var _this = this;
15689             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15690         }else{
15691             this.touchViewEl.setStyle('display', 'none');
15692         }
15693         
15694         if(this._touchViewMask){
15695             this._touchViewMask.removeClass('show');
15696             Roo.get(document.body).removeClass("x-body-masked");
15697         }
15698     },
15699     
15700     setTouchViewValue : function()
15701     {
15702         if(this.multiple){
15703             this.clearItem();
15704         
15705             var _this = this;
15706
15707             Roo.each(this.tickItems, function(o){
15708                 this.addItem(o);
15709             }, this);
15710         }
15711         
15712         this.hideTouchView();
15713     },
15714     
15715     doTouchViewQuery : function()
15716     {
15717         var qe = {
15718             query: '',
15719             forceAll: true,
15720             combo: this,
15721             cancel:false
15722         };
15723         
15724         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15725             return false;
15726         }
15727         
15728         if(!this.alwaysQuery || this.mode == 'local'){
15729             this.onTouchViewLoad();
15730             return;
15731         }
15732         
15733         this.store.load();
15734     },
15735     
15736     onTouchViewBeforeLoad : function(combo,opts)
15737     {
15738         return;
15739     },
15740
15741     // private
15742     onTouchViewLoad : function()
15743     {
15744         if(this.store.getCount() < 1){
15745             this.onTouchViewEmptyResults();
15746             return;
15747         }
15748         
15749         this.clearTouchView();
15750         
15751         var rawValue = this.getRawValue();
15752         
15753         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15754         
15755         this.tickItems = [];
15756         
15757         this.store.data.each(function(d, rowIndex){
15758             var row = this.touchViewListGroup.createChild(template);
15759             
15760             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15761                 row.addClass(d.data.cls);
15762             }
15763             
15764             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15765                 var cfg = {
15766                     data : d.data,
15767                     html : d.data[this.displayField]
15768                 };
15769                 
15770                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15771                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15772                 }
15773             }
15774             row.removeClass('selected');
15775             if(!this.multiple && this.valueField &&
15776                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15777             {
15778                 // radio buttons..
15779                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15780                 row.addClass('selected');
15781             }
15782             
15783             if(this.multiple && this.valueField &&
15784                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15785             {
15786                 
15787                 // checkboxes...
15788                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15789                 this.tickItems.push(d.data);
15790             }
15791             
15792             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15793             
15794         }, this);
15795         
15796         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15797         
15798         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15799
15800         if(this.modalTitle.length){
15801             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15802         }
15803
15804         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15805         
15806         if(this.mobile_restrict_height && listHeight < bodyHeight){
15807             this.touchViewBodyEl.setHeight(listHeight);
15808         }
15809         
15810         var _this = this;
15811         
15812         if(firstChecked && listHeight > bodyHeight){
15813             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15814         }
15815         
15816     },
15817     
15818     onTouchViewLoadException : function()
15819     {
15820         this.hideTouchView();
15821     },
15822     
15823     onTouchViewEmptyResults : function()
15824     {
15825         this.clearTouchView();
15826         
15827         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15828         
15829         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15830         
15831     },
15832     
15833     clearTouchView : function()
15834     {
15835         this.touchViewListGroup.dom.innerHTML = '';
15836     },
15837     
15838     onTouchViewClick : function(e, el, o)
15839     {
15840         e.preventDefault();
15841         
15842         var row = o.row;
15843         var rowIndex = o.rowIndex;
15844         
15845         var r = this.store.getAt(rowIndex);
15846         
15847         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15848             
15849             if(!this.multiple){
15850                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15851                     c.dom.removeAttribute('checked');
15852                 }, this);
15853
15854                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15855
15856                 this.setFromData(r.data);
15857
15858                 var close = this.closeTriggerEl();
15859
15860                 if(close){
15861                     close.show();
15862                 }
15863
15864                 this.hideTouchView();
15865
15866                 this.fireEvent('select', this, r, rowIndex);
15867
15868                 return;
15869             }
15870
15871             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15872                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15873                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15874                 return;
15875             }
15876
15877             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15878             this.addItem(r.data);
15879             this.tickItems.push(r.data);
15880         }
15881     },
15882     
15883     getAutoCreateNativeIOS : function()
15884     {
15885         var cfg = {
15886             cls: 'form-group' //input-group,
15887         };
15888         
15889         var combobox =  {
15890             tag: 'select',
15891             cls : 'roo-ios-select'
15892         };
15893         
15894         if (this.name) {
15895             combobox.name = this.name;
15896         }
15897         
15898         if (this.disabled) {
15899             combobox.disabled = true;
15900         }
15901         
15902         var settings = this;
15903         
15904         ['xs','sm','md','lg'].map(function(size){
15905             if (settings[size]) {
15906                 cfg.cls += ' col-' + size + '-' + settings[size];
15907             }
15908         });
15909         
15910         cfg.cn = combobox;
15911         
15912         return cfg;
15913         
15914     },
15915     
15916     initIOSView : function()
15917     {
15918         this.store.on('load', this.onIOSViewLoad, this);
15919         
15920         return;
15921     },
15922     
15923     onIOSViewLoad : function()
15924     {
15925         if(this.store.getCount() < 1){
15926             return;
15927         }
15928         
15929         this.clearIOSView();
15930         
15931         if(this.allowBlank) {
15932             
15933             var default_text = '-- SELECT --';
15934             
15935             if(this.placeholder.length){
15936                 default_text = this.placeholder;
15937             }
15938             
15939             if(this.emptyTitle.length){
15940                 default_text += ' - ' + this.emptyTitle + ' -';
15941             }
15942             
15943             var opt = this.inputEl().createChild({
15944                 tag: 'option',
15945                 value : 0,
15946                 html : default_text
15947             });
15948             
15949             var o = {};
15950             o[this.valueField] = 0;
15951             o[this.displayField] = default_text;
15952             
15953             this.ios_options.push({
15954                 data : o,
15955                 el : opt
15956             });
15957             
15958         }
15959         
15960         this.store.data.each(function(d, rowIndex){
15961             
15962             var html = '';
15963             
15964             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15965                 html = d.data[this.displayField];
15966             }
15967             
15968             var value = '';
15969             
15970             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15971                 value = d.data[this.valueField];
15972             }
15973             
15974             var option = {
15975                 tag: 'option',
15976                 value : value,
15977                 html : html
15978             };
15979             
15980             if(this.value == d.data[this.valueField]){
15981                 option['selected'] = true;
15982             }
15983             
15984             var opt = this.inputEl().createChild(option);
15985             
15986             this.ios_options.push({
15987                 data : d.data,
15988                 el : opt
15989             });
15990             
15991         }, this);
15992         
15993         this.inputEl().on('change', function(){
15994            this.fireEvent('select', this);
15995         }, this);
15996         
15997     },
15998     
15999     clearIOSView: function()
16000     {
16001         this.inputEl().dom.innerHTML = '';
16002         
16003         this.ios_options = [];
16004     },
16005     
16006     setIOSValue: function(v)
16007     {
16008         this.value = v;
16009         
16010         if(!this.ios_options){
16011             return;
16012         }
16013         
16014         Roo.each(this.ios_options, function(opts){
16015            
16016            opts.el.dom.removeAttribute('selected');
16017            
16018            if(opts.data[this.valueField] != v){
16019                return;
16020            }
16021            
16022            opts.el.dom.setAttribute('selected', true);
16023            
16024         }, this);
16025     }
16026
16027     /** 
16028     * @cfg {Boolean} grow 
16029     * @hide 
16030     */
16031     /** 
16032     * @cfg {Number} growMin 
16033     * @hide 
16034     */
16035     /** 
16036     * @cfg {Number} growMax 
16037     * @hide 
16038     */
16039     /**
16040      * @hide
16041      * @method autoSize
16042      */
16043 });
16044
16045 Roo.apply(Roo.bootstrap.ComboBox,  {
16046     
16047     header : {
16048         tag: 'div',
16049         cls: 'modal-header',
16050         cn: [
16051             {
16052                 tag: 'h4',
16053                 cls: 'modal-title'
16054             }
16055         ]
16056     },
16057     
16058     body : {
16059         tag: 'div',
16060         cls: 'modal-body',
16061         cn: [
16062             {
16063                 tag: 'ul',
16064                 cls: 'list-group'
16065             }
16066         ]
16067     },
16068     
16069     listItemRadio : {
16070         tag: 'li',
16071         cls: 'list-group-item',
16072         cn: [
16073             {
16074                 tag: 'span',
16075                 cls: 'roo-combobox-list-group-item-value'
16076             },
16077             {
16078                 tag: 'div',
16079                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
16080                 cn: [
16081                     {
16082                         tag: 'input',
16083                         type: 'radio'
16084                     },
16085                     {
16086                         tag: 'label'
16087                     }
16088                 ]
16089             }
16090         ]
16091     },
16092     
16093     listItemCheckbox : {
16094         tag: 'li',
16095         cls: 'list-group-item',
16096         cn: [
16097             {
16098                 tag: 'span',
16099                 cls: 'roo-combobox-list-group-item-value'
16100             },
16101             {
16102                 tag: 'div',
16103                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
16104                 cn: [
16105                     {
16106                         tag: 'input',
16107                         type: 'checkbox'
16108                     },
16109                     {
16110                         tag: 'label'
16111                     }
16112                 ]
16113             }
16114         ]
16115     },
16116     
16117     emptyResult : {
16118         tag: 'div',
16119         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
16120     },
16121     
16122     footer : {
16123         tag: 'div',
16124         cls: 'modal-footer',
16125         cn: [
16126             {
16127                 tag: 'div',
16128                 cls: 'row',
16129                 cn: [
16130                     {
16131                         tag: 'div',
16132                         cls: 'col-xs-6 text-left',
16133                         cn: {
16134                             tag: 'button',
16135                             cls: 'btn btn-danger roo-touch-view-cancel',
16136                             html: 'Cancel'
16137                         }
16138                     },
16139                     {
16140                         tag: 'div',
16141                         cls: 'col-xs-6 text-right',
16142                         cn: {
16143                             tag: 'button',
16144                             cls: 'btn btn-success roo-touch-view-ok',
16145                             html: 'OK'
16146                         }
16147                     }
16148                 ]
16149             }
16150         ]
16151         
16152     }
16153 });
16154
16155 Roo.apply(Roo.bootstrap.ComboBox,  {
16156     
16157     touchViewTemplate : {
16158         tag: 'div',
16159         cls: 'modal fade roo-combobox-touch-view',
16160         cn: [
16161             {
16162                 tag: 'div',
16163                 cls: 'modal-dialog',
16164                 style : 'position:fixed', // we have to fix position....
16165                 cn: [
16166                     {
16167                         tag: 'div',
16168                         cls: 'modal-content',
16169                         cn: [
16170                             Roo.bootstrap.ComboBox.header,
16171                             Roo.bootstrap.ComboBox.body,
16172                             Roo.bootstrap.ComboBox.footer
16173                         ]
16174                     }
16175                 ]
16176             }
16177         ]
16178     }
16179 });/*
16180  * Based on:
16181  * Ext JS Library 1.1.1
16182  * Copyright(c) 2006-2007, Ext JS, LLC.
16183  *
16184  * Originally Released Under LGPL - original licence link has changed is not relivant.
16185  *
16186  * Fork - LGPL
16187  * <script type="text/javascript">
16188  */
16189
16190 /**
16191  * @class Roo.View
16192  * @extends Roo.util.Observable
16193  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
16194  * This class also supports single and multi selection modes. <br>
16195  * Create a data model bound view:
16196  <pre><code>
16197  var store = new Roo.data.Store(...);
16198
16199  var view = new Roo.View({
16200     el : "my-element",
16201     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
16202  
16203     singleSelect: true,
16204     selectedClass: "ydataview-selected",
16205     store: store
16206  });
16207
16208  // listen for node click?
16209  view.on("click", function(vw, index, node, e){
16210  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16211  });
16212
16213  // load XML data
16214  dataModel.load("foobar.xml");
16215  </code></pre>
16216  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16217  * <br><br>
16218  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16219  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16220  * 
16221  * Note: old style constructor is still suported (container, template, config)
16222  * 
16223  * @constructor
16224  * Create a new View
16225  * @param {Object} config The config object
16226  * 
16227  */
16228 Roo.View = function(config, depreciated_tpl, depreciated_config){
16229     
16230     this.parent = false;
16231     
16232     if (typeof(depreciated_tpl) == 'undefined') {
16233         // new way.. - universal constructor.
16234         Roo.apply(this, config);
16235         this.el  = Roo.get(this.el);
16236     } else {
16237         // old format..
16238         this.el  = Roo.get(config);
16239         this.tpl = depreciated_tpl;
16240         Roo.apply(this, depreciated_config);
16241     }
16242     this.wrapEl  = this.el.wrap().wrap();
16243     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16244     
16245     
16246     if(typeof(this.tpl) == "string"){
16247         this.tpl = new Roo.Template(this.tpl);
16248     } else {
16249         // support xtype ctors..
16250         this.tpl = new Roo.factory(this.tpl, Roo);
16251     }
16252     
16253     
16254     this.tpl.compile();
16255     
16256     /** @private */
16257     this.addEvents({
16258         /**
16259          * @event beforeclick
16260          * Fires before a click is processed. Returns false to cancel the default action.
16261          * @param {Roo.View} this
16262          * @param {Number} index The index of the target node
16263          * @param {HTMLElement} node The target node
16264          * @param {Roo.EventObject} e The raw event object
16265          */
16266             "beforeclick" : true,
16267         /**
16268          * @event click
16269          * Fires when a template node is clicked.
16270          * @param {Roo.View} this
16271          * @param {Number} index The index of the target node
16272          * @param {HTMLElement} node The target node
16273          * @param {Roo.EventObject} e The raw event object
16274          */
16275             "click" : true,
16276         /**
16277          * @event dblclick
16278          * Fires when a template node is double clicked.
16279          * @param {Roo.View} this
16280          * @param {Number} index The index of the target node
16281          * @param {HTMLElement} node The target node
16282          * @param {Roo.EventObject} e The raw event object
16283          */
16284             "dblclick" : true,
16285         /**
16286          * @event contextmenu
16287          * Fires when a template node is right clicked.
16288          * @param {Roo.View} this
16289          * @param {Number} index The index of the target node
16290          * @param {HTMLElement} node The target node
16291          * @param {Roo.EventObject} e The raw event object
16292          */
16293             "contextmenu" : true,
16294         /**
16295          * @event selectionchange
16296          * Fires when the selected nodes change.
16297          * @param {Roo.View} this
16298          * @param {Array} selections Array of the selected nodes
16299          */
16300             "selectionchange" : true,
16301     
16302         /**
16303          * @event beforeselect
16304          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16305          * @param {Roo.View} this
16306          * @param {HTMLElement} node The node to be selected
16307          * @param {Array} selections Array of currently selected nodes
16308          */
16309             "beforeselect" : true,
16310         /**
16311          * @event preparedata
16312          * Fires on every row to render, to allow you to change the data.
16313          * @param {Roo.View} this
16314          * @param {Object} data to be rendered (change this)
16315          */
16316           "preparedata" : true
16317           
16318           
16319         });
16320
16321
16322
16323     this.el.on({
16324         "click": this.onClick,
16325         "dblclick": this.onDblClick,
16326         "contextmenu": this.onContextMenu,
16327         scope:this
16328     });
16329
16330     this.selections = [];
16331     this.nodes = [];
16332     this.cmp = new Roo.CompositeElementLite([]);
16333     if(this.store){
16334         this.store = Roo.factory(this.store, Roo.data);
16335         this.setStore(this.store, true);
16336     }
16337     
16338     if ( this.footer && this.footer.xtype) {
16339            
16340          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16341         
16342         this.footer.dataSource = this.store;
16343         this.footer.container = fctr;
16344         this.footer = Roo.factory(this.footer, Roo);
16345         fctr.insertFirst(this.el);
16346         
16347         // this is a bit insane - as the paging toolbar seems to detach the el..
16348 //        dom.parentNode.parentNode.parentNode
16349          // they get detached?
16350     }
16351     
16352     
16353     Roo.View.superclass.constructor.call(this);
16354     
16355     
16356 };
16357
16358 Roo.extend(Roo.View, Roo.util.Observable, {
16359     
16360      /**
16361      * @cfg {Roo.data.Store} store Data store to load data from.
16362      */
16363     store : false,
16364     
16365     /**
16366      * @cfg {String|Roo.Element} el The container element.
16367      */
16368     el : '',
16369     
16370     /**
16371      * @cfg {String|Roo.Template} tpl The template used by this View 
16372      */
16373     tpl : false,
16374     /**
16375      * @cfg {String} dataName the named area of the template to use as the data area
16376      *                          Works with domtemplates roo-name="name"
16377      */
16378     dataName: false,
16379     /**
16380      * @cfg {String} selectedClass The css class to add to selected nodes
16381      */
16382     selectedClass : "x-view-selected",
16383      /**
16384      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16385      */
16386     emptyText : "",
16387     
16388     /**
16389      * @cfg {String} text to display on mask (default Loading)
16390      */
16391     mask : false,
16392     /**
16393      * @cfg {Boolean} multiSelect Allow multiple selection
16394      */
16395     multiSelect : false,
16396     /**
16397      * @cfg {Boolean} singleSelect Allow single selection
16398      */
16399     singleSelect:  false,
16400     
16401     /**
16402      * @cfg {Boolean} toggleSelect - selecting 
16403      */
16404     toggleSelect : false,
16405     
16406     /**
16407      * @cfg {Boolean} tickable - selecting 
16408      */
16409     tickable : false,
16410     
16411     /**
16412      * Returns the element this view is bound to.
16413      * @return {Roo.Element}
16414      */
16415     getEl : function(){
16416         return this.wrapEl;
16417     },
16418     
16419     
16420
16421     /**
16422      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16423      */
16424     refresh : function(){
16425         //Roo.log('refresh');
16426         var t = this.tpl;
16427         
16428         // if we are using something like 'domtemplate', then
16429         // the what gets used is:
16430         // t.applySubtemplate(NAME, data, wrapping data..)
16431         // the outer template then get' applied with
16432         //     the store 'extra data'
16433         // and the body get's added to the
16434         //      roo-name="data" node?
16435         //      <span class='roo-tpl-{name}'></span> ?????
16436         
16437         
16438         
16439         this.clearSelections();
16440         this.el.update("");
16441         var html = [];
16442         var records = this.store.getRange();
16443         if(records.length < 1) {
16444             
16445             // is this valid??  = should it render a template??
16446             
16447             this.el.update(this.emptyText);
16448             return;
16449         }
16450         var el = this.el;
16451         if (this.dataName) {
16452             this.el.update(t.apply(this.store.meta)); //????
16453             el = this.el.child('.roo-tpl-' + this.dataName);
16454         }
16455         
16456         for(var i = 0, len = records.length; i < len; i++){
16457             var data = this.prepareData(records[i].data, i, records[i]);
16458             this.fireEvent("preparedata", this, data, i, records[i]);
16459             
16460             var d = Roo.apply({}, data);
16461             
16462             if(this.tickable){
16463                 Roo.apply(d, {'roo-id' : Roo.id()});
16464                 
16465                 var _this = this;
16466             
16467                 Roo.each(this.parent.item, function(item){
16468                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16469                         return;
16470                     }
16471                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16472                 });
16473             }
16474             
16475             html[html.length] = Roo.util.Format.trim(
16476                 this.dataName ?
16477                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16478                     t.apply(d)
16479             );
16480         }
16481         
16482         
16483         
16484         el.update(html.join(""));
16485         this.nodes = el.dom.childNodes;
16486         this.updateIndexes(0);
16487     },
16488     
16489
16490     /**
16491      * Function to override to reformat the data that is sent to
16492      * the template for each node.
16493      * DEPRICATED - use the preparedata event handler.
16494      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16495      * a JSON object for an UpdateManager bound view).
16496      */
16497     prepareData : function(data, index, record)
16498     {
16499         this.fireEvent("preparedata", this, data, index, record);
16500         return data;
16501     },
16502
16503     onUpdate : function(ds, record){
16504         // Roo.log('on update');   
16505         this.clearSelections();
16506         var index = this.store.indexOf(record);
16507         var n = this.nodes[index];
16508         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16509         n.parentNode.removeChild(n);
16510         this.updateIndexes(index, index);
16511     },
16512
16513     
16514     
16515 // --------- FIXME     
16516     onAdd : function(ds, records, index)
16517     {
16518         //Roo.log(['on Add', ds, records, index] );        
16519         this.clearSelections();
16520         if(this.nodes.length == 0){
16521             this.refresh();
16522             return;
16523         }
16524         var n = this.nodes[index];
16525         for(var i = 0, len = records.length; i < len; i++){
16526             var d = this.prepareData(records[i].data, i, records[i]);
16527             if(n){
16528                 this.tpl.insertBefore(n, d);
16529             }else{
16530                 
16531                 this.tpl.append(this.el, d);
16532             }
16533         }
16534         this.updateIndexes(index);
16535     },
16536
16537     onRemove : function(ds, record, index){
16538        // Roo.log('onRemove');
16539         this.clearSelections();
16540         var el = this.dataName  ?
16541             this.el.child('.roo-tpl-' + this.dataName) :
16542             this.el; 
16543         
16544         el.dom.removeChild(this.nodes[index]);
16545         this.updateIndexes(index);
16546     },
16547
16548     /**
16549      * Refresh an individual node.
16550      * @param {Number} index
16551      */
16552     refreshNode : function(index){
16553         this.onUpdate(this.store, this.store.getAt(index));
16554     },
16555
16556     updateIndexes : function(startIndex, endIndex){
16557         var ns = this.nodes;
16558         startIndex = startIndex || 0;
16559         endIndex = endIndex || ns.length - 1;
16560         for(var i = startIndex; i <= endIndex; i++){
16561             ns[i].nodeIndex = i;
16562         }
16563     },
16564
16565     /**
16566      * Changes the data store this view uses and refresh the view.
16567      * @param {Store} store
16568      */
16569     setStore : function(store, initial){
16570         if(!initial && this.store){
16571             this.store.un("datachanged", this.refresh);
16572             this.store.un("add", this.onAdd);
16573             this.store.un("remove", this.onRemove);
16574             this.store.un("update", this.onUpdate);
16575             this.store.un("clear", this.refresh);
16576             this.store.un("beforeload", this.onBeforeLoad);
16577             this.store.un("load", this.onLoad);
16578             this.store.un("loadexception", this.onLoad);
16579         }
16580         if(store){
16581           
16582             store.on("datachanged", this.refresh, this);
16583             store.on("add", this.onAdd, this);
16584             store.on("remove", this.onRemove, this);
16585             store.on("update", this.onUpdate, this);
16586             store.on("clear", this.refresh, this);
16587             store.on("beforeload", this.onBeforeLoad, this);
16588             store.on("load", this.onLoad, this);
16589             store.on("loadexception", this.onLoad, this);
16590         }
16591         
16592         if(store){
16593             this.refresh();
16594         }
16595     },
16596     /**
16597      * onbeforeLoad - masks the loading area.
16598      *
16599      */
16600     onBeforeLoad : function(store,opts)
16601     {
16602          //Roo.log('onBeforeLoad');   
16603         if (!opts.add) {
16604             this.el.update("");
16605         }
16606         this.el.mask(this.mask ? this.mask : "Loading" ); 
16607     },
16608     onLoad : function ()
16609     {
16610         this.el.unmask();
16611     },
16612     
16613
16614     /**
16615      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16616      * @param {HTMLElement} node
16617      * @return {HTMLElement} The template node
16618      */
16619     findItemFromChild : function(node){
16620         var el = this.dataName  ?
16621             this.el.child('.roo-tpl-' + this.dataName,true) :
16622             this.el.dom; 
16623         
16624         if(!node || node.parentNode == el){
16625                     return node;
16626             }
16627             var p = node.parentNode;
16628             while(p && p != el){
16629             if(p.parentNode == el){
16630                 return p;
16631             }
16632             p = p.parentNode;
16633         }
16634             return null;
16635     },
16636
16637     /** @ignore */
16638     onClick : function(e){
16639         var item = this.findItemFromChild(e.getTarget());
16640         if(item){
16641             var index = this.indexOf(item);
16642             if(this.onItemClick(item, index, e) !== false){
16643                 this.fireEvent("click", this, index, item, e);
16644             }
16645         }else{
16646             this.clearSelections();
16647         }
16648     },
16649
16650     /** @ignore */
16651     onContextMenu : function(e){
16652         var item = this.findItemFromChild(e.getTarget());
16653         if(item){
16654             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16655         }
16656     },
16657
16658     /** @ignore */
16659     onDblClick : function(e){
16660         var item = this.findItemFromChild(e.getTarget());
16661         if(item){
16662             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16663         }
16664     },
16665
16666     onItemClick : function(item, index, e)
16667     {
16668         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16669             return false;
16670         }
16671         if (this.toggleSelect) {
16672             var m = this.isSelected(item) ? 'unselect' : 'select';
16673             //Roo.log(m);
16674             var _t = this;
16675             _t[m](item, true, false);
16676             return true;
16677         }
16678         if(this.multiSelect || this.singleSelect){
16679             if(this.multiSelect && e.shiftKey && this.lastSelection){
16680                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16681             }else{
16682                 this.select(item, this.multiSelect && e.ctrlKey);
16683                 this.lastSelection = item;
16684             }
16685             
16686             if(!this.tickable){
16687                 e.preventDefault();
16688             }
16689             
16690         }
16691         return true;
16692     },
16693
16694     /**
16695      * Get the number of selected nodes.
16696      * @return {Number}
16697      */
16698     getSelectionCount : function(){
16699         return this.selections.length;
16700     },
16701
16702     /**
16703      * Get the currently selected nodes.
16704      * @return {Array} An array of HTMLElements
16705      */
16706     getSelectedNodes : function(){
16707         return this.selections;
16708     },
16709
16710     /**
16711      * Get the indexes of the selected nodes.
16712      * @return {Array}
16713      */
16714     getSelectedIndexes : function(){
16715         var indexes = [], s = this.selections;
16716         for(var i = 0, len = s.length; i < len; i++){
16717             indexes.push(s[i].nodeIndex);
16718         }
16719         return indexes;
16720     },
16721
16722     /**
16723      * Clear all selections
16724      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16725      */
16726     clearSelections : function(suppressEvent){
16727         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16728             this.cmp.elements = this.selections;
16729             this.cmp.removeClass(this.selectedClass);
16730             this.selections = [];
16731             if(!suppressEvent){
16732                 this.fireEvent("selectionchange", this, this.selections);
16733             }
16734         }
16735     },
16736
16737     /**
16738      * Returns true if the passed node is selected
16739      * @param {HTMLElement/Number} node The node or node index
16740      * @return {Boolean}
16741      */
16742     isSelected : function(node){
16743         var s = this.selections;
16744         if(s.length < 1){
16745             return false;
16746         }
16747         node = this.getNode(node);
16748         return s.indexOf(node) !== -1;
16749     },
16750
16751     /**
16752      * Selects nodes.
16753      * @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
16754      * @param {Boolean} keepExisting (optional) true to keep existing selections
16755      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16756      */
16757     select : function(nodeInfo, keepExisting, suppressEvent){
16758         if(nodeInfo instanceof Array){
16759             if(!keepExisting){
16760                 this.clearSelections(true);
16761             }
16762             for(var i = 0, len = nodeInfo.length; i < len; i++){
16763                 this.select(nodeInfo[i], true, true);
16764             }
16765             return;
16766         } 
16767         var node = this.getNode(nodeInfo);
16768         if(!node || this.isSelected(node)){
16769             return; // already selected.
16770         }
16771         if(!keepExisting){
16772             this.clearSelections(true);
16773         }
16774         
16775         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16776             Roo.fly(node).addClass(this.selectedClass);
16777             this.selections.push(node);
16778             if(!suppressEvent){
16779                 this.fireEvent("selectionchange", this, this.selections);
16780             }
16781         }
16782         
16783         
16784     },
16785       /**
16786      * Unselects nodes.
16787      * @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
16788      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16789      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16790      */
16791     unselect : function(nodeInfo, keepExisting, suppressEvent)
16792     {
16793         if(nodeInfo instanceof Array){
16794             Roo.each(this.selections, function(s) {
16795                 this.unselect(s, nodeInfo);
16796             }, this);
16797             return;
16798         }
16799         var node = this.getNode(nodeInfo);
16800         if(!node || !this.isSelected(node)){
16801             //Roo.log("not selected");
16802             return; // not selected.
16803         }
16804         // fireevent???
16805         var ns = [];
16806         Roo.each(this.selections, function(s) {
16807             if (s == node ) {
16808                 Roo.fly(node).removeClass(this.selectedClass);
16809
16810                 return;
16811             }
16812             ns.push(s);
16813         },this);
16814         
16815         this.selections= ns;
16816         this.fireEvent("selectionchange", this, this.selections);
16817     },
16818
16819     /**
16820      * Gets a template node.
16821      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16822      * @return {HTMLElement} The node or null if it wasn't found
16823      */
16824     getNode : function(nodeInfo){
16825         if(typeof nodeInfo == "string"){
16826             return document.getElementById(nodeInfo);
16827         }else if(typeof nodeInfo == "number"){
16828             return this.nodes[nodeInfo];
16829         }
16830         return nodeInfo;
16831     },
16832
16833     /**
16834      * Gets a range template nodes.
16835      * @param {Number} startIndex
16836      * @param {Number} endIndex
16837      * @return {Array} An array of nodes
16838      */
16839     getNodes : function(start, end){
16840         var ns = this.nodes;
16841         start = start || 0;
16842         end = typeof end == "undefined" ? ns.length - 1 : end;
16843         var nodes = [];
16844         if(start <= end){
16845             for(var i = start; i <= end; i++){
16846                 nodes.push(ns[i]);
16847             }
16848         } else{
16849             for(var i = start; i >= end; i--){
16850                 nodes.push(ns[i]);
16851             }
16852         }
16853         return nodes;
16854     },
16855
16856     /**
16857      * Finds the index of the passed node
16858      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16859      * @return {Number} The index of the node or -1
16860      */
16861     indexOf : function(node){
16862         node = this.getNode(node);
16863         if(typeof node.nodeIndex == "number"){
16864             return node.nodeIndex;
16865         }
16866         var ns = this.nodes;
16867         for(var i = 0, len = ns.length; i < len; i++){
16868             if(ns[i] == node){
16869                 return i;
16870             }
16871         }
16872         return -1;
16873     }
16874 });
16875 /*
16876  * - LGPL
16877  *
16878  * based on jquery fullcalendar
16879  * 
16880  */
16881
16882 Roo.bootstrap = Roo.bootstrap || {};
16883 /**
16884  * @class Roo.bootstrap.Calendar
16885  * @extends Roo.bootstrap.Component
16886  * Bootstrap Calendar class
16887  * @cfg {Boolean} loadMask (true|false) default false
16888  * @cfg {Object} header generate the user specific header of the calendar, default false
16889
16890  * @constructor
16891  * Create a new Container
16892  * @param {Object} config The config object
16893  */
16894
16895
16896
16897 Roo.bootstrap.Calendar = function(config){
16898     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16899      this.addEvents({
16900         /**
16901              * @event select
16902              * Fires when a date is selected
16903              * @param {DatePicker} this
16904              * @param {Date} date The selected date
16905              */
16906         'select': true,
16907         /**
16908              * @event monthchange
16909              * Fires when the displayed month changes 
16910              * @param {DatePicker} this
16911              * @param {Date} date The selected month
16912              */
16913         'monthchange': true,
16914         /**
16915              * @event evententer
16916              * Fires when mouse over an event
16917              * @param {Calendar} this
16918              * @param {event} Event
16919              */
16920         'evententer': true,
16921         /**
16922              * @event eventleave
16923              * Fires when the mouse leaves an
16924              * @param {Calendar} this
16925              * @param {event}
16926              */
16927         'eventleave': true,
16928         /**
16929              * @event eventclick
16930              * Fires when the mouse click an
16931              * @param {Calendar} this
16932              * @param {event}
16933              */
16934         'eventclick': true
16935         
16936     });
16937
16938 };
16939
16940 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16941     
16942      /**
16943      * @cfg {Number} startDay
16944      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16945      */
16946     startDay : 0,
16947     
16948     loadMask : false,
16949     
16950     header : false,
16951       
16952     getAutoCreate : function(){
16953         
16954         
16955         var fc_button = function(name, corner, style, content ) {
16956             return Roo.apply({},{
16957                 tag : 'span',
16958                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16959                          (corner.length ?
16960                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16961                             ''
16962                         ),
16963                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16964                 unselectable: 'on'
16965             });
16966         };
16967         
16968         var header = {};
16969         
16970         if(!this.header){
16971             header = {
16972                 tag : 'table',
16973                 cls : 'fc-header',
16974                 style : 'width:100%',
16975                 cn : [
16976                     {
16977                         tag: 'tr',
16978                         cn : [
16979                             {
16980                                 tag : 'td',
16981                                 cls : 'fc-header-left',
16982                                 cn : [
16983                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16984                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16985                                     { tag: 'span', cls: 'fc-header-space' },
16986                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16987
16988
16989                                 ]
16990                             },
16991
16992                             {
16993                                 tag : 'td',
16994                                 cls : 'fc-header-center',
16995                                 cn : [
16996                                     {
16997                                         tag: 'span',
16998                                         cls: 'fc-header-title',
16999                                         cn : {
17000                                             tag: 'H2',
17001                                             html : 'month / year'
17002                                         }
17003                                     }
17004
17005                                 ]
17006                             },
17007                             {
17008                                 tag : 'td',
17009                                 cls : 'fc-header-right',
17010                                 cn : [
17011                               /*      fc_button('month', 'left', '', 'month' ),
17012                                     fc_button('week', '', '', 'week' ),
17013                                     fc_button('day', 'right', '', 'day' )
17014                                 */    
17015
17016                                 ]
17017                             }
17018
17019                         ]
17020                     }
17021                 ]
17022             };
17023         }
17024         
17025         header = this.header;
17026         
17027        
17028         var cal_heads = function() {
17029             var ret = [];
17030             // fixme - handle this.
17031             
17032             for (var i =0; i < Date.dayNames.length; i++) {
17033                 var d = Date.dayNames[i];
17034                 ret.push({
17035                     tag: 'th',
17036                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
17037                     html : d.substring(0,3)
17038                 });
17039                 
17040             }
17041             ret[0].cls += ' fc-first';
17042             ret[6].cls += ' fc-last';
17043             return ret;
17044         };
17045         var cal_cell = function(n) {
17046             return  {
17047                 tag: 'td',
17048                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
17049                 cn : [
17050                     {
17051                         cn : [
17052                             {
17053                                 cls: 'fc-day-number',
17054                                 html: 'D'
17055                             },
17056                             {
17057                                 cls: 'fc-day-content',
17058                              
17059                                 cn : [
17060                                      {
17061                                         style: 'position: relative;' // height: 17px;
17062                                     }
17063                                 ]
17064                             }
17065                             
17066                             
17067                         ]
17068                     }
17069                 ]
17070                 
17071             }
17072         };
17073         var cal_rows = function() {
17074             
17075             var ret = [];
17076             for (var r = 0; r < 6; r++) {
17077                 var row= {
17078                     tag : 'tr',
17079                     cls : 'fc-week',
17080                     cn : []
17081                 };
17082                 
17083                 for (var i =0; i < Date.dayNames.length; i++) {
17084                     var d = Date.dayNames[i];
17085                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
17086
17087                 }
17088                 row.cn[0].cls+=' fc-first';
17089                 row.cn[0].cn[0].style = 'min-height:90px';
17090                 row.cn[6].cls+=' fc-last';
17091                 ret.push(row);
17092                 
17093             }
17094             ret[0].cls += ' fc-first';
17095             ret[4].cls += ' fc-prev-last';
17096             ret[5].cls += ' fc-last';
17097             return ret;
17098             
17099         };
17100         
17101         var cal_table = {
17102             tag: 'table',
17103             cls: 'fc-border-separate',
17104             style : 'width:100%',
17105             cellspacing  : 0,
17106             cn : [
17107                 { 
17108                     tag: 'thead',
17109                     cn : [
17110                         { 
17111                             tag: 'tr',
17112                             cls : 'fc-first fc-last',
17113                             cn : cal_heads()
17114                         }
17115                     ]
17116                 },
17117                 { 
17118                     tag: 'tbody',
17119                     cn : cal_rows()
17120                 }
17121                   
17122             ]
17123         };
17124          
17125          var cfg = {
17126             cls : 'fc fc-ltr',
17127             cn : [
17128                 header,
17129                 {
17130                     cls : 'fc-content',
17131                     style : "position: relative;",
17132                     cn : [
17133                         {
17134                             cls : 'fc-view fc-view-month fc-grid',
17135                             style : 'position: relative',
17136                             unselectable : 'on',
17137                             cn : [
17138                                 {
17139                                     cls : 'fc-event-container',
17140                                     style : 'position:absolute;z-index:8;top:0;left:0;'
17141                                 },
17142                                 cal_table
17143                             ]
17144                         }
17145                     ]
17146     
17147                 }
17148            ] 
17149             
17150         };
17151         
17152          
17153         
17154         return cfg;
17155     },
17156     
17157     
17158     initEvents : function()
17159     {
17160         if(!this.store){
17161             throw "can not find store for calendar";
17162         }
17163         
17164         var mark = {
17165             tag: "div",
17166             cls:"x-dlg-mask",
17167             style: "text-align:center",
17168             cn: [
17169                 {
17170                     tag: "div",
17171                     style: "background-color:white;width:50%;margin:250 auto",
17172                     cn: [
17173                         {
17174                             tag: "img",
17175                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
17176                         },
17177                         {
17178                             tag: "span",
17179                             html: "Loading"
17180                         }
17181                         
17182                     ]
17183                 }
17184             ]
17185         };
17186         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17187         
17188         var size = this.el.select('.fc-content', true).first().getSize();
17189         this.maskEl.setSize(size.width, size.height);
17190         this.maskEl.enableDisplayMode("block");
17191         if(!this.loadMask){
17192             this.maskEl.hide();
17193         }
17194         
17195         this.store = Roo.factory(this.store, Roo.data);
17196         this.store.on('load', this.onLoad, this);
17197         this.store.on('beforeload', this.onBeforeLoad, this);
17198         
17199         this.resize();
17200         
17201         this.cells = this.el.select('.fc-day',true);
17202         //Roo.log(this.cells);
17203         this.textNodes = this.el.query('.fc-day-number');
17204         this.cells.addClassOnOver('fc-state-hover');
17205         
17206         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17207         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17208         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17209         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17210         
17211         this.on('monthchange', this.onMonthChange, this);
17212         
17213         this.update(new Date().clearTime());
17214     },
17215     
17216     resize : function() {
17217         var sz  = this.el.getSize();
17218         
17219         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17220         this.el.select('.fc-day-content div',true).setHeight(34);
17221     },
17222     
17223     
17224     // private
17225     showPrevMonth : function(e){
17226         this.update(this.activeDate.add("mo", -1));
17227     },
17228     showToday : function(e){
17229         this.update(new Date().clearTime());
17230     },
17231     // private
17232     showNextMonth : function(e){
17233         this.update(this.activeDate.add("mo", 1));
17234     },
17235
17236     // private
17237     showPrevYear : function(){
17238         this.update(this.activeDate.add("y", -1));
17239     },
17240
17241     // private
17242     showNextYear : function(){
17243         this.update(this.activeDate.add("y", 1));
17244     },
17245
17246     
17247    // private
17248     update : function(date)
17249     {
17250         var vd = this.activeDate;
17251         this.activeDate = date;
17252 //        if(vd && this.el){
17253 //            var t = date.getTime();
17254 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17255 //                Roo.log('using add remove');
17256 //                
17257 //                this.fireEvent('monthchange', this, date);
17258 //                
17259 //                this.cells.removeClass("fc-state-highlight");
17260 //                this.cells.each(function(c){
17261 //                   if(c.dateValue == t){
17262 //                       c.addClass("fc-state-highlight");
17263 //                       setTimeout(function(){
17264 //                            try{c.dom.firstChild.focus();}catch(e){}
17265 //                       }, 50);
17266 //                       return false;
17267 //                   }
17268 //                   return true;
17269 //                });
17270 //                return;
17271 //            }
17272 //        }
17273         
17274         var days = date.getDaysInMonth();
17275         
17276         var firstOfMonth = date.getFirstDateOfMonth();
17277         var startingPos = firstOfMonth.getDay()-this.startDay;
17278         
17279         if(startingPos < this.startDay){
17280             startingPos += 7;
17281         }
17282         
17283         var pm = date.add(Date.MONTH, -1);
17284         var prevStart = pm.getDaysInMonth()-startingPos;
17285 //        
17286         this.cells = this.el.select('.fc-day',true);
17287         this.textNodes = this.el.query('.fc-day-number');
17288         this.cells.addClassOnOver('fc-state-hover');
17289         
17290         var cells = this.cells.elements;
17291         var textEls = this.textNodes;
17292         
17293         Roo.each(cells, function(cell){
17294             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17295         });
17296         
17297         days += startingPos;
17298
17299         // convert everything to numbers so it's fast
17300         var day = 86400000;
17301         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17302         //Roo.log(d);
17303         //Roo.log(pm);
17304         //Roo.log(prevStart);
17305         
17306         var today = new Date().clearTime().getTime();
17307         var sel = date.clearTime().getTime();
17308         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17309         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17310         var ddMatch = this.disabledDatesRE;
17311         var ddText = this.disabledDatesText;
17312         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17313         var ddaysText = this.disabledDaysText;
17314         var format = this.format;
17315         
17316         var setCellClass = function(cal, cell){
17317             cell.row = 0;
17318             cell.events = [];
17319             cell.more = [];
17320             //Roo.log('set Cell Class');
17321             cell.title = "";
17322             var t = d.getTime();
17323             
17324             //Roo.log(d);
17325             
17326             cell.dateValue = t;
17327             if(t == today){
17328                 cell.className += " fc-today";
17329                 cell.className += " fc-state-highlight";
17330                 cell.title = cal.todayText;
17331             }
17332             if(t == sel){
17333                 // disable highlight in other month..
17334                 //cell.className += " fc-state-highlight";
17335                 
17336             }
17337             // disabling
17338             if(t < min) {
17339                 cell.className = " fc-state-disabled";
17340                 cell.title = cal.minText;
17341                 return;
17342             }
17343             if(t > max) {
17344                 cell.className = " fc-state-disabled";
17345                 cell.title = cal.maxText;
17346                 return;
17347             }
17348             if(ddays){
17349                 if(ddays.indexOf(d.getDay()) != -1){
17350                     cell.title = ddaysText;
17351                     cell.className = " fc-state-disabled";
17352                 }
17353             }
17354             if(ddMatch && format){
17355                 var fvalue = d.dateFormat(format);
17356                 if(ddMatch.test(fvalue)){
17357                     cell.title = ddText.replace("%0", fvalue);
17358                     cell.className = " fc-state-disabled";
17359                 }
17360             }
17361             
17362             if (!cell.initialClassName) {
17363                 cell.initialClassName = cell.dom.className;
17364             }
17365             
17366             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17367         };
17368
17369         var i = 0;
17370         
17371         for(; i < startingPos; i++) {
17372             textEls[i].innerHTML = (++prevStart);
17373             d.setDate(d.getDate()+1);
17374             
17375             cells[i].className = "fc-past fc-other-month";
17376             setCellClass(this, cells[i]);
17377         }
17378         
17379         var intDay = 0;
17380         
17381         for(; i < days; i++){
17382             intDay = i - startingPos + 1;
17383             textEls[i].innerHTML = (intDay);
17384             d.setDate(d.getDate()+1);
17385             
17386             cells[i].className = ''; // "x-date-active";
17387             setCellClass(this, cells[i]);
17388         }
17389         var extraDays = 0;
17390         
17391         for(; i < 42; i++) {
17392             textEls[i].innerHTML = (++extraDays);
17393             d.setDate(d.getDate()+1);
17394             
17395             cells[i].className = "fc-future fc-other-month";
17396             setCellClass(this, cells[i]);
17397         }
17398         
17399         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17400         
17401         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17402         
17403         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17404         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17405         
17406         if(totalRows != 6){
17407             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17408             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17409         }
17410         
17411         this.fireEvent('monthchange', this, date);
17412         
17413         
17414         /*
17415         if(!this.internalRender){
17416             var main = this.el.dom.firstChild;
17417             var w = main.offsetWidth;
17418             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17419             Roo.fly(main).setWidth(w);
17420             this.internalRender = true;
17421             // opera does not respect the auto grow header center column
17422             // then, after it gets a width opera refuses to recalculate
17423             // without a second pass
17424             if(Roo.isOpera && !this.secondPass){
17425                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17426                 this.secondPass = true;
17427                 this.update.defer(10, this, [date]);
17428             }
17429         }
17430         */
17431         
17432     },
17433     
17434     findCell : function(dt) {
17435         dt = dt.clearTime().getTime();
17436         var ret = false;
17437         this.cells.each(function(c){
17438             //Roo.log("check " +c.dateValue + '?=' + dt);
17439             if(c.dateValue == dt){
17440                 ret = c;
17441                 return false;
17442             }
17443             return true;
17444         });
17445         
17446         return ret;
17447     },
17448     
17449     findCells : function(ev) {
17450         var s = ev.start.clone().clearTime().getTime();
17451        // Roo.log(s);
17452         var e= ev.end.clone().clearTime().getTime();
17453        // Roo.log(e);
17454         var ret = [];
17455         this.cells.each(function(c){
17456              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17457             
17458             if(c.dateValue > e){
17459                 return ;
17460             }
17461             if(c.dateValue < s){
17462                 return ;
17463             }
17464             ret.push(c);
17465         });
17466         
17467         return ret;    
17468     },
17469     
17470 //    findBestRow: function(cells)
17471 //    {
17472 //        var ret = 0;
17473 //        
17474 //        for (var i =0 ; i < cells.length;i++) {
17475 //            ret  = Math.max(cells[i].rows || 0,ret);
17476 //        }
17477 //        return ret;
17478 //        
17479 //    },
17480     
17481     
17482     addItem : function(ev)
17483     {
17484         // look for vertical location slot in
17485         var cells = this.findCells(ev);
17486         
17487 //        ev.row = this.findBestRow(cells);
17488         
17489         // work out the location.
17490         
17491         var crow = false;
17492         var rows = [];
17493         for(var i =0; i < cells.length; i++) {
17494             
17495             cells[i].row = cells[0].row;
17496             
17497             if(i == 0){
17498                 cells[i].row = cells[i].row + 1;
17499             }
17500             
17501             if (!crow) {
17502                 crow = {
17503                     start : cells[i],
17504                     end :  cells[i]
17505                 };
17506                 continue;
17507             }
17508             if (crow.start.getY() == cells[i].getY()) {
17509                 // on same row.
17510                 crow.end = cells[i];
17511                 continue;
17512             }
17513             // different row.
17514             rows.push(crow);
17515             crow = {
17516                 start: cells[i],
17517                 end : cells[i]
17518             };
17519             
17520         }
17521         
17522         rows.push(crow);
17523         ev.els = [];
17524         ev.rows = rows;
17525         ev.cells = cells;
17526         
17527         cells[0].events.push(ev);
17528         
17529         this.calevents.push(ev);
17530     },
17531     
17532     clearEvents: function() {
17533         
17534         if(!this.calevents){
17535             return;
17536         }
17537         
17538         Roo.each(this.cells.elements, function(c){
17539             c.row = 0;
17540             c.events = [];
17541             c.more = [];
17542         });
17543         
17544         Roo.each(this.calevents, function(e) {
17545             Roo.each(e.els, function(el) {
17546                 el.un('mouseenter' ,this.onEventEnter, this);
17547                 el.un('mouseleave' ,this.onEventLeave, this);
17548                 el.remove();
17549             },this);
17550         },this);
17551         
17552         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17553             e.remove();
17554         });
17555         
17556     },
17557     
17558     renderEvents: function()
17559     {   
17560         var _this = this;
17561         
17562         this.cells.each(function(c) {
17563             
17564             if(c.row < 5){
17565                 return;
17566             }
17567             
17568             var ev = c.events;
17569             
17570             var r = 4;
17571             if(c.row != c.events.length){
17572                 r = 4 - (4 - (c.row - c.events.length));
17573             }
17574             
17575             c.events = ev.slice(0, r);
17576             c.more = ev.slice(r);
17577             
17578             if(c.more.length && c.more.length == 1){
17579                 c.events.push(c.more.pop());
17580             }
17581             
17582             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17583             
17584         });
17585             
17586         this.cells.each(function(c) {
17587             
17588             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17589             
17590             
17591             for (var e = 0; e < c.events.length; e++){
17592                 var ev = c.events[e];
17593                 var rows = ev.rows;
17594                 
17595                 for(var i = 0; i < rows.length; i++) {
17596                 
17597                     // how many rows should it span..
17598
17599                     var  cfg = {
17600                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17601                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17602
17603                         unselectable : "on",
17604                         cn : [
17605                             {
17606                                 cls: 'fc-event-inner',
17607                                 cn : [
17608     //                                {
17609     //                                  tag:'span',
17610     //                                  cls: 'fc-event-time',
17611     //                                  html : cells.length > 1 ? '' : ev.time
17612     //                                },
17613                                     {
17614                                       tag:'span',
17615                                       cls: 'fc-event-title',
17616                                       html : String.format('{0}', ev.title)
17617                                     }
17618
17619
17620                                 ]
17621                             },
17622                             {
17623                                 cls: 'ui-resizable-handle ui-resizable-e',
17624                                 html : '&nbsp;&nbsp;&nbsp'
17625                             }
17626
17627                         ]
17628                     };
17629
17630                     if (i == 0) {
17631                         cfg.cls += ' fc-event-start';
17632                     }
17633                     if ((i+1) == rows.length) {
17634                         cfg.cls += ' fc-event-end';
17635                     }
17636
17637                     var ctr = _this.el.select('.fc-event-container',true).first();
17638                     var cg = ctr.createChild(cfg);
17639
17640                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17641                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17642
17643                     var r = (c.more.length) ? 1 : 0;
17644                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17645                     cg.setWidth(ebox.right - sbox.x -2);
17646
17647                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17648                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17649                     cg.on('click', _this.onEventClick, _this, ev);
17650
17651                     ev.els.push(cg);
17652                     
17653                 }
17654                 
17655             }
17656             
17657             
17658             if(c.more.length){
17659                 var  cfg = {
17660                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17661                     style : 'position: absolute',
17662                     unselectable : "on",
17663                     cn : [
17664                         {
17665                             cls: 'fc-event-inner',
17666                             cn : [
17667                                 {
17668                                   tag:'span',
17669                                   cls: 'fc-event-title',
17670                                   html : 'More'
17671                                 }
17672
17673
17674                             ]
17675                         },
17676                         {
17677                             cls: 'ui-resizable-handle ui-resizable-e',
17678                             html : '&nbsp;&nbsp;&nbsp'
17679                         }
17680
17681                     ]
17682                 };
17683
17684                 var ctr = _this.el.select('.fc-event-container',true).first();
17685                 var cg = ctr.createChild(cfg);
17686
17687                 var sbox = c.select('.fc-day-content',true).first().getBox();
17688                 var ebox = c.select('.fc-day-content',true).first().getBox();
17689                 //Roo.log(cg);
17690                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17691                 cg.setWidth(ebox.right - sbox.x -2);
17692
17693                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17694                 
17695             }
17696             
17697         });
17698         
17699         
17700         
17701     },
17702     
17703     onEventEnter: function (e, el,event,d) {
17704         this.fireEvent('evententer', this, el, event);
17705     },
17706     
17707     onEventLeave: function (e, el,event,d) {
17708         this.fireEvent('eventleave', this, el, event);
17709     },
17710     
17711     onEventClick: function (e, el,event,d) {
17712         this.fireEvent('eventclick', this, el, event);
17713     },
17714     
17715     onMonthChange: function () {
17716         this.store.load();
17717     },
17718     
17719     onMoreEventClick: function(e, el, more)
17720     {
17721         var _this = this;
17722         
17723         this.calpopover.placement = 'right';
17724         this.calpopover.setTitle('More');
17725         
17726         this.calpopover.setContent('');
17727         
17728         var ctr = this.calpopover.el.select('.popover-content', true).first();
17729         
17730         Roo.each(more, function(m){
17731             var cfg = {
17732                 cls : 'fc-event-hori fc-event-draggable',
17733                 html : m.title
17734             };
17735             var cg = ctr.createChild(cfg);
17736             
17737             cg.on('click', _this.onEventClick, _this, m);
17738         });
17739         
17740         this.calpopover.show(el);
17741         
17742         
17743     },
17744     
17745     onLoad: function () 
17746     {   
17747         this.calevents = [];
17748         var cal = this;
17749         
17750         if(this.store.getCount() > 0){
17751             this.store.data.each(function(d){
17752                cal.addItem({
17753                     id : d.data.id,
17754                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17755                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17756                     time : d.data.start_time,
17757                     title : d.data.title,
17758                     description : d.data.description,
17759                     venue : d.data.venue
17760                 });
17761             });
17762         }
17763         
17764         this.renderEvents();
17765         
17766         if(this.calevents.length && this.loadMask){
17767             this.maskEl.hide();
17768         }
17769     },
17770     
17771     onBeforeLoad: function()
17772     {
17773         this.clearEvents();
17774         if(this.loadMask){
17775             this.maskEl.show();
17776         }
17777     }
17778 });
17779
17780  
17781  /*
17782  * - LGPL
17783  *
17784  * element
17785  * 
17786  */
17787
17788 /**
17789  * @class Roo.bootstrap.Popover
17790  * @extends Roo.bootstrap.Component
17791  * Bootstrap Popover class
17792  * @cfg {String} html contents of the popover   (or false to use children..)
17793  * @cfg {String} title of popover (or false to hide)
17794  * @cfg {String} placement how it is placed
17795  * @cfg {String} trigger click || hover (or false to trigger manually)
17796  * @cfg {String} over what (parent or false to trigger manually.)
17797  * @cfg {Number} delay - delay before showing
17798  
17799  * @constructor
17800  * Create a new Popover
17801  * @param {Object} config The config object
17802  */
17803
17804 Roo.bootstrap.Popover = function(config){
17805     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17806     
17807     this.addEvents({
17808         // raw events
17809          /**
17810          * @event show
17811          * After the popover show
17812          * 
17813          * @param {Roo.bootstrap.Popover} this
17814          */
17815         "show" : true,
17816         /**
17817          * @event hide
17818          * After the popover hide
17819          * 
17820          * @param {Roo.bootstrap.Popover} this
17821          */
17822         "hide" : true
17823     });
17824 };
17825
17826 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17827     
17828     title: 'Fill in a title',
17829     html: false,
17830     
17831     placement : 'right',
17832     trigger : 'hover', // hover
17833     
17834     delay : 0,
17835     
17836     over: 'parent',
17837     
17838     can_build_overlaid : false,
17839     
17840     getChildContainer : function()
17841     {
17842         return this.el.select('.popover-content',true).first();
17843     },
17844     
17845     getAutoCreate : function(){
17846          
17847         var cfg = {
17848            cls : 'popover roo-dynamic',
17849            style: 'display:block',
17850            cn : [
17851                 {
17852                     cls : 'arrow'
17853                 },
17854                 {
17855                     cls : 'popover-inner',
17856                     cn : [
17857                         {
17858                             tag: 'h3',
17859                             cls: 'popover-title popover-header',
17860                             html : this.title
17861                         },
17862                         {
17863                             cls : 'popover-content popover-body',
17864                             html : this.html
17865                         }
17866                     ]
17867                     
17868                 }
17869            ]
17870         };
17871         
17872         return cfg;
17873     },
17874     setTitle: function(str)
17875     {
17876         this.title = str;
17877         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17878     },
17879     setContent: function(str)
17880     {
17881         this.html = str;
17882         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17883     },
17884     // as it get's added to the bottom of the page.
17885     onRender : function(ct, position)
17886     {
17887         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17888         if(!this.el){
17889             var cfg = Roo.apply({},  this.getAutoCreate());
17890             cfg.id = Roo.id();
17891             
17892             if (this.cls) {
17893                 cfg.cls += ' ' + this.cls;
17894             }
17895             if (this.style) {
17896                 cfg.style = this.style;
17897             }
17898             //Roo.log("adding to ");
17899             this.el = Roo.get(document.body).createChild(cfg, position);
17900 //            Roo.log(this.el);
17901         }
17902         this.initEvents();
17903     },
17904     
17905     initEvents : function()
17906     {
17907         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17908         this.el.enableDisplayMode('block');
17909         this.el.hide();
17910         if (this.over === false) {
17911             return; 
17912         }
17913         if (this.triggers === false) {
17914             return;
17915         }
17916         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17917         var triggers = this.trigger ? this.trigger.split(' ') : [];
17918         Roo.each(triggers, function(trigger) {
17919         
17920             if (trigger == 'click') {
17921                 on_el.on('click', this.toggle, this);
17922             } else if (trigger != 'manual') {
17923                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17924                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17925       
17926                 on_el.on(eventIn  ,this.enter, this);
17927                 on_el.on(eventOut, this.leave, this);
17928             }
17929         }, this);
17930         
17931     },
17932     
17933     
17934     // private
17935     timeout : null,
17936     hoverState : null,
17937     
17938     toggle : function () {
17939         this.hoverState == 'in' ? this.leave() : this.enter();
17940     },
17941     
17942     enter : function () {
17943         
17944         clearTimeout(this.timeout);
17945     
17946         this.hoverState = 'in';
17947     
17948         if (!this.delay || !this.delay.show) {
17949             this.show();
17950             return;
17951         }
17952         var _t = this;
17953         this.timeout = setTimeout(function () {
17954             if (_t.hoverState == 'in') {
17955                 _t.show();
17956             }
17957         }, this.delay.show)
17958     },
17959     
17960     leave : function() {
17961         clearTimeout(this.timeout);
17962     
17963         this.hoverState = 'out';
17964     
17965         if (!this.delay || !this.delay.hide) {
17966             this.hide();
17967             return;
17968         }
17969         var _t = this;
17970         this.timeout = setTimeout(function () {
17971             if (_t.hoverState == 'out') {
17972                 _t.hide();
17973             }
17974         }, this.delay.hide)
17975     },
17976     
17977     show : function (on_el)
17978     {
17979         if (!on_el) {
17980             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17981         }
17982         
17983         // set content.
17984         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17985         if (this.html !== false) {
17986             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17987         }
17988         this.el.removeClass([
17989             'fade','top','bottom', 'left', 'right','in',
17990             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
17991         ]);
17992         if (!this.title.length) {
17993             this.el.select('.popover-title',true).hide();
17994         }
17995         
17996         var placement = typeof this.placement == 'function' ?
17997             this.placement.call(this, this.el, on_el) :
17998             this.placement;
17999             
18000         var autoToken = /\s?auto?\s?/i;
18001         var autoPlace = autoToken.test(placement);
18002         if (autoPlace) {
18003             placement = placement.replace(autoToken, '') || 'top';
18004         }
18005         
18006         //this.el.detach()
18007         //this.el.setXY([0,0]);
18008         this.el.show();
18009         this.el.dom.style.display='block';
18010         this.el.addClass(placement);
18011         
18012         //this.el.appendTo(on_el);
18013         
18014         var p = this.getPosition();
18015         var box = this.el.getBox();
18016         
18017         if (autoPlace) {
18018             // fixme..
18019         }
18020         var align = Roo.bootstrap.Popover.alignment[placement];
18021         
18022 //        Roo.log(align);
18023         this.el.alignTo(on_el, align[0],align[1]);
18024         //var arrow = this.el.select('.arrow',true).first();
18025         //arrow.set(align[2], 
18026         
18027         this.el.addClass('in');
18028         
18029         
18030         if (this.el.hasClass('fade')) {
18031             // fade it?
18032         }
18033         
18034         this.hoverState = 'in';
18035         
18036         this.fireEvent('show', this);
18037         
18038     },
18039     hide : function()
18040     {
18041         this.el.setXY([0,0]);
18042         this.el.removeClass('in');
18043         this.el.hide();
18044         this.hoverState = null;
18045         
18046         this.fireEvent('hide', this);
18047     }
18048     
18049 });
18050
18051 Roo.bootstrap.Popover.alignment = {
18052     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
18053     'right' : ['l-r', [10,0], 'left bs-popover-left'],
18054     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
18055     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
18056 };
18057
18058  /*
18059  * - LGPL
18060  *
18061  * Progress
18062  * 
18063  */
18064
18065 /**
18066  * @class Roo.bootstrap.Progress
18067  * @extends Roo.bootstrap.Component
18068  * Bootstrap Progress class
18069  * @cfg {Boolean} striped striped of the progress bar
18070  * @cfg {Boolean} active animated of the progress bar
18071  * 
18072  * 
18073  * @constructor
18074  * Create a new Progress
18075  * @param {Object} config The config object
18076  */
18077
18078 Roo.bootstrap.Progress = function(config){
18079     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
18080 };
18081
18082 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
18083     
18084     striped : false,
18085     active: false,
18086     
18087     getAutoCreate : function(){
18088         var cfg = {
18089             tag: 'div',
18090             cls: 'progress'
18091         };
18092         
18093         
18094         if(this.striped){
18095             cfg.cls += ' progress-striped';
18096         }
18097       
18098         if(this.active){
18099             cfg.cls += ' active';
18100         }
18101         
18102         
18103         return cfg;
18104     }
18105    
18106 });
18107
18108  
18109
18110  /*
18111  * - LGPL
18112  *
18113  * ProgressBar
18114  * 
18115  */
18116
18117 /**
18118  * @class Roo.bootstrap.ProgressBar
18119  * @extends Roo.bootstrap.Component
18120  * Bootstrap ProgressBar class
18121  * @cfg {Number} aria_valuenow aria-value now
18122  * @cfg {Number} aria_valuemin aria-value min
18123  * @cfg {Number} aria_valuemax aria-value max
18124  * @cfg {String} label label for the progress bar
18125  * @cfg {String} panel (success | info | warning | danger )
18126  * @cfg {String} role role of the progress bar
18127  * @cfg {String} sr_only text
18128  * 
18129  * 
18130  * @constructor
18131  * Create a new ProgressBar
18132  * @param {Object} config The config object
18133  */
18134
18135 Roo.bootstrap.ProgressBar = function(config){
18136     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
18137 };
18138
18139 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
18140     
18141     aria_valuenow : 0,
18142     aria_valuemin : 0,
18143     aria_valuemax : 100,
18144     label : false,
18145     panel : false,
18146     role : false,
18147     sr_only: false,
18148     
18149     getAutoCreate : function()
18150     {
18151         
18152         var cfg = {
18153             tag: 'div',
18154             cls: 'progress-bar',
18155             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18156         };
18157         
18158         if(this.sr_only){
18159             cfg.cn = {
18160                 tag: 'span',
18161                 cls: 'sr-only',
18162                 html: this.sr_only
18163             }
18164         }
18165         
18166         if(this.role){
18167             cfg.role = this.role;
18168         }
18169         
18170         if(this.aria_valuenow){
18171             cfg['aria-valuenow'] = this.aria_valuenow;
18172         }
18173         
18174         if(this.aria_valuemin){
18175             cfg['aria-valuemin'] = this.aria_valuemin;
18176         }
18177         
18178         if(this.aria_valuemax){
18179             cfg['aria-valuemax'] = this.aria_valuemax;
18180         }
18181         
18182         if(this.label && !this.sr_only){
18183             cfg.html = this.label;
18184         }
18185         
18186         if(this.panel){
18187             cfg.cls += ' progress-bar-' + this.panel;
18188         }
18189         
18190         return cfg;
18191     },
18192     
18193     update : function(aria_valuenow)
18194     {
18195         this.aria_valuenow = aria_valuenow;
18196         
18197         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18198     }
18199    
18200 });
18201
18202  
18203
18204  /*
18205  * - LGPL
18206  *
18207  * column
18208  * 
18209  */
18210
18211 /**
18212  * @class Roo.bootstrap.TabGroup
18213  * @extends Roo.bootstrap.Column
18214  * Bootstrap Column class
18215  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18216  * @cfg {Boolean} carousel true to make the group behave like a carousel
18217  * @cfg {Boolean} bullets show bullets for the panels
18218  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18219  * @cfg {Number} timer auto slide timer .. default 0 millisecond
18220  * @cfg {Boolean} showarrow (true|false) show arrow default true
18221  * 
18222  * @constructor
18223  * Create a new TabGroup
18224  * @param {Object} config The config object
18225  */
18226
18227 Roo.bootstrap.TabGroup = function(config){
18228     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18229     if (!this.navId) {
18230         this.navId = Roo.id();
18231     }
18232     this.tabs = [];
18233     Roo.bootstrap.TabGroup.register(this);
18234     
18235 };
18236
18237 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
18238     
18239     carousel : false,
18240     transition : false,
18241     bullets : 0,
18242     timer : 0,
18243     autoslide : false,
18244     slideFn : false,
18245     slideOnTouch : false,
18246     showarrow : true,
18247     
18248     getAutoCreate : function()
18249     {
18250         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18251         
18252         cfg.cls += ' tab-content';
18253         
18254         if (this.carousel) {
18255             cfg.cls += ' carousel slide';
18256             
18257             cfg.cn = [{
18258                cls : 'carousel-inner',
18259                cn : []
18260             }];
18261         
18262             if(this.bullets  && !Roo.isTouch){
18263                 
18264                 var bullets = {
18265                     cls : 'carousel-bullets',
18266                     cn : []
18267                 };
18268                
18269                 if(this.bullets_cls){
18270                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18271                 }
18272                 
18273                 bullets.cn.push({
18274                     cls : 'clear'
18275                 });
18276                 
18277                 cfg.cn[0].cn.push(bullets);
18278             }
18279             
18280             if(this.showarrow){
18281                 cfg.cn[0].cn.push({
18282                     tag : 'div',
18283                     class : 'carousel-arrow',
18284                     cn : [
18285                         {
18286                             tag : 'div',
18287                             class : 'carousel-prev',
18288                             cn : [
18289                                 {
18290                                     tag : 'i',
18291                                     class : 'fa fa-chevron-left'
18292                                 }
18293                             ]
18294                         },
18295                         {
18296                             tag : 'div',
18297                             class : 'carousel-next',
18298                             cn : [
18299                                 {
18300                                     tag : 'i',
18301                                     class : 'fa fa-chevron-right'
18302                                 }
18303                             ]
18304                         }
18305                     ]
18306                 });
18307             }
18308             
18309         }
18310         
18311         return cfg;
18312     },
18313     
18314     initEvents:  function()
18315     {
18316 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18317 //            this.el.on("touchstart", this.onTouchStart, this);
18318 //        }
18319         
18320         if(this.autoslide){
18321             var _this = this;
18322             
18323             this.slideFn = window.setInterval(function() {
18324                 _this.showPanelNext();
18325             }, this.timer);
18326         }
18327         
18328         if(this.showarrow){
18329             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18330             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18331         }
18332         
18333         
18334     },
18335     
18336 //    onTouchStart : function(e, el, o)
18337 //    {
18338 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18339 //            return;
18340 //        }
18341 //        
18342 //        this.showPanelNext();
18343 //    },
18344     
18345     
18346     getChildContainer : function()
18347     {
18348         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18349     },
18350     
18351     /**
18352     * register a Navigation item
18353     * @param {Roo.bootstrap.NavItem} the navitem to add
18354     */
18355     register : function(item)
18356     {
18357         this.tabs.push( item);
18358         item.navId = this.navId; // not really needed..
18359         this.addBullet();
18360     
18361     },
18362     
18363     getActivePanel : function()
18364     {
18365         var r = false;
18366         Roo.each(this.tabs, function(t) {
18367             if (t.active) {
18368                 r = t;
18369                 return false;
18370             }
18371             return null;
18372         });
18373         return r;
18374         
18375     },
18376     getPanelByName : function(n)
18377     {
18378         var r = false;
18379         Roo.each(this.tabs, function(t) {
18380             if (t.tabId == n) {
18381                 r = t;
18382                 return false;
18383             }
18384             return null;
18385         });
18386         return r;
18387     },
18388     indexOfPanel : function(p)
18389     {
18390         var r = false;
18391         Roo.each(this.tabs, function(t,i) {
18392             if (t.tabId == p.tabId) {
18393                 r = i;
18394                 return false;
18395             }
18396             return null;
18397         });
18398         return r;
18399     },
18400     /**
18401      * show a specific panel
18402      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18403      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18404      */
18405     showPanel : function (pan)
18406     {
18407         if(this.transition || typeof(pan) == 'undefined'){
18408             Roo.log("waiting for the transitionend");
18409             return false;
18410         }
18411         
18412         if (typeof(pan) == 'number') {
18413             pan = this.tabs[pan];
18414         }
18415         
18416         if (typeof(pan) == 'string') {
18417             pan = this.getPanelByName(pan);
18418         }
18419         
18420         var cur = this.getActivePanel();
18421         
18422         if(!pan || !cur){
18423             Roo.log('pan or acitve pan is undefined');
18424             return false;
18425         }
18426         
18427         if (pan.tabId == this.getActivePanel().tabId) {
18428             return true;
18429         }
18430         
18431         if (false === cur.fireEvent('beforedeactivate')) {
18432             return false;
18433         }
18434         
18435         if(this.bullets > 0 && !Roo.isTouch){
18436             this.setActiveBullet(this.indexOfPanel(pan));
18437         }
18438         
18439         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18440             
18441             //class="carousel-item carousel-item-next carousel-item-left"
18442             
18443             this.transition = true;
18444             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18445             var lr = dir == 'next' ? 'left' : 'right';
18446             pan.el.addClass(dir); // or prev
18447             pan.el.addClass('carousel-item-' + dir); // or prev
18448             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18449             cur.el.addClass(lr); // or right
18450             pan.el.addClass(lr);
18451             cur.el.addClass('carousel-item-' +lr); // or right
18452             pan.el.addClass('carousel-item-' +lr);
18453             
18454             
18455             var _this = this;
18456             cur.el.on('transitionend', function() {
18457                 Roo.log("trans end?");
18458                 
18459                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
18460                 pan.setActive(true);
18461                 
18462                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
18463                 cur.setActive(false);
18464                 
18465                 _this.transition = false;
18466                 
18467             }, this, { single:  true } );
18468             
18469             return true;
18470         }
18471         
18472         cur.setActive(false);
18473         pan.setActive(true);
18474         
18475         return true;
18476         
18477     },
18478     showPanelNext : function()
18479     {
18480         var i = this.indexOfPanel(this.getActivePanel());
18481         
18482         if (i >= this.tabs.length - 1 && !this.autoslide) {
18483             return;
18484         }
18485         
18486         if (i >= this.tabs.length - 1 && this.autoslide) {
18487             i = -1;
18488         }
18489         
18490         this.showPanel(this.tabs[i+1]);
18491     },
18492     
18493     showPanelPrev : function()
18494     {
18495         var i = this.indexOfPanel(this.getActivePanel());
18496         
18497         if (i  < 1 && !this.autoslide) {
18498             return;
18499         }
18500         
18501         if (i < 1 && this.autoslide) {
18502             i = this.tabs.length;
18503         }
18504         
18505         this.showPanel(this.tabs[i-1]);
18506     },
18507     
18508     
18509     addBullet: function()
18510     {
18511         if(!this.bullets || Roo.isTouch){
18512             return;
18513         }
18514         var ctr = this.el.select('.carousel-bullets',true).first();
18515         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18516         var bullet = ctr.createChild({
18517             cls : 'bullet bullet-' + i
18518         },ctr.dom.lastChild);
18519         
18520         
18521         var _this = this;
18522         
18523         bullet.on('click', (function(e, el, o, ii, t){
18524
18525             e.preventDefault();
18526
18527             this.showPanel(ii);
18528
18529             if(this.autoslide && this.slideFn){
18530                 clearInterval(this.slideFn);
18531                 this.slideFn = window.setInterval(function() {
18532                     _this.showPanelNext();
18533                 }, this.timer);
18534             }
18535
18536         }).createDelegate(this, [i, bullet], true));
18537                 
18538         
18539     },
18540      
18541     setActiveBullet : function(i)
18542     {
18543         if(Roo.isTouch){
18544             return;
18545         }
18546         
18547         Roo.each(this.el.select('.bullet', true).elements, function(el){
18548             el.removeClass('selected');
18549         });
18550
18551         var bullet = this.el.select('.bullet-' + i, true).first();
18552         
18553         if(!bullet){
18554             return;
18555         }
18556         
18557         bullet.addClass('selected');
18558     }
18559     
18560     
18561   
18562 });
18563
18564  
18565
18566  
18567  
18568 Roo.apply(Roo.bootstrap.TabGroup, {
18569     
18570     groups: {},
18571      /**
18572     * register a Navigation Group
18573     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18574     */
18575     register : function(navgrp)
18576     {
18577         this.groups[navgrp.navId] = navgrp;
18578         
18579     },
18580     /**
18581     * fetch a Navigation Group based on the navigation ID
18582     * if one does not exist , it will get created.
18583     * @param {string} the navgroup to add
18584     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18585     */
18586     get: function(navId) {
18587         if (typeof(this.groups[navId]) == 'undefined') {
18588             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18589         }
18590         return this.groups[navId] ;
18591     }
18592     
18593     
18594     
18595 });
18596
18597  /*
18598  * - LGPL
18599  *
18600  * TabPanel
18601  * 
18602  */
18603
18604 /**
18605  * @class Roo.bootstrap.TabPanel
18606  * @extends Roo.bootstrap.Component
18607  * Bootstrap TabPanel class
18608  * @cfg {Boolean} active panel active
18609  * @cfg {String} html panel content
18610  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18611  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18612  * @cfg {String} href click to link..
18613  * 
18614  * 
18615  * @constructor
18616  * Create a new TabPanel
18617  * @param {Object} config The config object
18618  */
18619
18620 Roo.bootstrap.TabPanel = function(config){
18621     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18622     this.addEvents({
18623         /**
18624              * @event changed
18625              * Fires when the active status changes
18626              * @param {Roo.bootstrap.TabPanel} this
18627              * @param {Boolean} state the new state
18628             
18629          */
18630         'changed': true,
18631         /**
18632              * @event beforedeactivate
18633              * Fires before a tab is de-activated - can be used to do validation on a form.
18634              * @param {Roo.bootstrap.TabPanel} this
18635              * @return {Boolean} false if there is an error
18636             
18637          */
18638         'beforedeactivate': true
18639      });
18640     
18641     this.tabId = this.tabId || Roo.id();
18642   
18643 };
18644
18645 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18646     
18647     active: false,
18648     html: false,
18649     tabId: false,
18650     navId : false,
18651     href : '',
18652     
18653     getAutoCreate : function(){
18654         
18655         
18656         var cfg = {
18657             tag: 'div',
18658             // item is needed for carousel - not sure if it has any effect otherwise
18659             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18660             html: this.html || ''
18661         };
18662         
18663         if(this.active){
18664             cfg.cls += ' active';
18665         }
18666         
18667         if(this.tabId){
18668             cfg.tabId = this.tabId;
18669         }
18670         
18671         
18672         
18673         return cfg;
18674     },
18675     
18676     initEvents:  function()
18677     {
18678         var p = this.parent();
18679         
18680         this.navId = this.navId || p.navId;
18681         
18682         if (typeof(this.navId) != 'undefined') {
18683             // not really needed.. but just in case.. parent should be a NavGroup.
18684             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18685             
18686             tg.register(this);
18687             
18688             var i = tg.tabs.length - 1;
18689             
18690             if(this.active && tg.bullets > 0 && i < tg.bullets){
18691                 tg.setActiveBullet(i);
18692             }
18693         }
18694         
18695         this.el.on('click', this.onClick, this);
18696         
18697         if(Roo.isTouch){
18698             this.el.on("touchstart", this.onTouchStart, this);
18699             this.el.on("touchmove", this.onTouchMove, this);
18700             this.el.on("touchend", this.onTouchEnd, this);
18701         }
18702         
18703     },
18704     
18705     onRender : function(ct, position)
18706     {
18707         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18708     },
18709     
18710     setActive : function(state)
18711     {
18712         Roo.log("panel - set active " + this.tabId + "=" + state);
18713         
18714         this.active = state;
18715         if (!state) {
18716             this.el.removeClass('active');
18717             
18718         } else  if (!this.el.hasClass('active')) {
18719             this.el.addClass('active');
18720         }
18721         
18722         this.fireEvent('changed', this, state);
18723     },
18724     
18725     onClick : function(e)
18726     {
18727         e.preventDefault();
18728         
18729         if(!this.href.length){
18730             return;
18731         }
18732         
18733         window.location.href = this.href;
18734     },
18735     
18736     startX : 0,
18737     startY : 0,
18738     endX : 0,
18739     endY : 0,
18740     swiping : false,
18741     
18742     onTouchStart : function(e)
18743     {
18744         this.swiping = false;
18745         
18746         this.startX = e.browserEvent.touches[0].clientX;
18747         this.startY = e.browserEvent.touches[0].clientY;
18748     },
18749     
18750     onTouchMove : function(e)
18751     {
18752         this.swiping = true;
18753         
18754         this.endX = e.browserEvent.touches[0].clientX;
18755         this.endY = e.browserEvent.touches[0].clientY;
18756     },
18757     
18758     onTouchEnd : function(e)
18759     {
18760         if(!this.swiping){
18761             this.onClick(e);
18762             return;
18763         }
18764         
18765         var tabGroup = this.parent();
18766         
18767         if(this.endX > this.startX){ // swiping right
18768             tabGroup.showPanelPrev();
18769             return;
18770         }
18771         
18772         if(this.startX > this.endX){ // swiping left
18773             tabGroup.showPanelNext();
18774             return;
18775         }
18776     }
18777     
18778     
18779 });
18780  
18781
18782  
18783
18784  /*
18785  * - LGPL
18786  *
18787  * DateField
18788  * 
18789  */
18790
18791 /**
18792  * @class Roo.bootstrap.DateField
18793  * @extends Roo.bootstrap.Input
18794  * Bootstrap DateField class
18795  * @cfg {Number} weekStart default 0
18796  * @cfg {String} viewMode default empty, (months|years)
18797  * @cfg {String} minViewMode default empty, (months|years)
18798  * @cfg {Number} startDate default -Infinity
18799  * @cfg {Number} endDate default Infinity
18800  * @cfg {Boolean} todayHighlight default false
18801  * @cfg {Boolean} todayBtn default false
18802  * @cfg {Boolean} calendarWeeks default false
18803  * @cfg {Object} daysOfWeekDisabled default empty
18804  * @cfg {Boolean} singleMode default false (true | false)
18805  * 
18806  * @cfg {Boolean} keyboardNavigation default true
18807  * @cfg {String} language default en
18808  * 
18809  * @constructor
18810  * Create a new DateField
18811  * @param {Object} config The config object
18812  */
18813
18814 Roo.bootstrap.DateField = function(config){
18815     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18816      this.addEvents({
18817             /**
18818              * @event show
18819              * Fires when this field show.
18820              * @param {Roo.bootstrap.DateField} this
18821              * @param {Mixed} date The date value
18822              */
18823             show : true,
18824             /**
18825              * @event show
18826              * Fires when this field hide.
18827              * @param {Roo.bootstrap.DateField} this
18828              * @param {Mixed} date The date value
18829              */
18830             hide : true,
18831             /**
18832              * @event select
18833              * Fires when select a date.
18834              * @param {Roo.bootstrap.DateField} this
18835              * @param {Mixed} date The date value
18836              */
18837             select : true,
18838             /**
18839              * @event beforeselect
18840              * Fires when before select a date.
18841              * @param {Roo.bootstrap.DateField} this
18842              * @param {Mixed} date The date value
18843              */
18844             beforeselect : true
18845         });
18846 };
18847
18848 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18849     
18850     /**
18851      * @cfg {String} format
18852      * The default date format string which can be overriden for localization support.  The format must be
18853      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18854      */
18855     format : "m/d/y",
18856     /**
18857      * @cfg {String} altFormats
18858      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18859      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18860      */
18861     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18862     
18863     weekStart : 0,
18864     
18865     viewMode : '',
18866     
18867     minViewMode : '',
18868     
18869     todayHighlight : false,
18870     
18871     todayBtn: false,
18872     
18873     language: 'en',
18874     
18875     keyboardNavigation: true,
18876     
18877     calendarWeeks: false,
18878     
18879     startDate: -Infinity,
18880     
18881     endDate: Infinity,
18882     
18883     daysOfWeekDisabled: [],
18884     
18885     _events: [],
18886     
18887     singleMode : false,
18888     
18889     UTCDate: function()
18890     {
18891         return new Date(Date.UTC.apply(Date, arguments));
18892     },
18893     
18894     UTCToday: function()
18895     {
18896         var today = new Date();
18897         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18898     },
18899     
18900     getDate: function() {
18901             var d = this.getUTCDate();
18902             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18903     },
18904     
18905     getUTCDate: function() {
18906             return this.date;
18907     },
18908     
18909     setDate: function(d) {
18910             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18911     },
18912     
18913     setUTCDate: function(d) {
18914             this.date = d;
18915             this.setValue(this.formatDate(this.date));
18916     },
18917         
18918     onRender: function(ct, position)
18919     {
18920         
18921         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18922         
18923         this.language = this.language || 'en';
18924         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18925         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18926         
18927         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18928         this.format = this.format || 'm/d/y';
18929         this.isInline = false;
18930         this.isInput = true;
18931         this.component = this.el.select('.add-on', true).first() || false;
18932         this.component = (this.component && this.component.length === 0) ? false : this.component;
18933         this.hasInput = this.component && this.inputEl().length;
18934         
18935         if (typeof(this.minViewMode === 'string')) {
18936             switch (this.minViewMode) {
18937                 case 'months':
18938                     this.minViewMode = 1;
18939                     break;
18940                 case 'years':
18941                     this.minViewMode = 2;
18942                     break;
18943                 default:
18944                     this.minViewMode = 0;
18945                     break;
18946             }
18947         }
18948         
18949         if (typeof(this.viewMode === 'string')) {
18950             switch (this.viewMode) {
18951                 case 'months':
18952                     this.viewMode = 1;
18953                     break;
18954                 case 'years':
18955                     this.viewMode = 2;
18956                     break;
18957                 default:
18958                     this.viewMode = 0;
18959                     break;
18960             }
18961         }
18962                 
18963         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18964         
18965 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18966         
18967         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18968         
18969         this.picker().on('mousedown', this.onMousedown, this);
18970         this.picker().on('click', this.onClick, this);
18971         
18972         this.picker().addClass('datepicker-dropdown');
18973         
18974         this.startViewMode = this.viewMode;
18975         
18976         if(this.singleMode){
18977             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18978                 v.setVisibilityMode(Roo.Element.DISPLAY);
18979                 v.hide();
18980             });
18981             
18982             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18983                 v.setStyle('width', '189px');
18984             });
18985         }
18986         
18987         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18988             if(!this.calendarWeeks){
18989                 v.remove();
18990                 return;
18991             }
18992             
18993             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18994             v.attr('colspan', function(i, val){
18995                 return parseInt(val) + 1;
18996             });
18997         });
18998                         
18999         
19000         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
19001         
19002         this.setStartDate(this.startDate);
19003         this.setEndDate(this.endDate);
19004         
19005         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
19006         
19007         this.fillDow();
19008         this.fillMonths();
19009         this.update();
19010         this.showMode();
19011         
19012         if(this.isInline) {
19013             this.showPopup();
19014         }
19015     },
19016     
19017     picker : function()
19018     {
19019         return this.pickerEl;
19020 //        return this.el.select('.datepicker', true).first();
19021     },
19022     
19023     fillDow: function()
19024     {
19025         var dowCnt = this.weekStart;
19026         
19027         var dow = {
19028             tag: 'tr',
19029             cn: [
19030                 
19031             ]
19032         };
19033         
19034         if(this.calendarWeeks){
19035             dow.cn.push({
19036                 tag: 'th',
19037                 cls: 'cw',
19038                 html: '&nbsp;'
19039             })
19040         }
19041         
19042         while (dowCnt < this.weekStart + 7) {
19043             dow.cn.push({
19044                 tag: 'th',
19045                 cls: 'dow',
19046                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
19047             });
19048         }
19049         
19050         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
19051     },
19052     
19053     fillMonths: function()
19054     {    
19055         var i = 0;
19056         var months = this.picker().select('>.datepicker-months td', true).first();
19057         
19058         months.dom.innerHTML = '';
19059         
19060         while (i < 12) {
19061             var month = {
19062                 tag: 'span',
19063                 cls: 'month',
19064                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
19065             };
19066             
19067             months.createChild(month);
19068         }
19069         
19070     },
19071     
19072     update: function()
19073     {
19074         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;
19075         
19076         if (this.date < this.startDate) {
19077             this.viewDate = new Date(this.startDate);
19078         } else if (this.date > this.endDate) {
19079             this.viewDate = new Date(this.endDate);
19080         } else {
19081             this.viewDate = new Date(this.date);
19082         }
19083         
19084         this.fill();
19085     },
19086     
19087     fill: function() 
19088     {
19089         var d = new Date(this.viewDate),
19090                 year = d.getUTCFullYear(),
19091                 month = d.getUTCMonth(),
19092                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
19093                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
19094                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
19095                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
19096                 currentDate = this.date && this.date.valueOf(),
19097                 today = this.UTCToday();
19098         
19099         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
19100         
19101 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19102         
19103 //        this.picker.select('>tfoot th.today').
19104 //                                              .text(dates[this.language].today)
19105 //                                              .toggle(this.todayBtn !== false);
19106     
19107         this.updateNavArrows();
19108         this.fillMonths();
19109                                                 
19110         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
19111         
19112         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
19113          
19114         prevMonth.setUTCDate(day);
19115         
19116         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
19117         
19118         var nextMonth = new Date(prevMonth);
19119         
19120         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
19121         
19122         nextMonth = nextMonth.valueOf();
19123         
19124         var fillMonths = false;
19125         
19126         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
19127         
19128         while(prevMonth.valueOf() <= nextMonth) {
19129             var clsName = '';
19130             
19131             if (prevMonth.getUTCDay() === this.weekStart) {
19132                 if(fillMonths){
19133                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
19134                 }
19135                     
19136                 fillMonths = {
19137                     tag: 'tr',
19138                     cn: []
19139                 };
19140                 
19141                 if(this.calendarWeeks){
19142                     // ISO 8601: First week contains first thursday.
19143                     // ISO also states week starts on Monday, but we can be more abstract here.
19144                     var
19145                     // Start of current week: based on weekstart/current date
19146                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
19147                     // Thursday of this week
19148                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
19149                     // First Thursday of year, year from thursday
19150                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
19151                     // Calendar week: ms between thursdays, div ms per day, div 7 days
19152                     calWeek =  (th - yth) / 864e5 / 7 + 1;
19153                     
19154                     fillMonths.cn.push({
19155                         tag: 'td',
19156                         cls: 'cw',
19157                         html: calWeek
19158                     });
19159                 }
19160             }
19161             
19162             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
19163                 clsName += ' old';
19164             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19165                 clsName += ' new';
19166             }
19167             if (this.todayHighlight &&
19168                 prevMonth.getUTCFullYear() == today.getFullYear() &&
19169                 prevMonth.getUTCMonth() == today.getMonth() &&
19170                 prevMonth.getUTCDate() == today.getDate()) {
19171                 clsName += ' today';
19172             }
19173             
19174             if (currentDate && prevMonth.valueOf() === currentDate) {
19175                 clsName += ' active';
19176             }
19177             
19178             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19179                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19180                     clsName += ' disabled';
19181             }
19182             
19183             fillMonths.cn.push({
19184                 tag: 'td',
19185                 cls: 'day ' + clsName,
19186                 html: prevMonth.getDate()
19187             });
19188             
19189             prevMonth.setDate(prevMonth.getDate()+1);
19190         }
19191           
19192         var currentYear = this.date && this.date.getUTCFullYear();
19193         var currentMonth = this.date && this.date.getUTCMonth();
19194         
19195         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19196         
19197         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19198             v.removeClass('active');
19199             
19200             if(currentYear === year && k === currentMonth){
19201                 v.addClass('active');
19202             }
19203             
19204             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19205                 v.addClass('disabled');
19206             }
19207             
19208         });
19209         
19210         
19211         year = parseInt(year/10, 10) * 10;
19212         
19213         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19214         
19215         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19216         
19217         year -= 1;
19218         for (var i = -1; i < 11; i++) {
19219             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19220                 tag: 'span',
19221                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19222                 html: year
19223             });
19224             
19225             year += 1;
19226         }
19227     },
19228     
19229     showMode: function(dir) 
19230     {
19231         if (dir) {
19232             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19233         }
19234         
19235         Roo.each(this.picker().select('>div',true).elements, function(v){
19236             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19237             v.hide();
19238         });
19239         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19240     },
19241     
19242     place: function()
19243     {
19244         if(this.isInline) {
19245             return;
19246         }
19247         
19248         this.picker().removeClass(['bottom', 'top']);
19249         
19250         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19251             /*
19252              * place to the top of element!
19253              *
19254              */
19255             
19256             this.picker().addClass('top');
19257             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19258             
19259             return;
19260         }
19261         
19262         this.picker().addClass('bottom');
19263         
19264         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19265     },
19266     
19267     parseDate : function(value)
19268     {
19269         if(!value || value instanceof Date){
19270             return value;
19271         }
19272         var v = Date.parseDate(value, this.format);
19273         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19274             v = Date.parseDate(value, 'Y-m-d');
19275         }
19276         if(!v && this.altFormats){
19277             if(!this.altFormatsArray){
19278                 this.altFormatsArray = this.altFormats.split("|");
19279             }
19280             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19281                 v = Date.parseDate(value, this.altFormatsArray[i]);
19282             }
19283         }
19284         return v;
19285     },
19286     
19287     formatDate : function(date, fmt)
19288     {   
19289         return (!date || !(date instanceof Date)) ?
19290         date : date.dateFormat(fmt || this.format);
19291     },
19292     
19293     onFocus : function()
19294     {
19295         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19296         this.showPopup();
19297     },
19298     
19299     onBlur : function()
19300     {
19301         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19302         
19303         var d = this.inputEl().getValue();
19304         
19305         this.setValue(d);
19306                 
19307         this.hidePopup();
19308     },
19309     
19310     showPopup : function()
19311     {
19312         this.picker().show();
19313         this.update();
19314         this.place();
19315         
19316         this.fireEvent('showpopup', this, this.date);
19317     },
19318     
19319     hidePopup : function()
19320     {
19321         if(this.isInline) {
19322             return;
19323         }
19324         this.picker().hide();
19325         this.viewMode = this.startViewMode;
19326         this.showMode();
19327         
19328         this.fireEvent('hidepopup', this, this.date);
19329         
19330     },
19331     
19332     onMousedown: function(e)
19333     {
19334         e.stopPropagation();
19335         e.preventDefault();
19336     },
19337     
19338     keyup: function(e)
19339     {
19340         Roo.bootstrap.DateField.superclass.keyup.call(this);
19341         this.update();
19342     },
19343
19344     setValue: function(v)
19345     {
19346         if(this.fireEvent('beforeselect', this, v) !== false){
19347             var d = new Date(this.parseDate(v) ).clearTime();
19348         
19349             if(isNaN(d.getTime())){
19350                 this.date = this.viewDate = '';
19351                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19352                 return;
19353             }
19354
19355             v = this.formatDate(d);
19356
19357             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19358
19359             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19360
19361             this.update();
19362
19363             this.fireEvent('select', this, this.date);
19364         }
19365     },
19366     
19367     getValue: function()
19368     {
19369         return this.formatDate(this.date);
19370     },
19371     
19372     fireKey: function(e)
19373     {
19374         if (!this.picker().isVisible()){
19375             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19376                 this.showPopup();
19377             }
19378             return;
19379         }
19380         
19381         var dateChanged = false,
19382         dir, day, month,
19383         newDate, newViewDate;
19384         
19385         switch(e.keyCode){
19386             case 27: // escape
19387                 this.hidePopup();
19388                 e.preventDefault();
19389                 break;
19390             case 37: // left
19391             case 39: // right
19392                 if (!this.keyboardNavigation) {
19393                     break;
19394                 }
19395                 dir = e.keyCode == 37 ? -1 : 1;
19396                 
19397                 if (e.ctrlKey){
19398                     newDate = this.moveYear(this.date, dir);
19399                     newViewDate = this.moveYear(this.viewDate, dir);
19400                 } else if (e.shiftKey){
19401                     newDate = this.moveMonth(this.date, dir);
19402                     newViewDate = this.moveMonth(this.viewDate, dir);
19403                 } else {
19404                     newDate = new Date(this.date);
19405                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19406                     newViewDate = new Date(this.viewDate);
19407                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19408                 }
19409                 if (this.dateWithinRange(newDate)){
19410                     this.date = newDate;
19411                     this.viewDate = newViewDate;
19412                     this.setValue(this.formatDate(this.date));
19413 //                    this.update();
19414                     e.preventDefault();
19415                     dateChanged = true;
19416                 }
19417                 break;
19418             case 38: // up
19419             case 40: // down
19420                 if (!this.keyboardNavigation) {
19421                     break;
19422                 }
19423                 dir = e.keyCode == 38 ? -1 : 1;
19424                 if (e.ctrlKey){
19425                     newDate = this.moveYear(this.date, dir);
19426                     newViewDate = this.moveYear(this.viewDate, dir);
19427                 } else if (e.shiftKey){
19428                     newDate = this.moveMonth(this.date, dir);
19429                     newViewDate = this.moveMonth(this.viewDate, dir);
19430                 } else {
19431                     newDate = new Date(this.date);
19432                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19433                     newViewDate = new Date(this.viewDate);
19434                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19435                 }
19436                 if (this.dateWithinRange(newDate)){
19437                     this.date = newDate;
19438                     this.viewDate = newViewDate;
19439                     this.setValue(this.formatDate(this.date));
19440 //                    this.update();
19441                     e.preventDefault();
19442                     dateChanged = true;
19443                 }
19444                 break;
19445             case 13: // enter
19446                 this.setValue(this.formatDate(this.date));
19447                 this.hidePopup();
19448                 e.preventDefault();
19449                 break;
19450             case 9: // tab
19451                 this.setValue(this.formatDate(this.date));
19452                 this.hidePopup();
19453                 break;
19454             case 16: // shift
19455             case 17: // ctrl
19456             case 18: // alt
19457                 break;
19458             default :
19459                 this.hidePopup();
19460                 
19461         }
19462     },
19463     
19464     
19465     onClick: function(e) 
19466     {
19467         e.stopPropagation();
19468         e.preventDefault();
19469         
19470         var target = e.getTarget();
19471         
19472         if(target.nodeName.toLowerCase() === 'i'){
19473             target = Roo.get(target).dom.parentNode;
19474         }
19475         
19476         var nodeName = target.nodeName;
19477         var className = target.className;
19478         var html = target.innerHTML;
19479         //Roo.log(nodeName);
19480         
19481         switch(nodeName.toLowerCase()) {
19482             case 'th':
19483                 switch(className) {
19484                     case 'switch':
19485                         this.showMode(1);
19486                         break;
19487                     case 'prev':
19488                     case 'next':
19489                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19490                         switch(this.viewMode){
19491                                 case 0:
19492                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19493                                         break;
19494                                 case 1:
19495                                 case 2:
19496                                         this.viewDate = this.moveYear(this.viewDate, dir);
19497                                         break;
19498                         }
19499                         this.fill();
19500                         break;
19501                     case 'today':
19502                         var date = new Date();
19503                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19504 //                        this.fill()
19505                         this.setValue(this.formatDate(this.date));
19506                         
19507                         this.hidePopup();
19508                         break;
19509                 }
19510                 break;
19511             case 'span':
19512                 if (className.indexOf('disabled') < 0) {
19513                     this.viewDate.setUTCDate(1);
19514                     if (className.indexOf('month') > -1) {
19515                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19516                     } else {
19517                         var year = parseInt(html, 10) || 0;
19518                         this.viewDate.setUTCFullYear(year);
19519                         
19520                     }
19521                     
19522                     if(this.singleMode){
19523                         this.setValue(this.formatDate(this.viewDate));
19524                         this.hidePopup();
19525                         return;
19526                     }
19527                     
19528                     this.showMode(-1);
19529                     this.fill();
19530                 }
19531                 break;
19532                 
19533             case 'td':
19534                 //Roo.log(className);
19535                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19536                     var day = parseInt(html, 10) || 1;
19537                     var year = this.viewDate.getUTCFullYear(),
19538                         month = this.viewDate.getUTCMonth();
19539
19540                     if (className.indexOf('old') > -1) {
19541                         if(month === 0 ){
19542                             month = 11;
19543                             year -= 1;
19544                         }else{
19545                             month -= 1;
19546                         }
19547                     } else if (className.indexOf('new') > -1) {
19548                         if (month == 11) {
19549                             month = 0;
19550                             year += 1;
19551                         } else {
19552                             month += 1;
19553                         }
19554                     }
19555                     //Roo.log([year,month,day]);
19556                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19557                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19558 //                    this.fill();
19559                     //Roo.log(this.formatDate(this.date));
19560                     this.setValue(this.formatDate(this.date));
19561                     this.hidePopup();
19562                 }
19563                 break;
19564         }
19565     },
19566     
19567     setStartDate: function(startDate)
19568     {
19569         this.startDate = startDate || -Infinity;
19570         if (this.startDate !== -Infinity) {
19571             this.startDate = this.parseDate(this.startDate);
19572         }
19573         this.update();
19574         this.updateNavArrows();
19575     },
19576
19577     setEndDate: function(endDate)
19578     {
19579         this.endDate = endDate || Infinity;
19580         if (this.endDate !== Infinity) {
19581             this.endDate = this.parseDate(this.endDate);
19582         }
19583         this.update();
19584         this.updateNavArrows();
19585     },
19586     
19587     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19588     {
19589         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19590         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19591             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19592         }
19593         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19594             return parseInt(d, 10);
19595         });
19596         this.update();
19597         this.updateNavArrows();
19598     },
19599     
19600     updateNavArrows: function() 
19601     {
19602         if(this.singleMode){
19603             return;
19604         }
19605         
19606         var d = new Date(this.viewDate),
19607         year = d.getUTCFullYear(),
19608         month = d.getUTCMonth();
19609         
19610         Roo.each(this.picker().select('.prev', true).elements, function(v){
19611             v.show();
19612             switch (this.viewMode) {
19613                 case 0:
19614
19615                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19616                         v.hide();
19617                     }
19618                     break;
19619                 case 1:
19620                 case 2:
19621                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19622                         v.hide();
19623                     }
19624                     break;
19625             }
19626         });
19627         
19628         Roo.each(this.picker().select('.next', true).elements, function(v){
19629             v.show();
19630             switch (this.viewMode) {
19631                 case 0:
19632
19633                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19634                         v.hide();
19635                     }
19636                     break;
19637                 case 1:
19638                 case 2:
19639                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19640                         v.hide();
19641                     }
19642                     break;
19643             }
19644         })
19645     },
19646     
19647     moveMonth: function(date, dir)
19648     {
19649         if (!dir) {
19650             return date;
19651         }
19652         var new_date = new Date(date.valueOf()),
19653         day = new_date.getUTCDate(),
19654         month = new_date.getUTCMonth(),
19655         mag = Math.abs(dir),
19656         new_month, test;
19657         dir = dir > 0 ? 1 : -1;
19658         if (mag == 1){
19659             test = dir == -1
19660             // If going back one month, make sure month is not current month
19661             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19662             ? function(){
19663                 return new_date.getUTCMonth() == month;
19664             }
19665             // If going forward one month, make sure month is as expected
19666             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19667             : function(){
19668                 return new_date.getUTCMonth() != new_month;
19669             };
19670             new_month = month + dir;
19671             new_date.setUTCMonth(new_month);
19672             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19673             if (new_month < 0 || new_month > 11) {
19674                 new_month = (new_month + 12) % 12;
19675             }
19676         } else {
19677             // For magnitudes >1, move one month at a time...
19678             for (var i=0; i<mag; i++) {
19679                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19680                 new_date = this.moveMonth(new_date, dir);
19681             }
19682             // ...then reset the day, keeping it in the new month
19683             new_month = new_date.getUTCMonth();
19684             new_date.setUTCDate(day);
19685             test = function(){
19686                 return new_month != new_date.getUTCMonth();
19687             };
19688         }
19689         // Common date-resetting loop -- if date is beyond end of month, make it
19690         // end of month
19691         while (test()){
19692             new_date.setUTCDate(--day);
19693             new_date.setUTCMonth(new_month);
19694         }
19695         return new_date;
19696     },
19697
19698     moveYear: function(date, dir)
19699     {
19700         return this.moveMonth(date, dir*12);
19701     },
19702
19703     dateWithinRange: function(date)
19704     {
19705         return date >= this.startDate && date <= this.endDate;
19706     },
19707
19708     
19709     remove: function() 
19710     {
19711         this.picker().remove();
19712     },
19713     
19714     validateValue : function(value)
19715     {
19716         if(this.getVisibilityEl().hasClass('hidden')){
19717             return true;
19718         }
19719         
19720         if(value.length < 1)  {
19721             if(this.allowBlank){
19722                 return true;
19723             }
19724             return false;
19725         }
19726         
19727         if(value.length < this.minLength){
19728             return false;
19729         }
19730         if(value.length > this.maxLength){
19731             return false;
19732         }
19733         if(this.vtype){
19734             var vt = Roo.form.VTypes;
19735             if(!vt[this.vtype](value, this)){
19736                 return false;
19737             }
19738         }
19739         if(typeof this.validator == "function"){
19740             var msg = this.validator(value);
19741             if(msg !== true){
19742                 return false;
19743             }
19744         }
19745         
19746         if(this.regex && !this.regex.test(value)){
19747             return false;
19748         }
19749         
19750         if(typeof(this.parseDate(value)) == 'undefined'){
19751             return false;
19752         }
19753         
19754         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19755             return false;
19756         }      
19757         
19758         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19759             return false;
19760         } 
19761         
19762         
19763         return true;
19764     },
19765     
19766     reset : function()
19767     {
19768         this.date = this.viewDate = '';
19769         
19770         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19771     }
19772    
19773 });
19774
19775 Roo.apply(Roo.bootstrap.DateField,  {
19776     
19777     head : {
19778         tag: 'thead',
19779         cn: [
19780         {
19781             tag: 'tr',
19782             cn: [
19783             {
19784                 tag: 'th',
19785                 cls: 'prev',
19786                 html: '<i class="fa fa-arrow-left"/>'
19787             },
19788             {
19789                 tag: 'th',
19790                 cls: 'switch',
19791                 colspan: '5'
19792             },
19793             {
19794                 tag: 'th',
19795                 cls: 'next',
19796                 html: '<i class="fa fa-arrow-right"/>'
19797             }
19798
19799             ]
19800         }
19801         ]
19802     },
19803     
19804     content : {
19805         tag: 'tbody',
19806         cn: [
19807         {
19808             tag: 'tr',
19809             cn: [
19810             {
19811                 tag: 'td',
19812                 colspan: '7'
19813             }
19814             ]
19815         }
19816         ]
19817     },
19818     
19819     footer : {
19820         tag: 'tfoot',
19821         cn: [
19822         {
19823             tag: 'tr',
19824             cn: [
19825             {
19826                 tag: 'th',
19827                 colspan: '7',
19828                 cls: 'today'
19829             }
19830                     
19831             ]
19832         }
19833         ]
19834     },
19835     
19836     dates:{
19837         en: {
19838             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19839             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19840             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19841             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19842             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19843             today: "Today"
19844         }
19845     },
19846     
19847     modes: [
19848     {
19849         clsName: 'days',
19850         navFnc: 'Month',
19851         navStep: 1
19852     },
19853     {
19854         clsName: 'months',
19855         navFnc: 'FullYear',
19856         navStep: 1
19857     },
19858     {
19859         clsName: 'years',
19860         navFnc: 'FullYear',
19861         navStep: 10
19862     }]
19863 });
19864
19865 Roo.apply(Roo.bootstrap.DateField,  {
19866   
19867     template : {
19868         tag: 'div',
19869         cls: 'datepicker dropdown-menu roo-dynamic',
19870         cn: [
19871         {
19872             tag: 'div',
19873             cls: 'datepicker-days',
19874             cn: [
19875             {
19876                 tag: 'table',
19877                 cls: 'table-condensed',
19878                 cn:[
19879                 Roo.bootstrap.DateField.head,
19880                 {
19881                     tag: 'tbody'
19882                 },
19883                 Roo.bootstrap.DateField.footer
19884                 ]
19885             }
19886             ]
19887         },
19888         {
19889             tag: 'div',
19890             cls: 'datepicker-months',
19891             cn: [
19892             {
19893                 tag: 'table',
19894                 cls: 'table-condensed',
19895                 cn:[
19896                 Roo.bootstrap.DateField.head,
19897                 Roo.bootstrap.DateField.content,
19898                 Roo.bootstrap.DateField.footer
19899                 ]
19900             }
19901             ]
19902         },
19903         {
19904             tag: 'div',
19905             cls: 'datepicker-years',
19906             cn: [
19907             {
19908                 tag: 'table',
19909                 cls: 'table-condensed',
19910                 cn:[
19911                 Roo.bootstrap.DateField.head,
19912                 Roo.bootstrap.DateField.content,
19913                 Roo.bootstrap.DateField.footer
19914                 ]
19915             }
19916             ]
19917         }
19918         ]
19919     }
19920 });
19921
19922  
19923
19924  /*
19925  * - LGPL
19926  *
19927  * TimeField
19928  * 
19929  */
19930
19931 /**
19932  * @class Roo.bootstrap.TimeField
19933  * @extends Roo.bootstrap.Input
19934  * Bootstrap DateField class
19935  * 
19936  * 
19937  * @constructor
19938  * Create a new TimeField
19939  * @param {Object} config The config object
19940  */
19941
19942 Roo.bootstrap.TimeField = function(config){
19943     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19944     this.addEvents({
19945             /**
19946              * @event show
19947              * Fires when this field show.
19948              * @param {Roo.bootstrap.DateField} thisthis
19949              * @param {Mixed} date The date value
19950              */
19951             show : true,
19952             /**
19953              * @event show
19954              * Fires when this field hide.
19955              * @param {Roo.bootstrap.DateField} this
19956              * @param {Mixed} date The date value
19957              */
19958             hide : true,
19959             /**
19960              * @event select
19961              * Fires when select a date.
19962              * @param {Roo.bootstrap.DateField} this
19963              * @param {Mixed} date The date value
19964              */
19965             select : true
19966         });
19967 };
19968
19969 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19970     
19971     /**
19972      * @cfg {String} format
19973      * The default time format string which can be overriden for localization support.  The format must be
19974      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19975      */
19976     format : "H:i",
19977        
19978     onRender: function(ct, position)
19979     {
19980         
19981         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19982                 
19983         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19984         
19985         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19986         
19987         this.pop = this.picker().select('>.datepicker-time',true).first();
19988         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19989         
19990         this.picker().on('mousedown', this.onMousedown, this);
19991         this.picker().on('click', this.onClick, this);
19992         
19993         this.picker().addClass('datepicker-dropdown');
19994     
19995         this.fillTime();
19996         this.update();
19997             
19998         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19999         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
20000         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
20001         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
20002         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
20003         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
20004
20005     },
20006     
20007     fireKey: function(e){
20008         if (!this.picker().isVisible()){
20009             if (e.keyCode == 27) { // allow escape to hide and re-show picker
20010                 this.show();
20011             }
20012             return;
20013         }
20014
20015         e.preventDefault();
20016         
20017         switch(e.keyCode){
20018             case 27: // escape
20019                 this.hide();
20020                 break;
20021             case 37: // left
20022             case 39: // right
20023                 this.onTogglePeriod();
20024                 break;
20025             case 38: // up
20026                 this.onIncrementMinutes();
20027                 break;
20028             case 40: // down
20029                 this.onDecrementMinutes();
20030                 break;
20031             case 13: // enter
20032             case 9: // tab
20033                 this.setTime();
20034                 break;
20035         }
20036     },
20037     
20038     onClick: function(e) {
20039         e.stopPropagation();
20040         e.preventDefault();
20041     },
20042     
20043     picker : function()
20044     {
20045         return this.el.select('.datepicker', true).first();
20046     },
20047     
20048     fillTime: function()
20049     {    
20050         var time = this.pop.select('tbody', true).first();
20051         
20052         time.dom.innerHTML = '';
20053         
20054         time.createChild({
20055             tag: 'tr',
20056             cn: [
20057                 {
20058                     tag: 'td',
20059                     cn: [
20060                         {
20061                             tag: 'a',
20062                             href: '#',
20063                             cls: 'btn',
20064                             cn: [
20065                                 {
20066                                     tag: 'span',
20067                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
20068                                 }
20069                             ]
20070                         } 
20071                     ]
20072                 },
20073                 {
20074                     tag: 'td',
20075                     cls: 'separator'
20076                 },
20077                 {
20078                     tag: 'td',
20079                     cn: [
20080                         {
20081                             tag: 'a',
20082                             href: '#',
20083                             cls: 'btn',
20084                             cn: [
20085                                 {
20086                                     tag: 'span',
20087                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
20088                                 }
20089                             ]
20090                         }
20091                     ]
20092                 },
20093                 {
20094                     tag: 'td',
20095                     cls: 'separator'
20096                 }
20097             ]
20098         });
20099         
20100         time.createChild({
20101             tag: 'tr',
20102             cn: [
20103                 {
20104                     tag: 'td',
20105                     cn: [
20106                         {
20107                             tag: 'span',
20108                             cls: 'timepicker-hour',
20109                             html: '00'
20110                         }  
20111                     ]
20112                 },
20113                 {
20114                     tag: 'td',
20115                     cls: 'separator',
20116                     html: ':'
20117                 },
20118                 {
20119                     tag: 'td',
20120                     cn: [
20121                         {
20122                             tag: 'span',
20123                             cls: 'timepicker-minute',
20124                             html: '00'
20125                         }  
20126                     ]
20127                 },
20128                 {
20129                     tag: 'td',
20130                     cls: 'separator'
20131                 },
20132                 {
20133                     tag: 'td',
20134                     cn: [
20135                         {
20136                             tag: 'button',
20137                             type: 'button',
20138                             cls: 'btn btn-primary period',
20139                             html: 'AM'
20140                             
20141                         }
20142                     ]
20143                 }
20144             ]
20145         });
20146         
20147         time.createChild({
20148             tag: 'tr',
20149             cn: [
20150                 {
20151                     tag: 'td',
20152                     cn: [
20153                         {
20154                             tag: 'a',
20155                             href: '#',
20156                             cls: 'btn',
20157                             cn: [
20158                                 {
20159                                     tag: 'span',
20160                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
20161                                 }
20162                             ]
20163                         }
20164                     ]
20165                 },
20166                 {
20167                     tag: 'td',
20168                     cls: 'separator'
20169                 },
20170                 {
20171                     tag: 'td',
20172                     cn: [
20173                         {
20174                             tag: 'a',
20175                             href: '#',
20176                             cls: 'btn',
20177                             cn: [
20178                                 {
20179                                     tag: 'span',
20180                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
20181                                 }
20182                             ]
20183                         }
20184                     ]
20185                 },
20186                 {
20187                     tag: 'td',
20188                     cls: 'separator'
20189                 }
20190             ]
20191         });
20192         
20193     },
20194     
20195     update: function()
20196     {
20197         
20198         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20199         
20200         this.fill();
20201     },
20202     
20203     fill: function() 
20204     {
20205         var hours = this.time.getHours();
20206         var minutes = this.time.getMinutes();
20207         var period = 'AM';
20208         
20209         if(hours > 11){
20210             period = 'PM';
20211         }
20212         
20213         if(hours == 0){
20214             hours = 12;
20215         }
20216         
20217         
20218         if(hours > 12){
20219             hours = hours - 12;
20220         }
20221         
20222         if(hours < 10){
20223             hours = '0' + hours;
20224         }
20225         
20226         if(minutes < 10){
20227             minutes = '0' + minutes;
20228         }
20229         
20230         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20231         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20232         this.pop.select('button', true).first().dom.innerHTML = period;
20233         
20234     },
20235     
20236     place: function()
20237     {   
20238         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20239         
20240         var cls = ['bottom'];
20241         
20242         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20243             cls.pop();
20244             cls.push('top');
20245         }
20246         
20247         cls.push('right');
20248         
20249         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20250             cls.pop();
20251             cls.push('left');
20252         }
20253         
20254         this.picker().addClass(cls.join('-'));
20255         
20256         var _this = this;
20257         
20258         Roo.each(cls, function(c){
20259             if(c == 'bottom'){
20260                 _this.picker().setTop(_this.inputEl().getHeight());
20261                 return;
20262             }
20263             if(c == 'top'){
20264                 _this.picker().setTop(0 - _this.picker().getHeight());
20265                 return;
20266             }
20267             
20268             if(c == 'left'){
20269                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20270                 return;
20271             }
20272             if(c == 'right'){
20273                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20274                 return;
20275             }
20276         });
20277         
20278     },
20279   
20280     onFocus : function()
20281     {
20282         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20283         this.show();
20284     },
20285     
20286     onBlur : function()
20287     {
20288         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20289         this.hide();
20290     },
20291     
20292     show : function()
20293     {
20294         this.picker().show();
20295         this.pop.show();
20296         this.update();
20297         this.place();
20298         
20299         this.fireEvent('show', this, this.date);
20300     },
20301     
20302     hide : function()
20303     {
20304         this.picker().hide();
20305         this.pop.hide();
20306         
20307         this.fireEvent('hide', this, this.date);
20308     },
20309     
20310     setTime : function()
20311     {
20312         this.hide();
20313         this.setValue(this.time.format(this.format));
20314         
20315         this.fireEvent('select', this, this.date);
20316         
20317         
20318     },
20319     
20320     onMousedown: function(e){
20321         e.stopPropagation();
20322         e.preventDefault();
20323     },
20324     
20325     onIncrementHours: function()
20326     {
20327         Roo.log('onIncrementHours');
20328         this.time = this.time.add(Date.HOUR, 1);
20329         this.update();
20330         
20331     },
20332     
20333     onDecrementHours: function()
20334     {
20335         Roo.log('onDecrementHours');
20336         this.time = this.time.add(Date.HOUR, -1);
20337         this.update();
20338     },
20339     
20340     onIncrementMinutes: function()
20341     {
20342         Roo.log('onIncrementMinutes');
20343         this.time = this.time.add(Date.MINUTE, 1);
20344         this.update();
20345     },
20346     
20347     onDecrementMinutes: function()
20348     {
20349         Roo.log('onDecrementMinutes');
20350         this.time = this.time.add(Date.MINUTE, -1);
20351         this.update();
20352     },
20353     
20354     onTogglePeriod: function()
20355     {
20356         Roo.log('onTogglePeriod');
20357         this.time = this.time.add(Date.HOUR, 12);
20358         this.update();
20359     }
20360     
20361    
20362 });
20363
20364 Roo.apply(Roo.bootstrap.TimeField,  {
20365     
20366     content : {
20367         tag: 'tbody',
20368         cn: [
20369             {
20370                 tag: 'tr',
20371                 cn: [
20372                 {
20373                     tag: 'td',
20374                     colspan: '7'
20375                 }
20376                 ]
20377             }
20378         ]
20379     },
20380     
20381     footer : {
20382         tag: 'tfoot',
20383         cn: [
20384             {
20385                 tag: 'tr',
20386                 cn: [
20387                 {
20388                     tag: 'th',
20389                     colspan: '7',
20390                     cls: '',
20391                     cn: [
20392                         {
20393                             tag: 'button',
20394                             cls: 'btn btn-info ok',
20395                             html: 'OK'
20396                         }
20397                     ]
20398                 }
20399
20400                 ]
20401             }
20402         ]
20403     }
20404 });
20405
20406 Roo.apply(Roo.bootstrap.TimeField,  {
20407   
20408     template : {
20409         tag: 'div',
20410         cls: 'datepicker dropdown-menu',
20411         cn: [
20412             {
20413                 tag: 'div',
20414                 cls: 'datepicker-time',
20415                 cn: [
20416                 {
20417                     tag: 'table',
20418                     cls: 'table-condensed',
20419                     cn:[
20420                     Roo.bootstrap.TimeField.content,
20421                     Roo.bootstrap.TimeField.footer
20422                     ]
20423                 }
20424                 ]
20425             }
20426         ]
20427     }
20428 });
20429
20430  
20431
20432  /*
20433  * - LGPL
20434  *
20435  * MonthField
20436  * 
20437  */
20438
20439 /**
20440  * @class Roo.bootstrap.MonthField
20441  * @extends Roo.bootstrap.Input
20442  * Bootstrap MonthField class
20443  * 
20444  * @cfg {String} language default en
20445  * 
20446  * @constructor
20447  * Create a new MonthField
20448  * @param {Object} config The config object
20449  */
20450
20451 Roo.bootstrap.MonthField = function(config){
20452     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20453     
20454     this.addEvents({
20455         /**
20456          * @event show
20457          * Fires when this field show.
20458          * @param {Roo.bootstrap.MonthField} this
20459          * @param {Mixed} date The date value
20460          */
20461         show : true,
20462         /**
20463          * @event show
20464          * Fires when this field hide.
20465          * @param {Roo.bootstrap.MonthField} this
20466          * @param {Mixed} date The date value
20467          */
20468         hide : true,
20469         /**
20470          * @event select
20471          * Fires when select a date.
20472          * @param {Roo.bootstrap.MonthField} this
20473          * @param {String} oldvalue The old value
20474          * @param {String} newvalue The new value
20475          */
20476         select : true
20477     });
20478 };
20479
20480 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20481     
20482     onRender: function(ct, position)
20483     {
20484         
20485         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20486         
20487         this.language = this.language || 'en';
20488         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20489         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20490         
20491         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20492         this.isInline = false;
20493         this.isInput = true;
20494         this.component = this.el.select('.add-on', true).first() || false;
20495         this.component = (this.component && this.component.length === 0) ? false : this.component;
20496         this.hasInput = this.component && this.inputEL().length;
20497         
20498         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20499         
20500         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20501         
20502         this.picker().on('mousedown', this.onMousedown, this);
20503         this.picker().on('click', this.onClick, this);
20504         
20505         this.picker().addClass('datepicker-dropdown');
20506         
20507         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20508             v.setStyle('width', '189px');
20509         });
20510         
20511         this.fillMonths();
20512         
20513         this.update();
20514         
20515         if(this.isInline) {
20516             this.show();
20517         }
20518         
20519     },
20520     
20521     setValue: function(v, suppressEvent)
20522     {   
20523         var o = this.getValue();
20524         
20525         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20526         
20527         this.update();
20528
20529         if(suppressEvent !== true){
20530             this.fireEvent('select', this, o, v);
20531         }
20532         
20533     },
20534     
20535     getValue: function()
20536     {
20537         return this.value;
20538     },
20539     
20540     onClick: function(e) 
20541     {
20542         e.stopPropagation();
20543         e.preventDefault();
20544         
20545         var target = e.getTarget();
20546         
20547         if(target.nodeName.toLowerCase() === 'i'){
20548             target = Roo.get(target).dom.parentNode;
20549         }
20550         
20551         var nodeName = target.nodeName;
20552         var className = target.className;
20553         var html = target.innerHTML;
20554         
20555         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20556             return;
20557         }
20558         
20559         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20560         
20561         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20562         
20563         this.hide();
20564                         
20565     },
20566     
20567     picker : function()
20568     {
20569         return this.pickerEl;
20570     },
20571     
20572     fillMonths: function()
20573     {    
20574         var i = 0;
20575         var months = this.picker().select('>.datepicker-months td', true).first();
20576         
20577         months.dom.innerHTML = '';
20578         
20579         while (i < 12) {
20580             var month = {
20581                 tag: 'span',
20582                 cls: 'month',
20583                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20584             };
20585             
20586             months.createChild(month);
20587         }
20588         
20589     },
20590     
20591     update: function()
20592     {
20593         var _this = this;
20594         
20595         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20596             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20597         }
20598         
20599         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20600             e.removeClass('active');
20601             
20602             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20603                 e.addClass('active');
20604             }
20605         })
20606     },
20607     
20608     place: function()
20609     {
20610         if(this.isInline) {
20611             return;
20612         }
20613         
20614         this.picker().removeClass(['bottom', 'top']);
20615         
20616         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20617             /*
20618              * place to the top of element!
20619              *
20620              */
20621             
20622             this.picker().addClass('top');
20623             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20624             
20625             return;
20626         }
20627         
20628         this.picker().addClass('bottom');
20629         
20630         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20631     },
20632     
20633     onFocus : function()
20634     {
20635         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20636         this.show();
20637     },
20638     
20639     onBlur : function()
20640     {
20641         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20642         
20643         var d = this.inputEl().getValue();
20644         
20645         this.setValue(d);
20646                 
20647         this.hide();
20648     },
20649     
20650     show : function()
20651     {
20652         this.picker().show();
20653         this.picker().select('>.datepicker-months', true).first().show();
20654         this.update();
20655         this.place();
20656         
20657         this.fireEvent('show', this, this.date);
20658     },
20659     
20660     hide : function()
20661     {
20662         if(this.isInline) {
20663             return;
20664         }
20665         this.picker().hide();
20666         this.fireEvent('hide', this, this.date);
20667         
20668     },
20669     
20670     onMousedown: function(e)
20671     {
20672         e.stopPropagation();
20673         e.preventDefault();
20674     },
20675     
20676     keyup: function(e)
20677     {
20678         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20679         this.update();
20680     },
20681
20682     fireKey: function(e)
20683     {
20684         if (!this.picker().isVisible()){
20685             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20686                 this.show();
20687             }
20688             return;
20689         }
20690         
20691         var dir;
20692         
20693         switch(e.keyCode){
20694             case 27: // escape
20695                 this.hide();
20696                 e.preventDefault();
20697                 break;
20698             case 37: // left
20699             case 39: // right
20700                 dir = e.keyCode == 37 ? -1 : 1;
20701                 
20702                 this.vIndex = this.vIndex + dir;
20703                 
20704                 if(this.vIndex < 0){
20705                     this.vIndex = 0;
20706                 }
20707                 
20708                 if(this.vIndex > 11){
20709                     this.vIndex = 11;
20710                 }
20711                 
20712                 if(isNaN(this.vIndex)){
20713                     this.vIndex = 0;
20714                 }
20715                 
20716                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20717                 
20718                 break;
20719             case 38: // up
20720             case 40: // down
20721                 
20722                 dir = e.keyCode == 38 ? -1 : 1;
20723                 
20724                 this.vIndex = this.vIndex + dir * 4;
20725                 
20726                 if(this.vIndex < 0){
20727                     this.vIndex = 0;
20728                 }
20729                 
20730                 if(this.vIndex > 11){
20731                     this.vIndex = 11;
20732                 }
20733                 
20734                 if(isNaN(this.vIndex)){
20735                     this.vIndex = 0;
20736                 }
20737                 
20738                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20739                 break;
20740                 
20741             case 13: // enter
20742                 
20743                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20744                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20745                 }
20746                 
20747                 this.hide();
20748                 e.preventDefault();
20749                 break;
20750             case 9: // tab
20751                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20752                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20753                 }
20754                 this.hide();
20755                 break;
20756             case 16: // shift
20757             case 17: // ctrl
20758             case 18: // alt
20759                 break;
20760             default :
20761                 this.hide();
20762                 
20763         }
20764     },
20765     
20766     remove: function() 
20767     {
20768         this.picker().remove();
20769     }
20770    
20771 });
20772
20773 Roo.apply(Roo.bootstrap.MonthField,  {
20774     
20775     content : {
20776         tag: 'tbody',
20777         cn: [
20778         {
20779             tag: 'tr',
20780             cn: [
20781             {
20782                 tag: 'td',
20783                 colspan: '7'
20784             }
20785             ]
20786         }
20787         ]
20788     },
20789     
20790     dates:{
20791         en: {
20792             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20793             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20794         }
20795     }
20796 });
20797
20798 Roo.apply(Roo.bootstrap.MonthField,  {
20799   
20800     template : {
20801         tag: 'div',
20802         cls: 'datepicker dropdown-menu roo-dynamic',
20803         cn: [
20804             {
20805                 tag: 'div',
20806                 cls: 'datepicker-months',
20807                 cn: [
20808                 {
20809                     tag: 'table',
20810                     cls: 'table-condensed',
20811                     cn:[
20812                         Roo.bootstrap.DateField.content
20813                     ]
20814                 }
20815                 ]
20816             }
20817         ]
20818     }
20819 });
20820
20821  
20822
20823  
20824  /*
20825  * - LGPL
20826  *
20827  * CheckBox
20828  * 
20829  */
20830
20831 /**
20832  * @class Roo.bootstrap.CheckBox
20833  * @extends Roo.bootstrap.Input
20834  * Bootstrap CheckBox class
20835  * 
20836  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20837  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20838  * @cfg {String} boxLabel The text that appears beside the checkbox
20839  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20840  * @cfg {Boolean} checked initnal the element
20841  * @cfg {Boolean} inline inline the element (default false)
20842  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20843  * @cfg {String} tooltip label tooltip
20844  * 
20845  * @constructor
20846  * Create a new CheckBox
20847  * @param {Object} config The config object
20848  */
20849
20850 Roo.bootstrap.CheckBox = function(config){
20851     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20852    
20853     this.addEvents({
20854         /**
20855         * @event check
20856         * Fires when the element is checked or unchecked.
20857         * @param {Roo.bootstrap.CheckBox} this This input
20858         * @param {Boolean} checked The new checked value
20859         */
20860        check : true,
20861        /**
20862         * @event click
20863         * Fires when the element is click.
20864         * @param {Roo.bootstrap.CheckBox} this This input
20865         */
20866        click : true
20867     });
20868     
20869 };
20870
20871 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20872   
20873     inputType: 'checkbox',
20874     inputValue: 1,
20875     valueOff: 0,
20876     boxLabel: false,
20877     checked: false,
20878     weight : false,
20879     inline: false,
20880     tooltip : '',
20881     
20882     getAutoCreate : function()
20883     {
20884         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20885         
20886         var id = Roo.id();
20887         
20888         var cfg = {};
20889         
20890         cfg.cls = 'form-group ' + this.inputType; //input-group
20891         
20892         if(this.inline){
20893             cfg.cls += ' ' + this.inputType + '-inline';
20894         }
20895         
20896         var input =  {
20897             tag: 'input',
20898             id : id,
20899             type : this.inputType,
20900             value : this.inputValue,
20901             cls : 'roo-' + this.inputType, //'form-box',
20902             placeholder : this.placeholder || ''
20903             
20904         };
20905         
20906         if(this.inputType != 'radio'){
20907             var hidden =  {
20908                 tag: 'input',
20909                 type : 'hidden',
20910                 cls : 'roo-hidden-value',
20911                 value : this.checked ? this.inputValue : this.valueOff
20912             };
20913         }
20914         
20915             
20916         if (this.weight) { // Validity check?
20917             cfg.cls += " " + this.inputType + "-" + this.weight;
20918         }
20919         
20920         if (this.disabled) {
20921             input.disabled=true;
20922         }
20923         
20924         if(this.checked){
20925             input.checked = this.checked;
20926         }
20927         
20928         if (this.name) {
20929             
20930             input.name = this.name;
20931             
20932             if(this.inputType != 'radio'){
20933                 hidden.name = this.name;
20934                 input.name = '_hidden_' + this.name;
20935             }
20936         }
20937         
20938         if (this.size) {
20939             input.cls += ' input-' + this.size;
20940         }
20941         
20942         var settings=this;
20943         
20944         ['xs','sm','md','lg'].map(function(size){
20945             if (settings[size]) {
20946                 cfg.cls += ' col-' + size + '-' + settings[size];
20947             }
20948         });
20949         
20950         var inputblock = input;
20951          
20952         if (this.before || this.after) {
20953             
20954             inputblock = {
20955                 cls : 'input-group',
20956                 cn :  [] 
20957             };
20958             
20959             if (this.before) {
20960                 inputblock.cn.push({
20961                     tag :'span',
20962                     cls : 'input-group-addon',
20963                     html : this.before
20964                 });
20965             }
20966             
20967             inputblock.cn.push(input);
20968             
20969             if(this.inputType != 'radio'){
20970                 inputblock.cn.push(hidden);
20971             }
20972             
20973             if (this.after) {
20974                 inputblock.cn.push({
20975                     tag :'span',
20976                     cls : 'input-group-addon',
20977                     html : this.after
20978                 });
20979             }
20980             
20981         }
20982         
20983         if (align ==='left' && this.fieldLabel.length) {
20984 //                Roo.log("left and has label");
20985             cfg.cn = [
20986                 {
20987                     tag: 'label',
20988                     'for' :  id,
20989                     cls : 'control-label',
20990                     html : this.fieldLabel
20991                 },
20992                 {
20993                     cls : "", 
20994                     cn: [
20995                         inputblock
20996                     ]
20997                 }
20998             ];
20999             
21000             if(this.labelWidth > 12){
21001                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
21002             }
21003             
21004             if(this.labelWidth < 13 && this.labelmd == 0){
21005                 this.labelmd = this.labelWidth;
21006             }
21007             
21008             if(this.labellg > 0){
21009                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
21010                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
21011             }
21012             
21013             if(this.labelmd > 0){
21014                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
21015                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
21016             }
21017             
21018             if(this.labelsm > 0){
21019                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
21020                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
21021             }
21022             
21023             if(this.labelxs > 0){
21024                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
21025                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
21026             }
21027             
21028         } else if ( this.fieldLabel.length) {
21029 //                Roo.log(" label");
21030                 cfg.cn = [
21031                    
21032                     {
21033                         tag: this.boxLabel ? 'span' : 'label',
21034                         'for': id,
21035                         cls: 'control-label box-input-label',
21036                         //cls : 'input-group-addon',
21037                         html : this.fieldLabel
21038                     },
21039                     
21040                     inputblock
21041                     
21042                 ];
21043
21044         } else {
21045             
21046 //                Roo.log(" no label && no align");
21047                 cfg.cn = [  inputblock ] ;
21048                 
21049                 
21050         }
21051         
21052         if(this.boxLabel){
21053              var boxLabelCfg = {
21054                 tag: 'label',
21055                 //'for': id, // box label is handled by onclick - so no for...
21056                 cls: 'box-label',
21057                 html: this.boxLabel
21058             };
21059             
21060             if(this.tooltip){
21061                 boxLabelCfg.tooltip = this.tooltip;
21062             }
21063              
21064             cfg.cn.push(boxLabelCfg);
21065         }
21066         
21067         if(this.inputType != 'radio'){
21068             cfg.cn.push(hidden);
21069         }
21070         
21071         return cfg;
21072         
21073     },
21074     
21075     /**
21076      * return the real input element.
21077      */
21078     inputEl: function ()
21079     {
21080         return this.el.select('input.roo-' + this.inputType,true).first();
21081     },
21082     hiddenEl: function ()
21083     {
21084         return this.el.select('input.roo-hidden-value',true).first();
21085     },
21086     
21087     labelEl: function()
21088     {
21089         return this.el.select('label.control-label',true).first();
21090     },
21091     /* depricated... */
21092     
21093     label: function()
21094     {
21095         return this.labelEl();
21096     },
21097     
21098     boxLabelEl: function()
21099     {
21100         return this.el.select('label.box-label',true).first();
21101     },
21102     
21103     initEvents : function()
21104     {
21105 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
21106         
21107         this.inputEl().on('click', this.onClick,  this);
21108         
21109         if (this.boxLabel) { 
21110             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
21111         }
21112         
21113         this.startValue = this.getValue();
21114         
21115         if(this.groupId){
21116             Roo.bootstrap.CheckBox.register(this);
21117         }
21118     },
21119     
21120     onClick : function(e)
21121     {   
21122         if(this.fireEvent('click', this, e) !== false){
21123             this.setChecked(!this.checked);
21124         }
21125         
21126     },
21127     
21128     setChecked : function(state,suppressEvent)
21129     {
21130         this.startValue = this.getValue();
21131
21132         if(this.inputType == 'radio'){
21133             
21134             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21135                 e.dom.checked = false;
21136             });
21137             
21138             this.inputEl().dom.checked = true;
21139             
21140             this.inputEl().dom.value = this.inputValue;
21141             
21142             if(suppressEvent !== true){
21143                 this.fireEvent('check', this, true);
21144             }
21145             
21146             this.validate();
21147             
21148             return;
21149         }
21150         
21151         this.checked = state;
21152         
21153         this.inputEl().dom.checked = state;
21154         
21155         
21156         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
21157         
21158         if(suppressEvent !== true){
21159             this.fireEvent('check', this, state);
21160         }
21161         
21162         this.validate();
21163     },
21164     
21165     getValue : function()
21166     {
21167         if(this.inputType == 'radio'){
21168             return this.getGroupValue();
21169         }
21170         
21171         return this.hiddenEl().dom.value;
21172         
21173     },
21174     
21175     getGroupValue : function()
21176     {
21177         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21178             return '';
21179         }
21180         
21181         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21182     },
21183     
21184     setValue : function(v,suppressEvent)
21185     {
21186         if(this.inputType == 'radio'){
21187             this.setGroupValue(v, suppressEvent);
21188             return;
21189         }
21190         
21191         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21192         
21193         this.validate();
21194     },
21195     
21196     setGroupValue : function(v, suppressEvent)
21197     {
21198         this.startValue = this.getValue();
21199         
21200         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21201             e.dom.checked = false;
21202             
21203             if(e.dom.value == v){
21204                 e.dom.checked = true;
21205             }
21206         });
21207         
21208         if(suppressEvent !== true){
21209             this.fireEvent('check', this, true);
21210         }
21211
21212         this.validate();
21213         
21214         return;
21215     },
21216     
21217     validate : function()
21218     {
21219         if(this.getVisibilityEl().hasClass('hidden')){
21220             return true;
21221         }
21222         
21223         if(
21224                 this.disabled || 
21225                 (this.inputType == 'radio' && this.validateRadio()) ||
21226                 (this.inputType == 'checkbox' && this.validateCheckbox())
21227         ){
21228             this.markValid();
21229             return true;
21230         }
21231         
21232         this.markInvalid();
21233         return false;
21234     },
21235     
21236     validateRadio : function()
21237     {
21238         if(this.getVisibilityEl().hasClass('hidden')){
21239             return true;
21240         }
21241         
21242         if(this.allowBlank){
21243             return true;
21244         }
21245         
21246         var valid = false;
21247         
21248         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21249             if(!e.dom.checked){
21250                 return;
21251             }
21252             
21253             valid = true;
21254             
21255             return false;
21256         });
21257         
21258         return valid;
21259     },
21260     
21261     validateCheckbox : function()
21262     {
21263         if(!this.groupId){
21264             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21265             //return (this.getValue() == this.inputValue) ? true : false;
21266         }
21267         
21268         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21269         
21270         if(!group){
21271             return false;
21272         }
21273         
21274         var r = false;
21275         
21276         for(var i in group){
21277             if(group[i].el.isVisible(true)){
21278                 r = false;
21279                 break;
21280             }
21281             
21282             r = true;
21283         }
21284         
21285         for(var i in group){
21286             if(r){
21287                 break;
21288             }
21289             
21290             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21291         }
21292         
21293         return r;
21294     },
21295     
21296     /**
21297      * Mark this field as valid
21298      */
21299     markValid : function()
21300     {
21301         var _this = this;
21302         
21303         this.fireEvent('valid', this);
21304         
21305         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21306         
21307         if(this.groupId){
21308             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21309         }
21310         
21311         if(label){
21312             label.markValid();
21313         }
21314
21315         if(this.inputType == 'radio'){
21316             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21317                 var fg = e.findParent('.form-group', false, true);
21318                 if (Roo.bootstrap.version == 3) {
21319                     fg.removeClass([_this.invalidClass, _this.validClass]);
21320                     fg.addClass(_this.validClass);
21321                 } else {
21322                     fg.removeClass(['is-valid', 'is-invalid']);
21323                     fg.addClass('is-valid');
21324                 }
21325             });
21326             
21327             return;
21328         }
21329
21330         if(!this.groupId){
21331             var fg = this.el.findParent('.form-group', false, true);
21332             if (Roo.bootstrap.version == 3) {
21333                 fg.removeClass([this.invalidClass, this.validClass]);
21334                 fg.addClass(this.validClass);
21335             } else {
21336                 fg.removeClass(['is-valid', 'is-invalid']);
21337                 fg.addClass('is-valid');
21338             }
21339             return;
21340         }
21341         
21342         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21343         
21344         if(!group){
21345             return;
21346         }
21347         
21348         for(var i in group){
21349             var fg = group[i].el.findParent('.form-group', false, true);
21350             if (Roo.bootstrap.version == 3) {
21351                 fg.removeClass([this.invalidClass, this.validClass]);
21352                 fg.addClass(this.validClass);
21353             } else {
21354                 fg.removeClass(['is-valid', 'is-invalid']);
21355                 fg.addClass('is-valid');
21356             }
21357         }
21358     },
21359     
21360      /**
21361      * Mark this field as invalid
21362      * @param {String} msg The validation message
21363      */
21364     markInvalid : function(msg)
21365     {
21366         if(this.allowBlank){
21367             return;
21368         }
21369         
21370         var _this = this;
21371         
21372         this.fireEvent('invalid', this, msg);
21373         
21374         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21375         
21376         if(this.groupId){
21377             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21378         }
21379         
21380         if(label){
21381             label.markInvalid();
21382         }
21383             
21384         if(this.inputType == 'radio'){
21385             
21386             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21387                 var fg = e.findParent('.form-group', false, true);
21388                 if (Roo.bootstrap.version == 3) {
21389                     fg.removeClass([_this.invalidClass, _this.validClass]);
21390                     fg.addClass(_this.invalidClass);
21391                 } else {
21392                     fg.removeClass(['is-invalid', 'is-valid']);
21393                     fg.addClass('is-invalid');
21394                 }
21395             });
21396             
21397             return;
21398         }
21399         
21400         if(!this.groupId){
21401             var fg = this.el.findParent('.form-group', false, true);
21402             if (Roo.bootstrap.version == 3) {
21403                 fg.removeClass([_this.invalidClass, _this.validClass]);
21404                 fg.addClass(_this.invalidClass);
21405             } else {
21406                 fg.removeClass(['is-invalid', 'is-valid']);
21407                 fg.addClass('is-invalid');
21408             }
21409             return;
21410         }
21411         
21412         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21413         
21414         if(!group){
21415             return;
21416         }
21417         
21418         for(var i in group){
21419             var fg = group[i].el.findParent('.form-group', false, true);
21420             if (Roo.bootstrap.version == 3) {
21421                 fg.removeClass([_this.invalidClass, _this.validClass]);
21422                 fg.addClass(_this.invalidClass);
21423             } else {
21424                 fg.removeClass(['is-invalid', 'is-valid']);
21425                 fg.addClass('is-invalid');
21426             }
21427         }
21428         
21429     },
21430     
21431     clearInvalid : function()
21432     {
21433         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21434         
21435         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21436         
21437         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21438         
21439         if (label && label.iconEl) {
21440             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
21441             label.iconEl.removeClass(['is-invalid', 'is-valid']);
21442         }
21443     },
21444     
21445     disable : function()
21446     {
21447         if(this.inputType != 'radio'){
21448             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21449             return;
21450         }
21451         
21452         var _this = this;
21453         
21454         if(this.rendered){
21455             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21456                 _this.getActionEl().addClass(this.disabledClass);
21457                 e.dom.disabled = true;
21458             });
21459         }
21460         
21461         this.disabled = true;
21462         this.fireEvent("disable", this);
21463         return this;
21464     },
21465
21466     enable : function()
21467     {
21468         if(this.inputType != 'radio'){
21469             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21470             return;
21471         }
21472         
21473         var _this = this;
21474         
21475         if(this.rendered){
21476             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21477                 _this.getActionEl().removeClass(this.disabledClass);
21478                 e.dom.disabled = false;
21479             });
21480         }
21481         
21482         this.disabled = false;
21483         this.fireEvent("enable", this);
21484         return this;
21485     },
21486     
21487     setBoxLabel : function(v)
21488     {
21489         this.boxLabel = v;
21490         
21491         if(this.rendered){
21492             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21493         }
21494     }
21495
21496 });
21497
21498 Roo.apply(Roo.bootstrap.CheckBox, {
21499     
21500     groups: {},
21501     
21502      /**
21503     * register a CheckBox Group
21504     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21505     */
21506     register : function(checkbox)
21507     {
21508         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21509             this.groups[checkbox.groupId] = {};
21510         }
21511         
21512         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21513             return;
21514         }
21515         
21516         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21517         
21518     },
21519     /**
21520     * fetch a CheckBox Group based on the group ID
21521     * @param {string} the group ID
21522     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21523     */
21524     get: function(groupId) {
21525         if (typeof(this.groups[groupId]) == 'undefined') {
21526             return false;
21527         }
21528         
21529         return this.groups[groupId] ;
21530     }
21531     
21532     
21533 });
21534 /*
21535  * - LGPL
21536  *
21537  * RadioItem
21538  * 
21539  */
21540
21541 /**
21542  * @class Roo.bootstrap.Radio
21543  * @extends Roo.bootstrap.Component
21544  * Bootstrap Radio class
21545  * @cfg {String} boxLabel - the label associated
21546  * @cfg {String} value - the value of radio
21547  * 
21548  * @constructor
21549  * Create a new Radio
21550  * @param {Object} config The config object
21551  */
21552 Roo.bootstrap.Radio = function(config){
21553     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21554     
21555 };
21556
21557 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21558     
21559     boxLabel : '',
21560     
21561     value : '',
21562     
21563     getAutoCreate : function()
21564     {
21565         var cfg = {
21566             tag : 'div',
21567             cls : 'form-group radio',
21568             cn : [
21569                 {
21570                     tag : 'label',
21571                     cls : 'box-label',
21572                     html : this.boxLabel
21573                 }
21574             ]
21575         };
21576         
21577         return cfg;
21578     },
21579     
21580     initEvents : function() 
21581     {
21582         this.parent().register(this);
21583         
21584         this.el.on('click', this.onClick, this);
21585         
21586     },
21587     
21588     onClick : function(e)
21589     {
21590         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21591             this.setChecked(true);
21592         }
21593     },
21594     
21595     setChecked : function(state, suppressEvent)
21596     {
21597         this.parent().setValue(this.value, suppressEvent);
21598         
21599     },
21600     
21601     setBoxLabel : function(v)
21602     {
21603         this.boxLabel = v;
21604         
21605         if(this.rendered){
21606             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21607         }
21608     }
21609     
21610 });
21611  
21612
21613  /*
21614  * - LGPL
21615  *
21616  * Input
21617  * 
21618  */
21619
21620 /**
21621  * @class Roo.bootstrap.SecurePass
21622  * @extends Roo.bootstrap.Input
21623  * Bootstrap SecurePass class
21624  *
21625  * 
21626  * @constructor
21627  * Create a new SecurePass
21628  * @param {Object} config The config object
21629  */
21630  
21631 Roo.bootstrap.SecurePass = function (config) {
21632     // these go here, so the translation tool can replace them..
21633     this.errors = {
21634         PwdEmpty: "Please type a password, and then retype it to confirm.",
21635         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21636         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21637         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21638         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21639         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21640         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21641         TooWeak: "Your password is Too Weak."
21642     },
21643     this.meterLabel = "Password strength:";
21644     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21645     this.meterClass = [
21646         "roo-password-meter-tooweak", 
21647         "roo-password-meter-weak", 
21648         "roo-password-meter-medium", 
21649         "roo-password-meter-strong", 
21650         "roo-password-meter-grey"
21651     ];
21652     
21653     this.errors = {};
21654     
21655     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21656 }
21657
21658 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21659     /**
21660      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21661      * {
21662      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21663      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21664      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21665      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21666      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21667      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21668      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21669      * })
21670      */
21671     // private
21672     
21673     meterWidth: 300,
21674     errorMsg :'',    
21675     errors: false,
21676     imageRoot: '/',
21677     /**
21678      * @cfg {String/Object} Label for the strength meter (defaults to
21679      * 'Password strength:')
21680      */
21681     // private
21682     meterLabel: '',
21683     /**
21684      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21685      * ['Weak', 'Medium', 'Strong'])
21686      */
21687     // private    
21688     pwdStrengths: false,    
21689     // private
21690     strength: 0,
21691     // private
21692     _lastPwd: null,
21693     // private
21694     kCapitalLetter: 0,
21695     kSmallLetter: 1,
21696     kDigit: 2,
21697     kPunctuation: 3,
21698     
21699     insecure: false,
21700     // private
21701     initEvents: function ()
21702     {
21703         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21704
21705         if (this.el.is('input[type=password]') && Roo.isSafari) {
21706             this.el.on('keydown', this.SafariOnKeyDown, this);
21707         }
21708
21709         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21710     },
21711     // private
21712     onRender: function (ct, position)
21713     {
21714         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21715         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21716         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21717
21718         this.trigger.createChild({
21719                    cn: [
21720                     {
21721                     //id: 'PwdMeter',
21722                     tag: 'div',
21723                     cls: 'roo-password-meter-grey col-xs-12',
21724                     style: {
21725                         //width: 0,
21726                         //width: this.meterWidth + 'px'                                                
21727                         }
21728                     },
21729                     {                            
21730                          cls: 'roo-password-meter-text'                          
21731                     }
21732                 ]            
21733         });
21734
21735          
21736         if (this.hideTrigger) {
21737             this.trigger.setDisplayed(false);
21738         }
21739         this.setSize(this.width || '', this.height || '');
21740     },
21741     // private
21742     onDestroy: function ()
21743     {
21744         if (this.trigger) {
21745             this.trigger.removeAllListeners();
21746             this.trigger.remove();
21747         }
21748         if (this.wrap) {
21749             this.wrap.remove();
21750         }
21751         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21752     },
21753     // private
21754     checkStrength: function ()
21755     {
21756         var pwd = this.inputEl().getValue();
21757         if (pwd == this._lastPwd) {
21758             return;
21759         }
21760
21761         var strength;
21762         if (this.ClientSideStrongPassword(pwd)) {
21763             strength = 3;
21764         } else if (this.ClientSideMediumPassword(pwd)) {
21765             strength = 2;
21766         } else if (this.ClientSideWeakPassword(pwd)) {
21767             strength = 1;
21768         } else {
21769             strength = 0;
21770         }
21771         
21772         Roo.log('strength1: ' + strength);
21773         
21774         //var pm = this.trigger.child('div/div/div').dom;
21775         var pm = this.trigger.child('div/div');
21776         pm.removeClass(this.meterClass);
21777         pm.addClass(this.meterClass[strength]);
21778                 
21779         
21780         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21781                 
21782         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21783         
21784         this._lastPwd = pwd;
21785     },
21786     reset: function ()
21787     {
21788         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21789         
21790         this._lastPwd = '';
21791         
21792         var pm = this.trigger.child('div/div');
21793         pm.removeClass(this.meterClass);
21794         pm.addClass('roo-password-meter-grey');        
21795         
21796         
21797         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21798         
21799         pt.innerHTML = '';
21800         this.inputEl().dom.type='password';
21801     },
21802     // private
21803     validateValue: function (value)
21804     {
21805         
21806         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21807             return false;
21808         }
21809         if (value.length == 0) {
21810             if (this.allowBlank) {
21811                 this.clearInvalid();
21812                 return true;
21813             }
21814
21815             this.markInvalid(this.errors.PwdEmpty);
21816             this.errorMsg = this.errors.PwdEmpty;
21817             return false;
21818         }
21819         
21820         if(this.insecure){
21821             return true;
21822         }
21823         
21824         if ('[\x21-\x7e]*'.match(value)) {
21825             this.markInvalid(this.errors.PwdBadChar);
21826             this.errorMsg = this.errors.PwdBadChar;
21827             return false;
21828         }
21829         if (value.length < 6) {
21830             this.markInvalid(this.errors.PwdShort);
21831             this.errorMsg = this.errors.PwdShort;
21832             return false;
21833         }
21834         if (value.length > 16) {
21835             this.markInvalid(this.errors.PwdLong);
21836             this.errorMsg = this.errors.PwdLong;
21837             return false;
21838         }
21839         var strength;
21840         if (this.ClientSideStrongPassword(value)) {
21841             strength = 3;
21842         } else if (this.ClientSideMediumPassword(value)) {
21843             strength = 2;
21844         } else if (this.ClientSideWeakPassword(value)) {
21845             strength = 1;
21846         } else {
21847             strength = 0;
21848         }
21849
21850         
21851         if (strength < 2) {
21852             //this.markInvalid(this.errors.TooWeak);
21853             this.errorMsg = this.errors.TooWeak;
21854             //return false;
21855         }
21856         
21857         
21858         console.log('strength2: ' + strength);
21859         
21860         //var pm = this.trigger.child('div/div/div').dom;
21861         
21862         var pm = this.trigger.child('div/div');
21863         pm.removeClass(this.meterClass);
21864         pm.addClass(this.meterClass[strength]);
21865                 
21866         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21867                 
21868         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21869         
21870         this.errorMsg = ''; 
21871         return true;
21872     },
21873     // private
21874     CharacterSetChecks: function (type)
21875     {
21876         this.type = type;
21877         this.fResult = false;
21878     },
21879     // private
21880     isctype: function (character, type)
21881     {
21882         switch (type) {  
21883             case this.kCapitalLetter:
21884                 if (character >= 'A' && character <= 'Z') {
21885                     return true;
21886                 }
21887                 break;
21888             
21889             case this.kSmallLetter:
21890                 if (character >= 'a' && character <= 'z') {
21891                     return true;
21892                 }
21893                 break;
21894             
21895             case this.kDigit:
21896                 if (character >= '0' && character <= '9') {
21897                     return true;
21898                 }
21899                 break;
21900             
21901             case this.kPunctuation:
21902                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21903                     return true;
21904                 }
21905                 break;
21906             
21907             default:
21908                 return false;
21909         }
21910
21911     },
21912     // private
21913     IsLongEnough: function (pwd, size)
21914     {
21915         return !(pwd == null || isNaN(size) || pwd.length < size);
21916     },
21917     // private
21918     SpansEnoughCharacterSets: function (word, nb)
21919     {
21920         if (!this.IsLongEnough(word, nb))
21921         {
21922             return false;
21923         }
21924
21925         var characterSetChecks = new Array(
21926             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21927             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21928         );
21929         
21930         for (var index = 0; index < word.length; ++index) {
21931             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21932                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21933                     characterSetChecks[nCharSet].fResult = true;
21934                     break;
21935                 }
21936             }
21937         }
21938
21939         var nCharSets = 0;
21940         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21941             if (characterSetChecks[nCharSet].fResult) {
21942                 ++nCharSets;
21943             }
21944         }
21945
21946         if (nCharSets < nb) {
21947             return false;
21948         }
21949         return true;
21950     },
21951     // private
21952     ClientSideStrongPassword: function (pwd)
21953     {
21954         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21955     },
21956     // private
21957     ClientSideMediumPassword: function (pwd)
21958     {
21959         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21960     },
21961     // private
21962     ClientSideWeakPassword: function (pwd)
21963     {
21964         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21965     }
21966           
21967 })//<script type="text/javascript">
21968
21969 /*
21970  * Based  Ext JS Library 1.1.1
21971  * Copyright(c) 2006-2007, Ext JS, LLC.
21972  * LGPL
21973  *
21974  */
21975  
21976 /**
21977  * @class Roo.HtmlEditorCore
21978  * @extends Roo.Component
21979  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21980  *
21981  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21982  */
21983
21984 Roo.HtmlEditorCore = function(config){
21985     
21986     
21987     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21988     
21989     
21990     this.addEvents({
21991         /**
21992          * @event initialize
21993          * Fires when the editor is fully initialized (including the iframe)
21994          * @param {Roo.HtmlEditorCore} this
21995          */
21996         initialize: true,
21997         /**
21998          * @event activate
21999          * Fires when the editor is first receives the focus. Any insertion must wait
22000          * until after this event.
22001          * @param {Roo.HtmlEditorCore} this
22002          */
22003         activate: true,
22004          /**
22005          * @event beforesync
22006          * Fires before the textarea is updated with content from the editor iframe. Return false
22007          * to cancel the sync.
22008          * @param {Roo.HtmlEditorCore} this
22009          * @param {String} html
22010          */
22011         beforesync: true,
22012          /**
22013          * @event beforepush
22014          * Fires before the iframe editor is updated with content from the textarea. Return false
22015          * to cancel the push.
22016          * @param {Roo.HtmlEditorCore} this
22017          * @param {String} html
22018          */
22019         beforepush: true,
22020          /**
22021          * @event sync
22022          * Fires when the textarea is updated with content from the editor iframe.
22023          * @param {Roo.HtmlEditorCore} this
22024          * @param {String} html
22025          */
22026         sync: true,
22027          /**
22028          * @event push
22029          * Fires when the iframe editor is updated with content from the textarea.
22030          * @param {Roo.HtmlEditorCore} this
22031          * @param {String} html
22032          */
22033         push: true,
22034         
22035         /**
22036          * @event editorevent
22037          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22038          * @param {Roo.HtmlEditorCore} this
22039          */
22040         editorevent: true
22041         
22042     });
22043     
22044     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
22045     
22046     // defaults : white / black...
22047     this.applyBlacklists();
22048     
22049     
22050     
22051 };
22052
22053
22054 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
22055
22056
22057      /**
22058      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
22059      */
22060     
22061     owner : false,
22062     
22063      /**
22064      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22065      *                        Roo.resizable.
22066      */
22067     resizable : false,
22068      /**
22069      * @cfg {Number} height (in pixels)
22070      */   
22071     height: 300,
22072    /**
22073      * @cfg {Number} width (in pixels)
22074      */   
22075     width: 500,
22076     
22077     /**
22078      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22079      * 
22080      */
22081     stylesheets: false,
22082     
22083     // id of frame..
22084     frameId: false,
22085     
22086     // private properties
22087     validationEvent : false,
22088     deferHeight: true,
22089     initialized : false,
22090     activated : false,
22091     sourceEditMode : false,
22092     onFocus : Roo.emptyFn,
22093     iframePad:3,
22094     hideMode:'offsets',
22095     
22096     clearUp: true,
22097     
22098     // blacklist + whitelisted elements..
22099     black: false,
22100     white: false,
22101      
22102     bodyCls : '',
22103
22104     /**
22105      * Protected method that will not generally be called directly. It
22106      * is called when the editor initializes the iframe with HTML contents. Override this method if you
22107      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
22108      */
22109     getDocMarkup : function(){
22110         // body styles..
22111         var st = '';
22112         
22113         // inherit styels from page...?? 
22114         if (this.stylesheets === false) {
22115             
22116             Roo.get(document.head).select('style').each(function(node) {
22117                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22118             });
22119             
22120             Roo.get(document.head).select('link').each(function(node) { 
22121                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22122             });
22123             
22124         } else if (!this.stylesheets.length) {
22125                 // simple..
22126                 st = '<style type="text/css">' +
22127                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22128                    '</style>';
22129         } else { 
22130             st = '<style type="text/css">' +
22131                     this.stylesheets +
22132                 '</style>';
22133         }
22134         
22135         st +=  '<style type="text/css">' +
22136             'IMG { cursor: pointer } ' +
22137         '</style>';
22138
22139         var cls = 'roo-htmleditor-body';
22140         
22141         if(this.bodyCls.length){
22142             cls += ' ' + this.bodyCls;
22143         }
22144         
22145         return '<html><head>' + st  +
22146             //<style type="text/css">' +
22147             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22148             //'</style>' +
22149             ' </head><body class="' +  cls + '"></body></html>';
22150     },
22151
22152     // private
22153     onRender : function(ct, position)
22154     {
22155         var _t = this;
22156         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
22157         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
22158         
22159         
22160         this.el.dom.style.border = '0 none';
22161         this.el.dom.setAttribute('tabIndex', -1);
22162         this.el.addClass('x-hidden hide');
22163         
22164         
22165         
22166         if(Roo.isIE){ // fix IE 1px bogus margin
22167             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
22168         }
22169        
22170         
22171         this.frameId = Roo.id();
22172         
22173          
22174         
22175         var iframe = this.owner.wrap.createChild({
22176             tag: 'iframe',
22177             cls: 'form-control', // bootstrap..
22178             id: this.frameId,
22179             name: this.frameId,
22180             frameBorder : 'no',
22181             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
22182         }, this.el
22183         );
22184         
22185         
22186         this.iframe = iframe.dom;
22187
22188          this.assignDocWin();
22189         
22190         this.doc.designMode = 'on';
22191        
22192         this.doc.open();
22193         this.doc.write(this.getDocMarkup());
22194         this.doc.close();
22195
22196         
22197         var task = { // must defer to wait for browser to be ready
22198             run : function(){
22199                 //console.log("run task?" + this.doc.readyState);
22200                 this.assignDocWin();
22201                 if(this.doc.body || this.doc.readyState == 'complete'){
22202                     try {
22203                         this.doc.designMode="on";
22204                     } catch (e) {
22205                         return;
22206                     }
22207                     Roo.TaskMgr.stop(task);
22208                     this.initEditor.defer(10, this);
22209                 }
22210             },
22211             interval : 10,
22212             duration: 10000,
22213             scope: this
22214         };
22215         Roo.TaskMgr.start(task);
22216
22217     },
22218
22219     // private
22220     onResize : function(w, h)
22221     {
22222          Roo.log('resize: ' +w + ',' + h );
22223         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22224         if(!this.iframe){
22225             return;
22226         }
22227         if(typeof w == 'number'){
22228             
22229             this.iframe.style.width = w + 'px';
22230         }
22231         if(typeof h == 'number'){
22232             
22233             this.iframe.style.height = h + 'px';
22234             if(this.doc){
22235                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22236             }
22237         }
22238         
22239     },
22240
22241     /**
22242      * Toggles the editor between standard and source edit mode.
22243      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22244      */
22245     toggleSourceEdit : function(sourceEditMode){
22246         
22247         this.sourceEditMode = sourceEditMode === true;
22248         
22249         if(this.sourceEditMode){
22250  
22251             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
22252             
22253         }else{
22254             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22255             //this.iframe.className = '';
22256             this.deferFocus();
22257         }
22258         //this.setSize(this.owner.wrap.getSize());
22259         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22260     },
22261
22262     
22263   
22264
22265     /**
22266      * Protected method that will not generally be called directly. If you need/want
22267      * custom HTML cleanup, this is the method you should override.
22268      * @param {String} html The HTML to be cleaned
22269      * return {String} The cleaned HTML
22270      */
22271     cleanHtml : function(html){
22272         html = String(html);
22273         if(html.length > 5){
22274             if(Roo.isSafari){ // strip safari nonsense
22275                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22276             }
22277         }
22278         if(html == '&nbsp;'){
22279             html = '';
22280         }
22281         return html;
22282     },
22283
22284     /**
22285      * HTML Editor -> Textarea
22286      * Protected method that will not generally be called directly. Syncs the contents
22287      * of the editor iframe with the textarea.
22288      */
22289     syncValue : function(){
22290         if(this.initialized){
22291             var bd = (this.doc.body || this.doc.documentElement);
22292             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22293             var html = bd.innerHTML;
22294             if(Roo.isSafari){
22295                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22296                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22297                 if(m && m[1]){
22298                     html = '<div style="'+m[0]+'">' + html + '</div>';
22299                 }
22300             }
22301             html = this.cleanHtml(html);
22302             // fix up the special chars.. normaly like back quotes in word...
22303             // however we do not want to do this with chinese..
22304             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22305                 var cc = b.charCodeAt();
22306                 if (
22307                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22308                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22309                     (cc >= 0xf900 && cc < 0xfb00 )
22310                 ) {
22311                         return b;
22312                 }
22313                 return "&#"+cc+";" 
22314             });
22315             if(this.owner.fireEvent('beforesync', this, html) !== false){
22316                 this.el.dom.value = html;
22317                 this.owner.fireEvent('sync', this, html);
22318             }
22319         }
22320     },
22321
22322     /**
22323      * Protected method that will not generally be called directly. Pushes the value of the textarea
22324      * into the iframe editor.
22325      */
22326     pushValue : function(){
22327         if(this.initialized){
22328             var v = this.el.dom.value.trim();
22329             
22330 //            if(v.length < 1){
22331 //                v = '&#160;';
22332 //            }
22333             
22334             if(this.owner.fireEvent('beforepush', this, v) !== false){
22335                 var d = (this.doc.body || this.doc.documentElement);
22336                 d.innerHTML = v;
22337                 this.cleanUpPaste();
22338                 this.el.dom.value = d.innerHTML;
22339                 this.owner.fireEvent('push', this, v);
22340             }
22341         }
22342     },
22343
22344     // private
22345     deferFocus : function(){
22346         this.focus.defer(10, this);
22347     },
22348
22349     // doc'ed in Field
22350     focus : function(){
22351         if(this.win && !this.sourceEditMode){
22352             this.win.focus();
22353         }else{
22354             this.el.focus();
22355         }
22356     },
22357     
22358     assignDocWin: function()
22359     {
22360         var iframe = this.iframe;
22361         
22362          if(Roo.isIE){
22363             this.doc = iframe.contentWindow.document;
22364             this.win = iframe.contentWindow;
22365         } else {
22366 //            if (!Roo.get(this.frameId)) {
22367 //                return;
22368 //            }
22369 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22370 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22371             
22372             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22373                 return;
22374             }
22375             
22376             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22377             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22378         }
22379     },
22380     
22381     // private
22382     initEditor : function(){
22383         //console.log("INIT EDITOR");
22384         this.assignDocWin();
22385         
22386         
22387         
22388         this.doc.designMode="on";
22389         this.doc.open();
22390         this.doc.write(this.getDocMarkup());
22391         this.doc.close();
22392         
22393         var dbody = (this.doc.body || this.doc.documentElement);
22394         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22395         // this copies styles from the containing element into thsi one..
22396         // not sure why we need all of this..
22397         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22398         
22399         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22400         //ss['background-attachment'] = 'fixed'; // w3c
22401         dbody.bgProperties = 'fixed'; // ie
22402         //Roo.DomHelper.applyStyles(dbody, ss);
22403         Roo.EventManager.on(this.doc, {
22404             //'mousedown': this.onEditorEvent,
22405             'mouseup': this.onEditorEvent,
22406             'dblclick': this.onEditorEvent,
22407             'click': this.onEditorEvent,
22408             'keyup': this.onEditorEvent,
22409             buffer:100,
22410             scope: this
22411         });
22412         if(Roo.isGecko){
22413             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22414         }
22415         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22416             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22417         }
22418         this.initialized = true;
22419
22420         this.owner.fireEvent('initialize', this);
22421         this.pushValue();
22422     },
22423
22424     // private
22425     onDestroy : function(){
22426         
22427         
22428         
22429         if(this.rendered){
22430             
22431             //for (var i =0; i < this.toolbars.length;i++) {
22432             //    // fixme - ask toolbars for heights?
22433             //    this.toolbars[i].onDestroy();
22434            // }
22435             
22436             //this.wrap.dom.innerHTML = '';
22437             //this.wrap.remove();
22438         }
22439     },
22440
22441     // private
22442     onFirstFocus : function(){
22443         
22444         this.assignDocWin();
22445         
22446         
22447         this.activated = true;
22448          
22449     
22450         if(Roo.isGecko){ // prevent silly gecko errors
22451             this.win.focus();
22452             var s = this.win.getSelection();
22453             if(!s.focusNode || s.focusNode.nodeType != 3){
22454                 var r = s.getRangeAt(0);
22455                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22456                 r.collapse(true);
22457                 this.deferFocus();
22458             }
22459             try{
22460                 this.execCmd('useCSS', true);
22461                 this.execCmd('styleWithCSS', false);
22462             }catch(e){}
22463         }
22464         this.owner.fireEvent('activate', this);
22465     },
22466
22467     // private
22468     adjustFont: function(btn){
22469         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22470         //if(Roo.isSafari){ // safari
22471         //    adjust *= 2;
22472        // }
22473         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22474         if(Roo.isSafari){ // safari
22475             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22476             v =  (v < 10) ? 10 : v;
22477             v =  (v > 48) ? 48 : v;
22478             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22479             
22480         }
22481         
22482         
22483         v = Math.max(1, v+adjust);
22484         
22485         this.execCmd('FontSize', v  );
22486     },
22487
22488     onEditorEvent : function(e)
22489     {
22490         this.owner.fireEvent('editorevent', this, e);
22491       //  this.updateToolbar();
22492         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22493     },
22494
22495     insertTag : function(tg)
22496     {
22497         // could be a bit smarter... -> wrap the current selected tRoo..
22498         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22499             
22500             range = this.createRange(this.getSelection());
22501             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22502             wrappingNode.appendChild(range.extractContents());
22503             range.insertNode(wrappingNode);
22504
22505             return;
22506             
22507             
22508             
22509         }
22510         this.execCmd("formatblock",   tg);
22511         
22512     },
22513     
22514     insertText : function(txt)
22515     {
22516         
22517         
22518         var range = this.createRange();
22519         range.deleteContents();
22520                //alert(Sender.getAttribute('label'));
22521                
22522         range.insertNode(this.doc.createTextNode(txt));
22523     } ,
22524     
22525      
22526
22527     /**
22528      * Executes a Midas editor command on the editor document and performs necessary focus and
22529      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22530      * @param {String} cmd The Midas command
22531      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22532      */
22533     relayCmd : function(cmd, value){
22534         this.win.focus();
22535         this.execCmd(cmd, value);
22536         this.owner.fireEvent('editorevent', this);
22537         //this.updateToolbar();
22538         this.owner.deferFocus();
22539     },
22540
22541     /**
22542      * Executes a Midas editor command directly on the editor document.
22543      * For visual commands, you should use {@link #relayCmd} instead.
22544      * <b>This should only be called after the editor is initialized.</b>
22545      * @param {String} cmd The Midas command
22546      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22547      */
22548     execCmd : function(cmd, value){
22549         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22550         this.syncValue();
22551     },
22552  
22553  
22554    
22555     /**
22556      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22557      * to insert tRoo.
22558      * @param {String} text | dom node.. 
22559      */
22560     insertAtCursor : function(text)
22561     {
22562         
22563         if(!this.activated){
22564             return;
22565         }
22566         /*
22567         if(Roo.isIE){
22568             this.win.focus();
22569             var r = this.doc.selection.createRange();
22570             if(r){
22571                 r.collapse(true);
22572                 r.pasteHTML(text);
22573                 this.syncValue();
22574                 this.deferFocus();
22575             
22576             }
22577             return;
22578         }
22579         */
22580         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22581             this.win.focus();
22582             
22583             
22584             // from jquery ui (MIT licenced)
22585             var range, node;
22586             var win = this.win;
22587             
22588             if (win.getSelection && win.getSelection().getRangeAt) {
22589                 range = win.getSelection().getRangeAt(0);
22590                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22591                 range.insertNode(node);
22592             } else if (win.document.selection && win.document.selection.createRange) {
22593                 // no firefox support
22594                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22595                 win.document.selection.createRange().pasteHTML(txt);
22596             } else {
22597                 // no firefox support
22598                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22599                 this.execCmd('InsertHTML', txt);
22600             } 
22601             
22602             this.syncValue();
22603             
22604             this.deferFocus();
22605         }
22606     },
22607  // private
22608     mozKeyPress : function(e){
22609         if(e.ctrlKey){
22610             var c = e.getCharCode(), cmd;
22611           
22612             if(c > 0){
22613                 c = String.fromCharCode(c).toLowerCase();
22614                 switch(c){
22615                     case 'b':
22616                         cmd = 'bold';
22617                         break;
22618                     case 'i':
22619                         cmd = 'italic';
22620                         break;
22621                     
22622                     case 'u':
22623                         cmd = 'underline';
22624                         break;
22625                     
22626                     case 'v':
22627                         this.cleanUpPaste.defer(100, this);
22628                         return;
22629                         
22630                 }
22631                 if(cmd){
22632                     this.win.focus();
22633                     this.execCmd(cmd);
22634                     this.deferFocus();
22635                     e.preventDefault();
22636                 }
22637                 
22638             }
22639         }
22640     },
22641
22642     // private
22643     fixKeys : function(){ // load time branching for fastest keydown performance
22644         if(Roo.isIE){
22645             return function(e){
22646                 var k = e.getKey(), r;
22647                 if(k == e.TAB){
22648                     e.stopEvent();
22649                     r = this.doc.selection.createRange();
22650                     if(r){
22651                         r.collapse(true);
22652                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22653                         this.deferFocus();
22654                     }
22655                     return;
22656                 }
22657                 
22658                 if(k == e.ENTER){
22659                     r = this.doc.selection.createRange();
22660                     if(r){
22661                         var target = r.parentElement();
22662                         if(!target || target.tagName.toLowerCase() != 'li'){
22663                             e.stopEvent();
22664                             r.pasteHTML('<br />');
22665                             r.collapse(false);
22666                             r.select();
22667                         }
22668                     }
22669                 }
22670                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22671                     this.cleanUpPaste.defer(100, this);
22672                     return;
22673                 }
22674                 
22675                 
22676             };
22677         }else if(Roo.isOpera){
22678             return function(e){
22679                 var k = e.getKey();
22680                 if(k == e.TAB){
22681                     e.stopEvent();
22682                     this.win.focus();
22683                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22684                     this.deferFocus();
22685                 }
22686                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22687                     this.cleanUpPaste.defer(100, this);
22688                     return;
22689                 }
22690                 
22691             };
22692         }else if(Roo.isSafari){
22693             return function(e){
22694                 var k = e.getKey();
22695                 
22696                 if(k == e.TAB){
22697                     e.stopEvent();
22698                     this.execCmd('InsertText','\t');
22699                     this.deferFocus();
22700                     return;
22701                 }
22702                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22703                     this.cleanUpPaste.defer(100, this);
22704                     return;
22705                 }
22706                 
22707              };
22708         }
22709     }(),
22710     
22711     getAllAncestors: function()
22712     {
22713         var p = this.getSelectedNode();
22714         var a = [];
22715         if (!p) {
22716             a.push(p); // push blank onto stack..
22717             p = this.getParentElement();
22718         }
22719         
22720         
22721         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22722             a.push(p);
22723             p = p.parentNode;
22724         }
22725         a.push(this.doc.body);
22726         return a;
22727     },
22728     lastSel : false,
22729     lastSelNode : false,
22730     
22731     
22732     getSelection : function() 
22733     {
22734         this.assignDocWin();
22735         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22736     },
22737     
22738     getSelectedNode: function() 
22739     {
22740         // this may only work on Gecko!!!
22741         
22742         // should we cache this!!!!
22743         
22744         
22745         
22746          
22747         var range = this.createRange(this.getSelection()).cloneRange();
22748         
22749         if (Roo.isIE) {
22750             var parent = range.parentElement();
22751             while (true) {
22752                 var testRange = range.duplicate();
22753                 testRange.moveToElementText(parent);
22754                 if (testRange.inRange(range)) {
22755                     break;
22756                 }
22757                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22758                     break;
22759                 }
22760                 parent = parent.parentElement;
22761             }
22762             return parent;
22763         }
22764         
22765         // is ancestor a text element.
22766         var ac =  range.commonAncestorContainer;
22767         if (ac.nodeType == 3) {
22768             ac = ac.parentNode;
22769         }
22770         
22771         var ar = ac.childNodes;
22772          
22773         var nodes = [];
22774         var other_nodes = [];
22775         var has_other_nodes = false;
22776         for (var i=0;i<ar.length;i++) {
22777             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22778                 continue;
22779             }
22780             // fullly contained node.
22781             
22782             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22783                 nodes.push(ar[i]);
22784                 continue;
22785             }
22786             
22787             // probably selected..
22788             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22789                 other_nodes.push(ar[i]);
22790                 continue;
22791             }
22792             // outer..
22793             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22794                 continue;
22795             }
22796             
22797             
22798             has_other_nodes = true;
22799         }
22800         if (!nodes.length && other_nodes.length) {
22801             nodes= other_nodes;
22802         }
22803         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22804             return false;
22805         }
22806         
22807         return nodes[0];
22808     },
22809     createRange: function(sel)
22810     {
22811         // this has strange effects when using with 
22812         // top toolbar - not sure if it's a great idea.
22813         //this.editor.contentWindow.focus();
22814         if (typeof sel != "undefined") {
22815             try {
22816                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22817             } catch(e) {
22818                 return this.doc.createRange();
22819             }
22820         } else {
22821             return this.doc.createRange();
22822         }
22823     },
22824     getParentElement: function()
22825     {
22826         
22827         this.assignDocWin();
22828         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22829         
22830         var range = this.createRange(sel);
22831          
22832         try {
22833             var p = range.commonAncestorContainer;
22834             while (p.nodeType == 3) { // text node
22835                 p = p.parentNode;
22836             }
22837             return p;
22838         } catch (e) {
22839             return null;
22840         }
22841     
22842     },
22843     /***
22844      *
22845      * Range intersection.. the hard stuff...
22846      *  '-1' = before
22847      *  '0' = hits..
22848      *  '1' = after.
22849      *         [ -- selected range --- ]
22850      *   [fail]                        [fail]
22851      *
22852      *    basically..
22853      *      if end is before start or  hits it. fail.
22854      *      if start is after end or hits it fail.
22855      *
22856      *   if either hits (but other is outside. - then it's not 
22857      *   
22858      *    
22859      **/
22860     
22861     
22862     // @see http://www.thismuchiknow.co.uk/?p=64.
22863     rangeIntersectsNode : function(range, node)
22864     {
22865         var nodeRange = node.ownerDocument.createRange();
22866         try {
22867             nodeRange.selectNode(node);
22868         } catch (e) {
22869             nodeRange.selectNodeContents(node);
22870         }
22871     
22872         var rangeStartRange = range.cloneRange();
22873         rangeStartRange.collapse(true);
22874     
22875         var rangeEndRange = range.cloneRange();
22876         rangeEndRange.collapse(false);
22877     
22878         var nodeStartRange = nodeRange.cloneRange();
22879         nodeStartRange.collapse(true);
22880     
22881         var nodeEndRange = nodeRange.cloneRange();
22882         nodeEndRange.collapse(false);
22883     
22884         return rangeStartRange.compareBoundaryPoints(
22885                  Range.START_TO_START, nodeEndRange) == -1 &&
22886                rangeEndRange.compareBoundaryPoints(
22887                  Range.START_TO_START, nodeStartRange) == 1;
22888         
22889          
22890     },
22891     rangeCompareNode : function(range, node)
22892     {
22893         var nodeRange = node.ownerDocument.createRange();
22894         try {
22895             nodeRange.selectNode(node);
22896         } catch (e) {
22897             nodeRange.selectNodeContents(node);
22898         }
22899         
22900         
22901         range.collapse(true);
22902     
22903         nodeRange.collapse(true);
22904      
22905         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22906         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22907          
22908         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22909         
22910         var nodeIsBefore   =  ss == 1;
22911         var nodeIsAfter    = ee == -1;
22912         
22913         if (nodeIsBefore && nodeIsAfter) {
22914             return 0; // outer
22915         }
22916         if (!nodeIsBefore && nodeIsAfter) {
22917             return 1; //right trailed.
22918         }
22919         
22920         if (nodeIsBefore && !nodeIsAfter) {
22921             return 2;  // left trailed.
22922         }
22923         // fully contined.
22924         return 3;
22925     },
22926
22927     // private? - in a new class?
22928     cleanUpPaste :  function()
22929     {
22930         // cleans up the whole document..
22931         Roo.log('cleanuppaste');
22932         
22933         this.cleanUpChildren(this.doc.body);
22934         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22935         if (clean != this.doc.body.innerHTML) {
22936             this.doc.body.innerHTML = clean;
22937         }
22938         
22939     },
22940     
22941     cleanWordChars : function(input) {// change the chars to hex code
22942         var he = Roo.HtmlEditorCore;
22943         
22944         var output = input;
22945         Roo.each(he.swapCodes, function(sw) { 
22946             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22947             
22948             output = output.replace(swapper, sw[1]);
22949         });
22950         
22951         return output;
22952     },
22953     
22954     
22955     cleanUpChildren : function (n)
22956     {
22957         if (!n.childNodes.length) {
22958             return;
22959         }
22960         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22961            this.cleanUpChild(n.childNodes[i]);
22962         }
22963     },
22964     
22965     
22966         
22967     
22968     cleanUpChild : function (node)
22969     {
22970         var ed = this;
22971         //console.log(node);
22972         if (node.nodeName == "#text") {
22973             // clean up silly Windows -- stuff?
22974             return; 
22975         }
22976         if (node.nodeName == "#comment") {
22977             node.parentNode.removeChild(node);
22978             // clean up silly Windows -- stuff?
22979             return; 
22980         }
22981         var lcname = node.tagName.toLowerCase();
22982         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22983         // whitelist of tags..
22984         
22985         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22986             // remove node.
22987             node.parentNode.removeChild(node);
22988             return;
22989             
22990         }
22991         
22992         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22993         
22994         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22995         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22996         
22997         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22998         //    remove_keep_children = true;
22999         //}
23000         
23001         if (remove_keep_children) {
23002             this.cleanUpChildren(node);
23003             // inserts everything just before this node...
23004             while (node.childNodes.length) {
23005                 var cn = node.childNodes[0];
23006                 node.removeChild(cn);
23007                 node.parentNode.insertBefore(cn, node);
23008             }
23009             node.parentNode.removeChild(node);
23010             return;
23011         }
23012         
23013         if (!node.attributes || !node.attributes.length) {
23014             this.cleanUpChildren(node);
23015             return;
23016         }
23017         
23018         function cleanAttr(n,v)
23019         {
23020             
23021             if (v.match(/^\./) || v.match(/^\//)) {
23022                 return;
23023             }
23024             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
23025                 return;
23026             }
23027             if (v.match(/^#/)) {
23028                 return;
23029             }
23030 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
23031             node.removeAttribute(n);
23032             
23033         }
23034         
23035         var cwhite = this.cwhite;
23036         var cblack = this.cblack;
23037             
23038         function cleanStyle(n,v)
23039         {
23040             if (v.match(/expression/)) { //XSS?? should we even bother..
23041                 node.removeAttribute(n);
23042                 return;
23043             }
23044             
23045             var parts = v.split(/;/);
23046             var clean = [];
23047             
23048             Roo.each(parts, function(p) {
23049                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
23050                 if (!p.length) {
23051                     return true;
23052                 }
23053                 var l = p.split(':').shift().replace(/\s+/g,'');
23054                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
23055                 
23056                 if ( cwhite.length && cblack.indexOf(l) > -1) {
23057 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23058                     //node.removeAttribute(n);
23059                     return true;
23060                 }
23061                 //Roo.log()
23062                 // only allow 'c whitelisted system attributes'
23063                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
23064 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23065                     //node.removeAttribute(n);
23066                     return true;
23067                 }
23068                 
23069                 
23070                  
23071                 
23072                 clean.push(p);
23073                 return true;
23074             });
23075             if (clean.length) { 
23076                 node.setAttribute(n, clean.join(';'));
23077             } else {
23078                 node.removeAttribute(n);
23079             }
23080             
23081         }
23082         
23083         
23084         for (var i = node.attributes.length-1; i > -1 ; i--) {
23085             var a = node.attributes[i];
23086             //console.log(a);
23087             
23088             if (a.name.toLowerCase().substr(0,2)=='on')  {
23089                 node.removeAttribute(a.name);
23090                 continue;
23091             }
23092             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
23093                 node.removeAttribute(a.name);
23094                 continue;
23095             }
23096             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
23097                 cleanAttr(a.name,a.value); // fixme..
23098                 continue;
23099             }
23100             if (a.name == 'style') {
23101                 cleanStyle(a.name,a.value);
23102                 continue;
23103             }
23104             /// clean up MS crap..
23105             // tecnically this should be a list of valid class'es..
23106             
23107             
23108             if (a.name == 'class') {
23109                 if (a.value.match(/^Mso/)) {
23110                     node.className = '';
23111                 }
23112                 
23113                 if (a.value.match(/^body$/)) {
23114                     node.className = '';
23115                 }
23116                 continue;
23117             }
23118             
23119             // style cleanup!?
23120             // class cleanup?
23121             
23122         }
23123         
23124         
23125         this.cleanUpChildren(node);
23126         
23127         
23128     },
23129     
23130     /**
23131      * Clean up MS wordisms...
23132      */
23133     cleanWord : function(node)
23134     {
23135         
23136         
23137         if (!node) {
23138             this.cleanWord(this.doc.body);
23139             return;
23140         }
23141         if (node.nodeName == "#text") {
23142             // clean up silly Windows -- stuff?
23143             return; 
23144         }
23145         if (node.nodeName == "#comment") {
23146             node.parentNode.removeChild(node);
23147             // clean up silly Windows -- stuff?
23148             return; 
23149         }
23150         
23151         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
23152             node.parentNode.removeChild(node);
23153             return;
23154         }
23155         
23156         // remove - but keep children..
23157         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
23158             while (node.childNodes.length) {
23159                 var cn = node.childNodes[0];
23160                 node.removeChild(cn);
23161                 node.parentNode.insertBefore(cn, node);
23162             }
23163             node.parentNode.removeChild(node);
23164             this.iterateChildren(node, this.cleanWord);
23165             return;
23166         }
23167         // clean styles
23168         if (node.className.length) {
23169             
23170             var cn = node.className.split(/\W+/);
23171             var cna = [];
23172             Roo.each(cn, function(cls) {
23173                 if (cls.match(/Mso[a-zA-Z]+/)) {
23174                     return;
23175                 }
23176                 cna.push(cls);
23177             });
23178             node.className = cna.length ? cna.join(' ') : '';
23179             if (!cna.length) {
23180                 node.removeAttribute("class");
23181             }
23182         }
23183         
23184         if (node.hasAttribute("lang")) {
23185             node.removeAttribute("lang");
23186         }
23187         
23188         if (node.hasAttribute("style")) {
23189             
23190             var styles = node.getAttribute("style").split(";");
23191             var nstyle = [];
23192             Roo.each(styles, function(s) {
23193                 if (!s.match(/:/)) {
23194                     return;
23195                 }
23196                 var kv = s.split(":");
23197                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
23198                     return;
23199                 }
23200                 // what ever is left... we allow.
23201                 nstyle.push(s);
23202             });
23203             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23204             if (!nstyle.length) {
23205                 node.removeAttribute('style');
23206             }
23207         }
23208         this.iterateChildren(node, this.cleanWord);
23209         
23210         
23211         
23212     },
23213     /**
23214      * iterateChildren of a Node, calling fn each time, using this as the scole..
23215      * @param {DomNode} node node to iterate children of.
23216      * @param {Function} fn method of this class to call on each item.
23217      */
23218     iterateChildren : function(node, fn)
23219     {
23220         if (!node.childNodes.length) {
23221                 return;
23222         }
23223         for (var i = node.childNodes.length-1; i > -1 ; i--) {
23224            fn.call(this, node.childNodes[i])
23225         }
23226     },
23227     
23228     
23229     /**
23230      * cleanTableWidths.
23231      *
23232      * Quite often pasting from word etc.. results in tables with column and widths.
23233      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23234      *
23235      */
23236     cleanTableWidths : function(node)
23237     {
23238          
23239          
23240         if (!node) {
23241             this.cleanTableWidths(this.doc.body);
23242             return;
23243         }
23244         
23245         // ignore list...
23246         if (node.nodeName == "#text" || node.nodeName == "#comment") {
23247             return; 
23248         }
23249         Roo.log(node.tagName);
23250         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23251             this.iterateChildren(node, this.cleanTableWidths);
23252             return;
23253         }
23254         if (node.hasAttribute('width')) {
23255             node.removeAttribute('width');
23256         }
23257         
23258          
23259         if (node.hasAttribute("style")) {
23260             // pretty basic...
23261             
23262             var styles = node.getAttribute("style").split(";");
23263             var nstyle = [];
23264             Roo.each(styles, function(s) {
23265                 if (!s.match(/:/)) {
23266                     return;
23267                 }
23268                 var kv = s.split(":");
23269                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23270                     return;
23271                 }
23272                 // what ever is left... we allow.
23273                 nstyle.push(s);
23274             });
23275             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23276             if (!nstyle.length) {
23277                 node.removeAttribute('style');
23278             }
23279         }
23280         
23281         this.iterateChildren(node, this.cleanTableWidths);
23282         
23283         
23284     },
23285     
23286     
23287     
23288     
23289     domToHTML : function(currentElement, depth, nopadtext) {
23290         
23291         depth = depth || 0;
23292         nopadtext = nopadtext || false;
23293     
23294         if (!currentElement) {
23295             return this.domToHTML(this.doc.body);
23296         }
23297         
23298         //Roo.log(currentElement);
23299         var j;
23300         var allText = false;
23301         var nodeName = currentElement.nodeName;
23302         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23303         
23304         if  (nodeName == '#text') {
23305             
23306             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23307         }
23308         
23309         
23310         var ret = '';
23311         if (nodeName != 'BODY') {
23312              
23313             var i = 0;
23314             // Prints the node tagName, such as <A>, <IMG>, etc
23315             if (tagName) {
23316                 var attr = [];
23317                 for(i = 0; i < currentElement.attributes.length;i++) {
23318                     // quoting?
23319                     var aname = currentElement.attributes.item(i).name;
23320                     if (!currentElement.attributes.item(i).value.length) {
23321                         continue;
23322                     }
23323                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23324                 }
23325                 
23326                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23327             } 
23328             else {
23329                 
23330                 // eack
23331             }
23332         } else {
23333             tagName = false;
23334         }
23335         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23336             return ret;
23337         }
23338         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23339             nopadtext = true;
23340         }
23341         
23342         
23343         // Traverse the tree
23344         i = 0;
23345         var currentElementChild = currentElement.childNodes.item(i);
23346         var allText = true;
23347         var innerHTML  = '';
23348         lastnode = '';
23349         while (currentElementChild) {
23350             // Formatting code (indent the tree so it looks nice on the screen)
23351             var nopad = nopadtext;
23352             if (lastnode == 'SPAN') {
23353                 nopad  = true;
23354             }
23355             // text
23356             if  (currentElementChild.nodeName == '#text') {
23357                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23358                 toadd = nopadtext ? toadd : toadd.trim();
23359                 if (!nopad && toadd.length > 80) {
23360                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23361                 }
23362                 innerHTML  += toadd;
23363                 
23364                 i++;
23365                 currentElementChild = currentElement.childNodes.item(i);
23366                 lastNode = '';
23367                 continue;
23368             }
23369             allText = false;
23370             
23371             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23372                 
23373             // Recursively traverse the tree structure of the child node
23374             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23375             lastnode = currentElementChild.nodeName;
23376             i++;
23377             currentElementChild=currentElement.childNodes.item(i);
23378         }
23379         
23380         ret += innerHTML;
23381         
23382         if (!allText) {
23383                 // The remaining code is mostly for formatting the tree
23384             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23385         }
23386         
23387         
23388         if (tagName) {
23389             ret+= "</"+tagName+">";
23390         }
23391         return ret;
23392         
23393     },
23394         
23395     applyBlacklists : function()
23396     {
23397         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23398         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23399         
23400         this.white = [];
23401         this.black = [];
23402         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23403             if (b.indexOf(tag) > -1) {
23404                 return;
23405             }
23406             this.white.push(tag);
23407             
23408         }, this);
23409         
23410         Roo.each(w, function(tag) {
23411             if (b.indexOf(tag) > -1) {
23412                 return;
23413             }
23414             if (this.white.indexOf(tag) > -1) {
23415                 return;
23416             }
23417             this.white.push(tag);
23418             
23419         }, this);
23420         
23421         
23422         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23423             if (w.indexOf(tag) > -1) {
23424                 return;
23425             }
23426             this.black.push(tag);
23427             
23428         }, this);
23429         
23430         Roo.each(b, function(tag) {
23431             if (w.indexOf(tag) > -1) {
23432                 return;
23433             }
23434             if (this.black.indexOf(tag) > -1) {
23435                 return;
23436             }
23437             this.black.push(tag);
23438             
23439         }, this);
23440         
23441         
23442         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23443         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23444         
23445         this.cwhite = [];
23446         this.cblack = [];
23447         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23448             if (b.indexOf(tag) > -1) {
23449                 return;
23450             }
23451             this.cwhite.push(tag);
23452             
23453         }, this);
23454         
23455         Roo.each(w, function(tag) {
23456             if (b.indexOf(tag) > -1) {
23457                 return;
23458             }
23459             if (this.cwhite.indexOf(tag) > -1) {
23460                 return;
23461             }
23462             this.cwhite.push(tag);
23463             
23464         }, this);
23465         
23466         
23467         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23468             if (w.indexOf(tag) > -1) {
23469                 return;
23470             }
23471             this.cblack.push(tag);
23472             
23473         }, this);
23474         
23475         Roo.each(b, function(tag) {
23476             if (w.indexOf(tag) > -1) {
23477                 return;
23478             }
23479             if (this.cblack.indexOf(tag) > -1) {
23480                 return;
23481             }
23482             this.cblack.push(tag);
23483             
23484         }, this);
23485     },
23486     
23487     setStylesheets : function(stylesheets)
23488     {
23489         if(typeof(stylesheets) == 'string'){
23490             Roo.get(this.iframe.contentDocument.head).createChild({
23491                 tag : 'link',
23492                 rel : 'stylesheet',
23493                 type : 'text/css',
23494                 href : stylesheets
23495             });
23496             
23497             return;
23498         }
23499         var _this = this;
23500      
23501         Roo.each(stylesheets, function(s) {
23502             if(!s.length){
23503                 return;
23504             }
23505             
23506             Roo.get(_this.iframe.contentDocument.head).createChild({
23507                 tag : 'link',
23508                 rel : 'stylesheet',
23509                 type : 'text/css',
23510                 href : s
23511             });
23512         });
23513
23514         
23515     },
23516     
23517     removeStylesheets : function()
23518     {
23519         var _this = this;
23520         
23521         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23522             s.remove();
23523         });
23524     },
23525     
23526     setStyle : function(style)
23527     {
23528         Roo.get(this.iframe.contentDocument.head).createChild({
23529             tag : 'style',
23530             type : 'text/css',
23531             html : style
23532         });
23533
23534         return;
23535     }
23536     
23537     // hide stuff that is not compatible
23538     /**
23539      * @event blur
23540      * @hide
23541      */
23542     /**
23543      * @event change
23544      * @hide
23545      */
23546     /**
23547      * @event focus
23548      * @hide
23549      */
23550     /**
23551      * @event specialkey
23552      * @hide
23553      */
23554     /**
23555      * @cfg {String} fieldClass @hide
23556      */
23557     /**
23558      * @cfg {String} focusClass @hide
23559      */
23560     /**
23561      * @cfg {String} autoCreate @hide
23562      */
23563     /**
23564      * @cfg {String} inputType @hide
23565      */
23566     /**
23567      * @cfg {String} invalidClass @hide
23568      */
23569     /**
23570      * @cfg {String} invalidText @hide
23571      */
23572     /**
23573      * @cfg {String} msgFx @hide
23574      */
23575     /**
23576      * @cfg {String} validateOnBlur @hide
23577      */
23578 });
23579
23580 Roo.HtmlEditorCore.white = [
23581         'area', 'br', 'img', 'input', 'hr', 'wbr',
23582         
23583        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23584        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23585        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23586        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23587        'table',   'ul',         'xmp', 
23588        
23589        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23590       'thead',   'tr', 
23591      
23592       'dir', 'menu', 'ol', 'ul', 'dl',
23593        
23594       'embed',  'object'
23595 ];
23596
23597
23598 Roo.HtmlEditorCore.black = [
23599     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23600         'applet', // 
23601         'base',   'basefont', 'bgsound', 'blink',  'body', 
23602         'frame',  'frameset', 'head',    'html',   'ilayer', 
23603         'iframe', 'layer',  'link',     'meta',    'object',   
23604         'script', 'style' ,'title',  'xml' // clean later..
23605 ];
23606 Roo.HtmlEditorCore.clean = [
23607     'script', 'style', 'title', 'xml'
23608 ];
23609 Roo.HtmlEditorCore.remove = [
23610     'font'
23611 ];
23612 // attributes..
23613
23614 Roo.HtmlEditorCore.ablack = [
23615     'on'
23616 ];
23617     
23618 Roo.HtmlEditorCore.aclean = [ 
23619     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23620 ];
23621
23622 // protocols..
23623 Roo.HtmlEditorCore.pwhite= [
23624         'http',  'https',  'mailto'
23625 ];
23626
23627 // white listed style attributes.
23628 Roo.HtmlEditorCore.cwhite= [
23629       //  'text-align', /// default is to allow most things..
23630       
23631          
23632 //        'font-size'//??
23633 ];
23634
23635 // black listed style attributes.
23636 Roo.HtmlEditorCore.cblack= [
23637       //  'font-size' -- this can be set by the project 
23638 ];
23639
23640
23641 Roo.HtmlEditorCore.swapCodes   =[ 
23642     [    8211, "--" ], 
23643     [    8212, "--" ], 
23644     [    8216,  "'" ],  
23645     [    8217, "'" ],  
23646     [    8220, '"' ],  
23647     [    8221, '"' ],  
23648     [    8226, "*" ],  
23649     [    8230, "..." ]
23650 ]; 
23651
23652     /*
23653  * - LGPL
23654  *
23655  * HtmlEditor
23656  * 
23657  */
23658
23659 /**
23660  * @class Roo.bootstrap.HtmlEditor
23661  * @extends Roo.bootstrap.TextArea
23662  * Bootstrap HtmlEditor class
23663
23664  * @constructor
23665  * Create a new HtmlEditor
23666  * @param {Object} config The config object
23667  */
23668
23669 Roo.bootstrap.HtmlEditor = function(config){
23670     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23671     if (!this.toolbars) {
23672         this.toolbars = [];
23673     }
23674     
23675     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23676     this.addEvents({
23677             /**
23678              * @event initialize
23679              * Fires when the editor is fully initialized (including the iframe)
23680              * @param {HtmlEditor} this
23681              */
23682             initialize: true,
23683             /**
23684              * @event activate
23685              * Fires when the editor is first receives the focus. Any insertion must wait
23686              * until after this event.
23687              * @param {HtmlEditor} this
23688              */
23689             activate: true,
23690              /**
23691              * @event beforesync
23692              * Fires before the textarea is updated with content from the editor iframe. Return false
23693              * to cancel the sync.
23694              * @param {HtmlEditor} this
23695              * @param {String} html
23696              */
23697             beforesync: true,
23698              /**
23699              * @event beforepush
23700              * Fires before the iframe editor is updated with content from the textarea. Return false
23701              * to cancel the push.
23702              * @param {HtmlEditor} this
23703              * @param {String} html
23704              */
23705             beforepush: true,
23706              /**
23707              * @event sync
23708              * Fires when the textarea is updated with content from the editor iframe.
23709              * @param {HtmlEditor} this
23710              * @param {String} html
23711              */
23712             sync: true,
23713              /**
23714              * @event push
23715              * Fires when the iframe editor is updated with content from the textarea.
23716              * @param {HtmlEditor} this
23717              * @param {String} html
23718              */
23719             push: true,
23720              /**
23721              * @event editmodechange
23722              * Fires when the editor switches edit modes
23723              * @param {HtmlEditor} this
23724              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23725              */
23726             editmodechange: true,
23727             /**
23728              * @event editorevent
23729              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23730              * @param {HtmlEditor} this
23731              */
23732             editorevent: true,
23733             /**
23734              * @event firstfocus
23735              * Fires when on first focus - needed by toolbars..
23736              * @param {HtmlEditor} this
23737              */
23738             firstfocus: true,
23739             /**
23740              * @event autosave
23741              * Auto save the htmlEditor value as a file into Events
23742              * @param {HtmlEditor} this
23743              */
23744             autosave: true,
23745             /**
23746              * @event savedpreview
23747              * preview the saved version of htmlEditor
23748              * @param {HtmlEditor} this
23749              */
23750             savedpreview: true
23751         });
23752 };
23753
23754
23755 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23756     
23757     
23758       /**
23759      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23760      */
23761     toolbars : false,
23762     
23763      /**
23764     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23765     */
23766     btns : [],
23767    
23768      /**
23769      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23770      *                        Roo.resizable.
23771      */
23772     resizable : false,
23773      /**
23774      * @cfg {Number} height (in pixels)
23775      */   
23776     height: 300,
23777    /**
23778      * @cfg {Number} width (in pixels)
23779      */   
23780     width: false,
23781     
23782     /**
23783      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23784      * 
23785      */
23786     stylesheets: false,
23787     
23788     // id of frame..
23789     frameId: false,
23790     
23791     // private properties
23792     validationEvent : false,
23793     deferHeight: true,
23794     initialized : false,
23795     activated : false,
23796     
23797     onFocus : Roo.emptyFn,
23798     iframePad:3,
23799     hideMode:'offsets',
23800     
23801     tbContainer : false,
23802     
23803     bodyCls : '',
23804     
23805     toolbarContainer :function() {
23806         return this.wrap.select('.x-html-editor-tb',true).first();
23807     },
23808
23809     /**
23810      * Protected method that will not generally be called directly. It
23811      * is called when the editor creates its toolbar. Override this method if you need to
23812      * add custom toolbar buttons.
23813      * @param {HtmlEditor} editor
23814      */
23815     createToolbar : function(){
23816         Roo.log('renewing');
23817         Roo.log("create toolbars");
23818         
23819         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23820         this.toolbars[0].render(this.toolbarContainer());
23821         
23822         return;
23823         
23824 //        if (!editor.toolbars || !editor.toolbars.length) {
23825 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23826 //        }
23827 //        
23828 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23829 //            editor.toolbars[i] = Roo.factory(
23830 //                    typeof(editor.toolbars[i]) == 'string' ?
23831 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23832 //                Roo.bootstrap.HtmlEditor);
23833 //            editor.toolbars[i].init(editor);
23834 //        }
23835     },
23836
23837      
23838     // private
23839     onRender : function(ct, position)
23840     {
23841        // Roo.log("Call onRender: " + this.xtype);
23842         var _t = this;
23843         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23844       
23845         this.wrap = this.inputEl().wrap({
23846             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23847         });
23848         
23849         this.editorcore.onRender(ct, position);
23850          
23851         if (this.resizable) {
23852             this.resizeEl = new Roo.Resizable(this.wrap, {
23853                 pinned : true,
23854                 wrap: true,
23855                 dynamic : true,
23856                 minHeight : this.height,
23857                 height: this.height,
23858                 handles : this.resizable,
23859                 width: this.width,
23860                 listeners : {
23861                     resize : function(r, w, h) {
23862                         _t.onResize(w,h); // -something
23863                     }
23864                 }
23865             });
23866             
23867         }
23868         this.createToolbar(this);
23869        
23870         
23871         if(!this.width && this.resizable){
23872             this.setSize(this.wrap.getSize());
23873         }
23874         if (this.resizeEl) {
23875             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23876             // should trigger onReize..
23877         }
23878         
23879     },
23880
23881     // private
23882     onResize : function(w, h)
23883     {
23884         Roo.log('resize: ' +w + ',' + h );
23885         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23886         var ew = false;
23887         var eh = false;
23888         
23889         if(this.inputEl() ){
23890             if(typeof w == 'number'){
23891                 var aw = w - this.wrap.getFrameWidth('lr');
23892                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23893                 ew = aw;
23894             }
23895             if(typeof h == 'number'){
23896                  var tbh = -11;  // fixme it needs to tool bar size!
23897                 for (var i =0; i < this.toolbars.length;i++) {
23898                     // fixme - ask toolbars for heights?
23899                     tbh += this.toolbars[i].el.getHeight();
23900                     //if (this.toolbars[i].footer) {
23901                     //    tbh += this.toolbars[i].footer.el.getHeight();
23902                     //}
23903                 }
23904               
23905                 
23906                 
23907                 
23908                 
23909                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23910                 ah -= 5; // knock a few pixes off for look..
23911                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23912                 var eh = ah;
23913             }
23914         }
23915         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23916         this.editorcore.onResize(ew,eh);
23917         
23918     },
23919
23920     /**
23921      * Toggles the editor between standard and source edit mode.
23922      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23923      */
23924     toggleSourceEdit : function(sourceEditMode)
23925     {
23926         this.editorcore.toggleSourceEdit(sourceEditMode);
23927         
23928         if(this.editorcore.sourceEditMode){
23929             Roo.log('editor - showing textarea');
23930             
23931 //            Roo.log('in');
23932 //            Roo.log(this.syncValue());
23933             this.syncValue();
23934             this.inputEl().removeClass(['hide', 'x-hidden']);
23935             this.inputEl().dom.removeAttribute('tabIndex');
23936             this.inputEl().focus();
23937         }else{
23938             Roo.log('editor - hiding textarea');
23939 //            Roo.log('out')
23940 //            Roo.log(this.pushValue()); 
23941             this.pushValue();
23942             
23943             this.inputEl().addClass(['hide', 'x-hidden']);
23944             this.inputEl().dom.setAttribute('tabIndex', -1);
23945             //this.deferFocus();
23946         }
23947          
23948         if(this.resizable){
23949             this.setSize(this.wrap.getSize());
23950         }
23951         
23952         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23953     },
23954  
23955     // private (for BoxComponent)
23956     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23957
23958     // private (for BoxComponent)
23959     getResizeEl : function(){
23960         return this.wrap;
23961     },
23962
23963     // private (for BoxComponent)
23964     getPositionEl : function(){
23965         return this.wrap;
23966     },
23967
23968     // private
23969     initEvents : function(){
23970         this.originalValue = this.getValue();
23971     },
23972
23973 //    /**
23974 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23975 //     * @method
23976 //     */
23977 //    markInvalid : Roo.emptyFn,
23978 //    /**
23979 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23980 //     * @method
23981 //     */
23982 //    clearInvalid : Roo.emptyFn,
23983
23984     setValue : function(v){
23985         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23986         this.editorcore.pushValue();
23987     },
23988
23989      
23990     // private
23991     deferFocus : function(){
23992         this.focus.defer(10, this);
23993     },
23994
23995     // doc'ed in Field
23996     focus : function(){
23997         this.editorcore.focus();
23998         
23999     },
24000       
24001
24002     // private
24003     onDestroy : function(){
24004         
24005         
24006         
24007         if(this.rendered){
24008             
24009             for (var i =0; i < this.toolbars.length;i++) {
24010                 // fixme - ask toolbars for heights?
24011                 this.toolbars[i].onDestroy();
24012             }
24013             
24014             this.wrap.dom.innerHTML = '';
24015             this.wrap.remove();
24016         }
24017     },
24018
24019     // private
24020     onFirstFocus : function(){
24021         //Roo.log("onFirstFocus");
24022         this.editorcore.onFirstFocus();
24023          for (var i =0; i < this.toolbars.length;i++) {
24024             this.toolbars[i].onFirstFocus();
24025         }
24026         
24027     },
24028     
24029     // private
24030     syncValue : function()
24031     {   
24032         this.editorcore.syncValue();
24033     },
24034     
24035     pushValue : function()
24036     {   
24037         this.editorcore.pushValue();
24038     }
24039      
24040     
24041     // hide stuff that is not compatible
24042     /**
24043      * @event blur
24044      * @hide
24045      */
24046     /**
24047      * @event change
24048      * @hide
24049      */
24050     /**
24051      * @event focus
24052      * @hide
24053      */
24054     /**
24055      * @event specialkey
24056      * @hide
24057      */
24058     /**
24059      * @cfg {String} fieldClass @hide
24060      */
24061     /**
24062      * @cfg {String} focusClass @hide
24063      */
24064     /**
24065      * @cfg {String} autoCreate @hide
24066      */
24067     /**
24068      * @cfg {String} inputType @hide
24069      */
24070      
24071     /**
24072      * @cfg {String} invalidText @hide
24073      */
24074     /**
24075      * @cfg {String} msgFx @hide
24076      */
24077     /**
24078      * @cfg {String} validateOnBlur @hide
24079      */
24080 });
24081  
24082     
24083    
24084    
24085    
24086       
24087 Roo.namespace('Roo.bootstrap.htmleditor');
24088 /**
24089  * @class Roo.bootstrap.HtmlEditorToolbar1
24090  * Basic Toolbar
24091  * 
24092  * @example
24093  * Usage:
24094  *
24095  new Roo.bootstrap.HtmlEditor({
24096     ....
24097     toolbars : [
24098         new Roo.bootstrap.HtmlEditorToolbar1({
24099             disable : { fonts: 1 , format: 1, ..., ... , ...],
24100             btns : [ .... ]
24101         })
24102     }
24103      
24104  * 
24105  * @cfg {Object} disable List of elements to disable..
24106  * @cfg {Array} btns List of additional buttons.
24107  * 
24108  * 
24109  * NEEDS Extra CSS? 
24110  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24111  */
24112  
24113 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
24114 {
24115     
24116     Roo.apply(this, config);
24117     
24118     // default disabled, based on 'good practice'..
24119     this.disable = this.disable || {};
24120     Roo.applyIf(this.disable, {
24121         fontSize : true,
24122         colors : true,
24123         specialElements : true
24124     });
24125     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
24126     
24127     this.editor = config.editor;
24128     this.editorcore = config.editor.editorcore;
24129     
24130     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
24131     
24132     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24133     // dont call parent... till later.
24134 }
24135 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
24136      
24137     bar : true,
24138     
24139     editor : false,
24140     editorcore : false,
24141     
24142     
24143     formats : [
24144         "p" ,  
24145         "h1","h2","h3","h4","h5","h6", 
24146         "pre", "code", 
24147         "abbr", "acronym", "address", "cite", "samp", "var",
24148         'div','span'
24149     ],
24150     
24151     onRender : function(ct, position)
24152     {
24153        // Roo.log("Call onRender: " + this.xtype);
24154         
24155        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
24156        Roo.log(this.el);
24157        this.el.dom.style.marginBottom = '0';
24158        var _this = this;
24159        var editorcore = this.editorcore;
24160        var editor= this.editor;
24161        
24162        var children = [];
24163        var btn = function(id,cmd , toggle, handler, html){
24164        
24165             var  event = toggle ? 'toggle' : 'click';
24166        
24167             var a = {
24168                 size : 'sm',
24169                 xtype: 'Button',
24170                 xns: Roo.bootstrap,
24171                 //glyphicon : id,
24172                 fa: id,
24173                 cmd : id || cmd,
24174                 enableToggle:toggle !== false,
24175                 html : html || '',
24176                 pressed : toggle ? false : null,
24177                 listeners : {}
24178             };
24179             a.listeners[toggle ? 'toggle' : 'click'] = function() {
24180                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
24181             };
24182             children.push(a);
24183             return a;
24184        }
24185        
24186     //    var cb_box = function...
24187         
24188         var style = {
24189                 xtype: 'Button',
24190                 size : 'sm',
24191                 xns: Roo.bootstrap,
24192                 fa : 'font',
24193                 //html : 'submit'
24194                 menu : {
24195                     xtype: 'Menu',
24196                     xns: Roo.bootstrap,
24197                     items:  []
24198                 }
24199         };
24200         Roo.each(this.formats, function(f) {
24201             style.menu.items.push({
24202                 xtype :'MenuItem',
24203                 xns: Roo.bootstrap,
24204                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24205                 tagname : f,
24206                 listeners : {
24207                     click : function()
24208                     {
24209                         editorcore.insertTag(this.tagname);
24210                         editor.focus();
24211                     }
24212                 }
24213                 
24214             });
24215         });
24216         children.push(style);   
24217         
24218         btn('bold',false,true);
24219         btn('italic',false,true);
24220         btn('align-left', 'justifyleft',true);
24221         btn('align-center', 'justifycenter',true);
24222         btn('align-right' , 'justifyright',true);
24223         btn('link', false, false, function(btn) {
24224             //Roo.log("create link?");
24225             var url = prompt(this.createLinkText, this.defaultLinkValue);
24226             if(url && url != 'http:/'+'/'){
24227                 this.editorcore.relayCmd('createlink', url);
24228             }
24229         }),
24230         btn('list','insertunorderedlist',true);
24231         btn('pencil', false,true, function(btn){
24232                 Roo.log(this);
24233                 this.toggleSourceEdit(btn.pressed);
24234         });
24235         
24236         if (this.editor.btns.length > 0) {
24237             for (var i = 0; i<this.editor.btns.length; i++) {
24238                 children.push(this.editor.btns[i]);
24239             }
24240         }
24241         
24242         /*
24243         var cog = {
24244                 xtype: 'Button',
24245                 size : 'sm',
24246                 xns: Roo.bootstrap,
24247                 glyphicon : 'cog',
24248                 //html : 'submit'
24249                 menu : {
24250                     xtype: 'Menu',
24251                     xns: Roo.bootstrap,
24252                     items:  []
24253                 }
24254         };
24255         
24256         cog.menu.items.push({
24257             xtype :'MenuItem',
24258             xns: Roo.bootstrap,
24259             html : Clean styles,
24260             tagname : f,
24261             listeners : {
24262                 click : function()
24263                 {
24264                     editorcore.insertTag(this.tagname);
24265                     editor.focus();
24266                 }
24267             }
24268             
24269         });
24270        */
24271         
24272          
24273        this.xtype = 'NavSimplebar';
24274         
24275         for(var i=0;i< children.length;i++) {
24276             
24277             this.buttons.add(this.addxtypeChild(children[i]));
24278             
24279         }
24280         
24281         editor.on('editorevent', this.updateToolbar, this);
24282     },
24283     onBtnClick : function(id)
24284     {
24285        this.editorcore.relayCmd(id);
24286        this.editorcore.focus();
24287     },
24288     
24289     /**
24290      * Protected method that will not generally be called directly. It triggers
24291      * a toolbar update by reading the markup state of the current selection in the editor.
24292      */
24293     updateToolbar: function(){
24294
24295         if(!this.editorcore.activated){
24296             this.editor.onFirstFocus(); // is this neeed?
24297             return;
24298         }
24299
24300         var btns = this.buttons; 
24301         var doc = this.editorcore.doc;
24302         btns.get('bold').setActive(doc.queryCommandState('bold'));
24303         btns.get('italic').setActive(doc.queryCommandState('italic'));
24304         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24305         
24306         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24307         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24308         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24309         
24310         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24311         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24312          /*
24313         
24314         var ans = this.editorcore.getAllAncestors();
24315         if (this.formatCombo) {
24316             
24317             
24318             var store = this.formatCombo.store;
24319             this.formatCombo.setValue("");
24320             for (var i =0; i < ans.length;i++) {
24321                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24322                     // select it..
24323                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24324                     break;
24325                 }
24326             }
24327         }
24328         
24329         
24330         
24331         // hides menus... - so this cant be on a menu...
24332         Roo.bootstrap.MenuMgr.hideAll();
24333         */
24334         Roo.bootstrap.MenuMgr.hideAll();
24335         //this.editorsyncValue();
24336     },
24337     onFirstFocus: function() {
24338         this.buttons.each(function(item){
24339            item.enable();
24340         });
24341     },
24342     toggleSourceEdit : function(sourceEditMode){
24343         
24344           
24345         if(sourceEditMode){
24346             Roo.log("disabling buttons");
24347            this.buttons.each( function(item){
24348                 if(item.cmd != 'pencil'){
24349                     item.disable();
24350                 }
24351             });
24352           
24353         }else{
24354             Roo.log("enabling buttons");
24355             if(this.editorcore.initialized){
24356                 this.buttons.each( function(item){
24357                     item.enable();
24358                 });
24359             }
24360             
24361         }
24362         Roo.log("calling toggole on editor");
24363         // tell the editor that it's been pressed..
24364         this.editor.toggleSourceEdit(sourceEditMode);
24365        
24366     }
24367 });
24368
24369
24370
24371
24372
24373 /**
24374  * @class Roo.bootstrap.Table.AbstractSelectionModel
24375  * @extends Roo.util.Observable
24376  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24377  * implemented by descendant classes.  This class should not be directly instantiated.
24378  * @constructor
24379  */
24380 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24381     this.locked = false;
24382     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24383 };
24384
24385
24386 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24387     /** @ignore Called by the grid automatically. Do not call directly. */
24388     init : function(grid){
24389         this.grid = grid;
24390         this.initEvents();
24391     },
24392
24393     /**
24394      * Locks the selections.
24395      */
24396     lock : function(){
24397         this.locked = true;
24398     },
24399
24400     /**
24401      * Unlocks the selections.
24402      */
24403     unlock : function(){
24404         this.locked = false;
24405     },
24406
24407     /**
24408      * Returns true if the selections are locked.
24409      * @return {Boolean}
24410      */
24411     isLocked : function(){
24412         return this.locked;
24413     }
24414 });
24415 /**
24416  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24417  * @class Roo.bootstrap.Table.RowSelectionModel
24418  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24419  * It supports multiple selections and keyboard selection/navigation. 
24420  * @constructor
24421  * @param {Object} config
24422  */
24423
24424 Roo.bootstrap.Table.RowSelectionModel = function(config){
24425     Roo.apply(this, config);
24426     this.selections = new Roo.util.MixedCollection(false, function(o){
24427         return o.id;
24428     });
24429
24430     this.last = false;
24431     this.lastActive = false;
24432
24433     this.addEvents({
24434         /**
24435              * @event selectionchange
24436              * Fires when the selection changes
24437              * @param {SelectionModel} this
24438              */
24439             "selectionchange" : true,
24440         /**
24441              * @event afterselectionchange
24442              * Fires after the selection changes (eg. by key press or clicking)
24443              * @param {SelectionModel} this
24444              */
24445             "afterselectionchange" : true,
24446         /**
24447              * @event beforerowselect
24448              * Fires when a row is selected being selected, return false to cancel.
24449              * @param {SelectionModel} this
24450              * @param {Number} rowIndex The selected index
24451              * @param {Boolean} keepExisting False if other selections will be cleared
24452              */
24453             "beforerowselect" : true,
24454         /**
24455              * @event rowselect
24456              * Fires when a row is selected.
24457              * @param {SelectionModel} this
24458              * @param {Number} rowIndex The selected index
24459              * @param {Roo.data.Record} r The record
24460              */
24461             "rowselect" : true,
24462         /**
24463              * @event rowdeselect
24464              * Fires when a row is deselected.
24465              * @param {SelectionModel} this
24466              * @param {Number} rowIndex The selected index
24467              */
24468         "rowdeselect" : true
24469     });
24470     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24471     this.locked = false;
24472  };
24473
24474 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24475     /**
24476      * @cfg {Boolean} singleSelect
24477      * True to allow selection of only one row at a time (defaults to false)
24478      */
24479     singleSelect : false,
24480
24481     // private
24482     initEvents : function()
24483     {
24484
24485         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24486         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24487         //}else{ // allow click to work like normal
24488          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24489         //}
24490         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24491         this.grid.on("rowclick", this.handleMouseDown, this);
24492         
24493         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24494             "up" : function(e){
24495                 if(!e.shiftKey){
24496                     this.selectPrevious(e.shiftKey);
24497                 }else if(this.last !== false && this.lastActive !== false){
24498                     var last = this.last;
24499                     this.selectRange(this.last,  this.lastActive-1);
24500                     this.grid.getView().focusRow(this.lastActive);
24501                     if(last !== false){
24502                         this.last = last;
24503                     }
24504                 }else{
24505                     this.selectFirstRow();
24506                 }
24507                 this.fireEvent("afterselectionchange", this);
24508             },
24509             "down" : function(e){
24510                 if(!e.shiftKey){
24511                     this.selectNext(e.shiftKey);
24512                 }else if(this.last !== false && this.lastActive !== false){
24513                     var last = this.last;
24514                     this.selectRange(this.last,  this.lastActive+1);
24515                     this.grid.getView().focusRow(this.lastActive);
24516                     if(last !== false){
24517                         this.last = last;
24518                     }
24519                 }else{
24520                     this.selectFirstRow();
24521                 }
24522                 this.fireEvent("afterselectionchange", this);
24523             },
24524             scope: this
24525         });
24526         this.grid.store.on('load', function(){
24527             this.selections.clear();
24528         },this);
24529         /*
24530         var view = this.grid.view;
24531         view.on("refresh", this.onRefresh, this);
24532         view.on("rowupdated", this.onRowUpdated, this);
24533         view.on("rowremoved", this.onRemove, this);
24534         */
24535     },
24536
24537     // private
24538     onRefresh : function()
24539     {
24540         var ds = this.grid.store, i, v = this.grid.view;
24541         var s = this.selections;
24542         s.each(function(r){
24543             if((i = ds.indexOfId(r.id)) != -1){
24544                 v.onRowSelect(i);
24545             }else{
24546                 s.remove(r);
24547             }
24548         });
24549     },
24550
24551     // private
24552     onRemove : function(v, index, r){
24553         this.selections.remove(r);
24554     },
24555
24556     // private
24557     onRowUpdated : function(v, index, r){
24558         if(this.isSelected(r)){
24559             v.onRowSelect(index);
24560         }
24561     },
24562
24563     /**
24564      * Select records.
24565      * @param {Array} records The records to select
24566      * @param {Boolean} keepExisting (optional) True to keep existing selections
24567      */
24568     selectRecords : function(records, keepExisting)
24569     {
24570         if(!keepExisting){
24571             this.clearSelections();
24572         }
24573             var ds = this.grid.store;
24574         for(var i = 0, len = records.length; i < len; i++){
24575             this.selectRow(ds.indexOf(records[i]), true);
24576         }
24577     },
24578
24579     /**
24580      * Gets the number of selected rows.
24581      * @return {Number}
24582      */
24583     getCount : function(){
24584         return this.selections.length;
24585     },
24586
24587     /**
24588      * Selects the first row in the grid.
24589      */
24590     selectFirstRow : function(){
24591         this.selectRow(0);
24592     },
24593
24594     /**
24595      * Select the last row.
24596      * @param {Boolean} keepExisting (optional) True to keep existing selections
24597      */
24598     selectLastRow : function(keepExisting){
24599         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24600         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24601     },
24602
24603     /**
24604      * Selects the row immediately following the last selected row.
24605      * @param {Boolean} keepExisting (optional) True to keep existing selections
24606      */
24607     selectNext : function(keepExisting)
24608     {
24609             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24610             this.selectRow(this.last+1, keepExisting);
24611             this.grid.getView().focusRow(this.last);
24612         }
24613     },
24614
24615     /**
24616      * Selects the row that precedes the last selected row.
24617      * @param {Boolean} keepExisting (optional) True to keep existing selections
24618      */
24619     selectPrevious : function(keepExisting){
24620         if(this.last){
24621             this.selectRow(this.last-1, keepExisting);
24622             this.grid.getView().focusRow(this.last);
24623         }
24624     },
24625
24626     /**
24627      * Returns the selected records
24628      * @return {Array} Array of selected records
24629      */
24630     getSelections : function(){
24631         return [].concat(this.selections.items);
24632     },
24633
24634     /**
24635      * Returns the first selected record.
24636      * @return {Record}
24637      */
24638     getSelected : function(){
24639         return this.selections.itemAt(0);
24640     },
24641
24642
24643     /**
24644      * Clears all selections.
24645      */
24646     clearSelections : function(fast)
24647     {
24648         if(this.locked) {
24649             return;
24650         }
24651         if(fast !== true){
24652                 var ds = this.grid.store;
24653             var s = this.selections;
24654             s.each(function(r){
24655                 this.deselectRow(ds.indexOfId(r.id));
24656             }, this);
24657             s.clear();
24658         }else{
24659             this.selections.clear();
24660         }
24661         this.last = false;
24662     },
24663
24664
24665     /**
24666      * Selects all rows.
24667      */
24668     selectAll : function(){
24669         if(this.locked) {
24670             return;
24671         }
24672         this.selections.clear();
24673         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24674             this.selectRow(i, true);
24675         }
24676     },
24677
24678     /**
24679      * Returns True if there is a selection.
24680      * @return {Boolean}
24681      */
24682     hasSelection : function(){
24683         return this.selections.length > 0;
24684     },
24685
24686     /**
24687      * Returns True if the specified row is selected.
24688      * @param {Number/Record} record The record or index of the record to check
24689      * @return {Boolean}
24690      */
24691     isSelected : function(index){
24692             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24693         return (r && this.selections.key(r.id) ? true : false);
24694     },
24695
24696     /**
24697      * Returns True if the specified record id is selected.
24698      * @param {String} id The id of record to check
24699      * @return {Boolean}
24700      */
24701     isIdSelected : function(id){
24702         return (this.selections.key(id) ? true : false);
24703     },
24704
24705
24706     // private
24707     handleMouseDBClick : function(e, t){
24708         
24709     },
24710     // private
24711     handleMouseDown : function(e, t)
24712     {
24713             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24714         if(this.isLocked() || rowIndex < 0 ){
24715             return;
24716         };
24717         if(e.shiftKey && this.last !== false){
24718             var last = this.last;
24719             this.selectRange(last, rowIndex, e.ctrlKey);
24720             this.last = last; // reset the last
24721             t.focus();
24722     
24723         }else{
24724             var isSelected = this.isSelected(rowIndex);
24725             //Roo.log("select row:" + rowIndex);
24726             if(isSelected){
24727                 this.deselectRow(rowIndex);
24728             } else {
24729                         this.selectRow(rowIndex, true);
24730             }
24731     
24732             /*
24733                 if(e.button !== 0 && isSelected){
24734                 alert('rowIndex 2: ' + rowIndex);
24735                     view.focusRow(rowIndex);
24736                 }else if(e.ctrlKey && isSelected){
24737                     this.deselectRow(rowIndex);
24738                 }else if(!isSelected){
24739                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24740                     view.focusRow(rowIndex);
24741                 }
24742             */
24743         }
24744         this.fireEvent("afterselectionchange", this);
24745     },
24746     // private
24747     handleDragableRowClick :  function(grid, rowIndex, e) 
24748     {
24749         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24750             this.selectRow(rowIndex, false);
24751             grid.view.focusRow(rowIndex);
24752              this.fireEvent("afterselectionchange", this);
24753         }
24754     },
24755     
24756     /**
24757      * Selects multiple rows.
24758      * @param {Array} rows Array of the indexes of the row to select
24759      * @param {Boolean} keepExisting (optional) True to keep existing selections
24760      */
24761     selectRows : function(rows, keepExisting){
24762         if(!keepExisting){
24763             this.clearSelections();
24764         }
24765         for(var i = 0, len = rows.length; i < len; i++){
24766             this.selectRow(rows[i], true);
24767         }
24768     },
24769
24770     /**
24771      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24772      * @param {Number} startRow The index of the first row in the range
24773      * @param {Number} endRow The index of the last row in the range
24774      * @param {Boolean} keepExisting (optional) True to retain existing selections
24775      */
24776     selectRange : function(startRow, endRow, keepExisting){
24777         if(this.locked) {
24778             return;
24779         }
24780         if(!keepExisting){
24781             this.clearSelections();
24782         }
24783         if(startRow <= endRow){
24784             for(var i = startRow; i <= endRow; i++){
24785                 this.selectRow(i, true);
24786             }
24787         }else{
24788             for(var i = startRow; i >= endRow; i--){
24789                 this.selectRow(i, true);
24790             }
24791         }
24792     },
24793
24794     /**
24795      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24796      * @param {Number} startRow The index of the first row in the range
24797      * @param {Number} endRow The index of the last row in the range
24798      */
24799     deselectRange : function(startRow, endRow, preventViewNotify){
24800         if(this.locked) {
24801             return;
24802         }
24803         for(var i = startRow; i <= endRow; i++){
24804             this.deselectRow(i, preventViewNotify);
24805         }
24806     },
24807
24808     /**
24809      * Selects a row.
24810      * @param {Number} row The index of the row to select
24811      * @param {Boolean} keepExisting (optional) True to keep existing selections
24812      */
24813     selectRow : function(index, keepExisting, preventViewNotify)
24814     {
24815             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24816             return;
24817         }
24818         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24819             if(!keepExisting || this.singleSelect){
24820                 this.clearSelections();
24821             }
24822             
24823             var r = this.grid.store.getAt(index);
24824             //console.log('selectRow - record id :' + r.id);
24825             
24826             this.selections.add(r);
24827             this.last = this.lastActive = index;
24828             if(!preventViewNotify){
24829                 var proxy = new Roo.Element(
24830                                 this.grid.getRowDom(index)
24831                 );
24832                 proxy.addClass('bg-info info');
24833             }
24834             this.fireEvent("rowselect", this, index, r);
24835             this.fireEvent("selectionchange", this);
24836         }
24837     },
24838
24839     /**
24840      * Deselects a row.
24841      * @param {Number} row The index of the row to deselect
24842      */
24843     deselectRow : function(index, preventViewNotify)
24844     {
24845         if(this.locked) {
24846             return;
24847         }
24848         if(this.last == index){
24849             this.last = false;
24850         }
24851         if(this.lastActive == index){
24852             this.lastActive = false;
24853         }
24854         
24855         var r = this.grid.store.getAt(index);
24856         if (!r) {
24857             return;
24858         }
24859         
24860         this.selections.remove(r);
24861         //.console.log('deselectRow - record id :' + r.id);
24862         if(!preventViewNotify){
24863         
24864             var proxy = new Roo.Element(
24865                 this.grid.getRowDom(index)
24866             );
24867             proxy.removeClass('bg-info info');
24868         }
24869         this.fireEvent("rowdeselect", this, index);
24870         this.fireEvent("selectionchange", this);
24871     },
24872
24873     // private
24874     restoreLast : function(){
24875         if(this._last){
24876             this.last = this._last;
24877         }
24878     },
24879
24880     // private
24881     acceptsNav : function(row, col, cm){
24882         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24883     },
24884
24885     // private
24886     onEditorKey : function(field, e){
24887         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24888         if(k == e.TAB){
24889             e.stopEvent();
24890             ed.completeEdit();
24891             if(e.shiftKey){
24892                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24893             }else{
24894                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24895             }
24896         }else if(k == e.ENTER && !e.ctrlKey){
24897             e.stopEvent();
24898             ed.completeEdit();
24899             if(e.shiftKey){
24900                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24901             }else{
24902                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24903             }
24904         }else if(k == e.ESC){
24905             ed.cancelEdit();
24906         }
24907         if(newCell){
24908             g.startEditing(newCell[0], newCell[1]);
24909         }
24910     }
24911 });
24912 /*
24913  * Based on:
24914  * Ext JS Library 1.1.1
24915  * Copyright(c) 2006-2007, Ext JS, LLC.
24916  *
24917  * Originally Released Under LGPL - original licence link has changed is not relivant.
24918  *
24919  * Fork - LGPL
24920  * <script type="text/javascript">
24921  */
24922  
24923 /**
24924  * @class Roo.bootstrap.PagingToolbar
24925  * @extends Roo.bootstrap.NavSimplebar
24926  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24927  * @constructor
24928  * Create a new PagingToolbar
24929  * @param {Object} config The config object
24930  * @param {Roo.data.Store} store
24931  */
24932 Roo.bootstrap.PagingToolbar = function(config)
24933 {
24934     // old args format still supported... - xtype is prefered..
24935         // created from xtype...
24936     
24937     this.ds = config.dataSource;
24938     
24939     if (config.store && !this.ds) {
24940         this.store= Roo.factory(config.store, Roo.data);
24941         this.ds = this.store;
24942         this.ds.xmodule = this.xmodule || false;
24943     }
24944     
24945     this.toolbarItems = [];
24946     if (config.items) {
24947         this.toolbarItems = config.items;
24948     }
24949     
24950     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24951     
24952     this.cursor = 0;
24953     
24954     if (this.ds) { 
24955         this.bind(this.ds);
24956     }
24957     
24958     if (Roo.bootstrap.version == 4) {
24959         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24960     } else {
24961         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24962     }
24963     
24964 };
24965
24966 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24967     /**
24968      * @cfg {Roo.data.Store} dataSource
24969      * The underlying data store providing the paged data
24970      */
24971     /**
24972      * @cfg {String/HTMLElement/Element} container
24973      * container The id or element that will contain the toolbar
24974      */
24975     /**
24976      * @cfg {Boolean} displayInfo
24977      * True to display the displayMsg (defaults to false)
24978      */
24979     /**
24980      * @cfg {Number} pageSize
24981      * The number of records to display per page (defaults to 20)
24982      */
24983     pageSize: 20,
24984     /**
24985      * @cfg {String} displayMsg
24986      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24987      */
24988     displayMsg : 'Displaying {0} - {1} of {2}',
24989     /**
24990      * @cfg {String} emptyMsg
24991      * The message to display when no records are found (defaults to "No data to display")
24992      */
24993     emptyMsg : 'No data to display',
24994     /**
24995      * Customizable piece of the default paging text (defaults to "Page")
24996      * @type String
24997      */
24998     beforePageText : "Page",
24999     /**
25000      * Customizable piece of the default paging text (defaults to "of %0")
25001      * @type String
25002      */
25003     afterPageText : "of {0}",
25004     /**
25005      * Customizable piece of the default paging text (defaults to "First Page")
25006      * @type String
25007      */
25008     firstText : "First Page",
25009     /**
25010      * Customizable piece of the default paging text (defaults to "Previous Page")
25011      * @type String
25012      */
25013     prevText : "Previous Page",
25014     /**
25015      * Customizable piece of the default paging text (defaults to "Next Page")
25016      * @type String
25017      */
25018     nextText : "Next Page",
25019     /**
25020      * Customizable piece of the default paging text (defaults to "Last Page")
25021      * @type String
25022      */
25023     lastText : "Last Page",
25024     /**
25025      * Customizable piece of the default paging text (defaults to "Refresh")
25026      * @type String
25027      */
25028     refreshText : "Refresh",
25029
25030     buttons : false,
25031     // private
25032     onRender : function(ct, position) 
25033     {
25034         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
25035         this.navgroup.parentId = this.id;
25036         this.navgroup.onRender(this.el, null);
25037         // add the buttons to the navgroup
25038         
25039         if(this.displayInfo){
25040             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
25041             this.displayEl = this.el.select('.x-paging-info', true).first();
25042 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
25043 //            this.displayEl = navel.el.select('span',true).first();
25044         }
25045         
25046         var _this = this;
25047         
25048         if(this.buttons){
25049             Roo.each(_this.buttons, function(e){ // this might need to use render????
25050                Roo.factory(e).render(_this.el);
25051             });
25052         }
25053             
25054         Roo.each(_this.toolbarItems, function(e) {
25055             _this.navgroup.addItem(e);
25056         });
25057         
25058         
25059         this.first = this.navgroup.addItem({
25060             tooltip: this.firstText,
25061             cls: "prev btn-outline-secondary",
25062             html : ' <i class="fa fa-step-backward"></i>',
25063             disabled: true,
25064             preventDefault: true,
25065             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
25066         });
25067         
25068         this.prev =  this.navgroup.addItem({
25069             tooltip: this.prevText,
25070             cls: "prev btn-outline-secondary",
25071             html : ' <i class="fa fa-backward"></i>',
25072             disabled: true,
25073             preventDefault: true,
25074             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
25075         });
25076     //this.addSeparator();
25077         
25078         
25079         var field = this.navgroup.addItem( {
25080             tagtype : 'span',
25081             cls : 'x-paging-position  btn-outline-secondary',
25082              disabled: true,
25083             html : this.beforePageText  +
25084                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
25085                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
25086          } ); //?? escaped?
25087         
25088         this.field = field.el.select('input', true).first();
25089         this.field.on("keydown", this.onPagingKeydown, this);
25090         this.field.on("focus", function(){this.dom.select();});
25091     
25092     
25093         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
25094         //this.field.setHeight(18);
25095         //this.addSeparator();
25096         this.next = this.navgroup.addItem({
25097             tooltip: this.nextText,
25098             cls: "next btn-outline-secondary",
25099             html : ' <i class="fa fa-forward"></i>',
25100             disabled: true,
25101             preventDefault: true,
25102             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
25103         });
25104         this.last = this.navgroup.addItem({
25105             tooltip: this.lastText,
25106             html : ' <i class="fa fa-step-forward"></i>',
25107             cls: "next btn-outline-secondary",
25108             disabled: true,
25109             preventDefault: true,
25110             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
25111         });
25112     //this.addSeparator();
25113         this.loading = this.navgroup.addItem({
25114             tooltip: this.refreshText,
25115             cls: "btn-outline-secondary",
25116             html : ' <i class="fa fa-refresh"></i>',
25117             preventDefault: true,
25118             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
25119         });
25120         
25121     },
25122
25123     // private
25124     updateInfo : function(){
25125         if(this.displayEl){
25126             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
25127             var msg = count == 0 ?
25128                 this.emptyMsg :
25129                 String.format(
25130                     this.displayMsg,
25131                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
25132                 );
25133             this.displayEl.update(msg);
25134         }
25135     },
25136
25137     // private
25138     onLoad : function(ds, r, o)
25139     {
25140         this.cursor = o.params.start ? o.params.start : 0;
25141         
25142         var d = this.getPageData(),
25143             ap = d.activePage,
25144             ps = d.pages;
25145         
25146         
25147         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
25148         this.field.dom.value = ap;
25149         this.first.setDisabled(ap == 1);
25150         this.prev.setDisabled(ap == 1);
25151         this.next.setDisabled(ap == ps);
25152         this.last.setDisabled(ap == ps);
25153         this.loading.enable();
25154         this.updateInfo();
25155     },
25156
25157     // private
25158     getPageData : function(){
25159         var total = this.ds.getTotalCount();
25160         return {
25161             total : total,
25162             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
25163             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
25164         };
25165     },
25166
25167     // private
25168     onLoadError : function(){
25169         this.loading.enable();
25170     },
25171
25172     // private
25173     onPagingKeydown : function(e){
25174         var k = e.getKey();
25175         var d = this.getPageData();
25176         if(k == e.RETURN){
25177             var v = this.field.dom.value, pageNum;
25178             if(!v || isNaN(pageNum = parseInt(v, 10))){
25179                 this.field.dom.value = d.activePage;
25180                 return;
25181             }
25182             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
25183             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25184             e.stopEvent();
25185         }
25186         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))
25187         {
25188           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
25189           this.field.dom.value = pageNum;
25190           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
25191           e.stopEvent();
25192         }
25193         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
25194         {
25195           var v = this.field.dom.value, pageNum; 
25196           var increment = (e.shiftKey) ? 10 : 1;
25197           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
25198                 increment *= -1;
25199           }
25200           if(!v || isNaN(pageNum = parseInt(v, 10))) {
25201             this.field.dom.value = d.activePage;
25202             return;
25203           }
25204           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25205           {
25206             this.field.dom.value = parseInt(v, 10) + increment;
25207             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25208             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25209           }
25210           e.stopEvent();
25211         }
25212     },
25213
25214     // private
25215     beforeLoad : function(){
25216         if(this.loading){
25217             this.loading.disable();
25218         }
25219     },
25220
25221     // private
25222     onClick : function(which){
25223         
25224         var ds = this.ds;
25225         if (!ds) {
25226             return;
25227         }
25228         
25229         switch(which){
25230             case "first":
25231                 ds.load({params:{start: 0, limit: this.pageSize}});
25232             break;
25233             case "prev":
25234                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25235             break;
25236             case "next":
25237                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25238             break;
25239             case "last":
25240                 var total = ds.getTotalCount();
25241                 var extra = total % this.pageSize;
25242                 var lastStart = extra ? (total - extra) : total-this.pageSize;
25243                 ds.load({params:{start: lastStart, limit: this.pageSize}});
25244             break;
25245             case "refresh":
25246                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25247             break;
25248         }
25249     },
25250
25251     /**
25252      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25253      * @param {Roo.data.Store} store The data store to unbind
25254      */
25255     unbind : function(ds){
25256         ds.un("beforeload", this.beforeLoad, this);
25257         ds.un("load", this.onLoad, this);
25258         ds.un("loadexception", this.onLoadError, this);
25259         ds.un("remove", this.updateInfo, this);
25260         ds.un("add", this.updateInfo, this);
25261         this.ds = undefined;
25262     },
25263
25264     /**
25265      * Binds the paging toolbar to the specified {@link Roo.data.Store}
25266      * @param {Roo.data.Store} store The data store to bind
25267      */
25268     bind : function(ds){
25269         ds.on("beforeload", this.beforeLoad, this);
25270         ds.on("load", this.onLoad, this);
25271         ds.on("loadexception", this.onLoadError, this);
25272         ds.on("remove", this.updateInfo, this);
25273         ds.on("add", this.updateInfo, this);
25274         this.ds = ds;
25275     }
25276 });/*
25277  * - LGPL
25278  *
25279  * element
25280  * 
25281  */
25282
25283 /**
25284  * @class Roo.bootstrap.MessageBar
25285  * @extends Roo.bootstrap.Component
25286  * Bootstrap MessageBar class
25287  * @cfg {String} html contents of the MessageBar
25288  * @cfg {String} weight (info | success | warning | danger) default info
25289  * @cfg {String} beforeClass insert the bar before the given class
25290  * @cfg {Boolean} closable (true | false) default false
25291  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25292  * 
25293  * @constructor
25294  * Create a new Element
25295  * @param {Object} config The config object
25296  */
25297
25298 Roo.bootstrap.MessageBar = function(config){
25299     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25300 };
25301
25302 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25303     
25304     html: '',
25305     weight: 'info',
25306     closable: false,
25307     fixed: false,
25308     beforeClass: 'bootstrap-sticky-wrap',
25309     
25310     getAutoCreate : function(){
25311         
25312         var cfg = {
25313             tag: 'div',
25314             cls: 'alert alert-dismissable alert-' + this.weight,
25315             cn: [
25316                 {
25317                     tag: 'span',
25318                     cls: 'message',
25319                     html: this.html || ''
25320                 }
25321             ]
25322         };
25323         
25324         if(this.fixed){
25325             cfg.cls += ' alert-messages-fixed';
25326         }
25327         
25328         if(this.closable){
25329             cfg.cn.push({
25330                 tag: 'button',
25331                 cls: 'close',
25332                 html: 'x'
25333             });
25334         }
25335         
25336         return cfg;
25337     },
25338     
25339     onRender : function(ct, position)
25340     {
25341         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25342         
25343         if(!this.el){
25344             var cfg = Roo.apply({},  this.getAutoCreate());
25345             cfg.id = Roo.id();
25346             
25347             if (this.cls) {
25348                 cfg.cls += ' ' + this.cls;
25349             }
25350             if (this.style) {
25351                 cfg.style = this.style;
25352             }
25353             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25354             
25355             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25356         }
25357         
25358         this.el.select('>button.close').on('click', this.hide, this);
25359         
25360     },
25361     
25362     show : function()
25363     {
25364         if (!this.rendered) {
25365             this.render();
25366         }
25367         
25368         this.el.show();
25369         
25370         this.fireEvent('show', this);
25371         
25372     },
25373     
25374     hide : function()
25375     {
25376         if (!this.rendered) {
25377             this.render();
25378         }
25379         
25380         this.el.hide();
25381         
25382         this.fireEvent('hide', this);
25383     },
25384     
25385     update : function()
25386     {
25387 //        var e = this.el.dom.firstChild;
25388 //        
25389 //        if(this.closable){
25390 //            e = e.nextSibling;
25391 //        }
25392 //        
25393 //        e.data = this.html || '';
25394
25395         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25396     }
25397    
25398 });
25399
25400  
25401
25402      /*
25403  * - LGPL
25404  *
25405  * Graph
25406  * 
25407  */
25408
25409
25410 /**
25411  * @class Roo.bootstrap.Graph
25412  * @extends Roo.bootstrap.Component
25413  * Bootstrap Graph class
25414 > Prameters
25415  -sm {number} sm 4
25416  -md {number} md 5
25417  @cfg {String} graphtype  bar | vbar | pie
25418  @cfg {number} g_x coodinator | centre x (pie)
25419  @cfg {number} g_y coodinator | centre y (pie)
25420  @cfg {number} g_r radius (pie)
25421  @cfg {number} g_height height of the chart (respected by all elements in the set)
25422  @cfg {number} g_width width of the chart (respected by all elements in the set)
25423  @cfg {Object} title The title of the chart
25424     
25425  -{Array}  values
25426  -opts (object) options for the chart 
25427      o {
25428      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25429      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25430      o vgutter (number)
25431      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.
25432      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25433      o to
25434      o stretch (boolean)
25435      o }
25436  -opts (object) options for the pie
25437      o{
25438      o cut
25439      o startAngle (number)
25440      o endAngle (number)
25441      } 
25442  *
25443  * @constructor
25444  * Create a new Input
25445  * @param {Object} config The config object
25446  */
25447
25448 Roo.bootstrap.Graph = function(config){
25449     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25450     
25451     this.addEvents({
25452         // img events
25453         /**
25454          * @event click
25455          * The img click event for the img.
25456          * @param {Roo.EventObject} e
25457          */
25458         "click" : true
25459     });
25460 };
25461
25462 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25463     
25464     sm: 4,
25465     md: 5,
25466     graphtype: 'bar',
25467     g_height: 250,
25468     g_width: 400,
25469     g_x: 50,
25470     g_y: 50,
25471     g_r: 30,
25472     opts:{
25473         //g_colors: this.colors,
25474         g_type: 'soft',
25475         g_gutter: '20%'
25476
25477     },
25478     title : false,
25479
25480     getAutoCreate : function(){
25481         
25482         var cfg = {
25483             tag: 'div',
25484             html : null
25485         };
25486         
25487         
25488         return  cfg;
25489     },
25490
25491     onRender : function(ct,position){
25492         
25493         
25494         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25495         
25496         if (typeof(Raphael) == 'undefined') {
25497             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25498             return;
25499         }
25500         
25501         this.raphael = Raphael(this.el.dom);
25502         
25503                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25504                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25505                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25506                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25507                 /*
25508                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25509                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25510                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25511                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25512                 
25513                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25514                 r.barchart(330, 10, 300, 220, data1);
25515                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25516                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25517                 */
25518                 
25519                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25520                 // r.barchart(30, 30, 560, 250,  xdata, {
25521                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25522                 //     axis : "0 0 1 1",
25523                 //     axisxlabels :  xdata
25524                 //     //yvalues : cols,
25525                    
25526                 // });
25527 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25528 //        
25529 //        this.load(null,xdata,{
25530 //                axis : "0 0 1 1",
25531 //                axisxlabels :  xdata
25532 //                });
25533
25534     },
25535
25536     load : function(graphtype,xdata,opts)
25537     {
25538         this.raphael.clear();
25539         if(!graphtype) {
25540             graphtype = this.graphtype;
25541         }
25542         if(!opts){
25543             opts = this.opts;
25544         }
25545         var r = this.raphael,
25546             fin = function () {
25547                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25548             },
25549             fout = function () {
25550                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25551             },
25552             pfin = function() {
25553                 this.sector.stop();
25554                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25555
25556                 if (this.label) {
25557                     this.label[0].stop();
25558                     this.label[0].attr({ r: 7.5 });
25559                     this.label[1].attr({ "font-weight": 800 });
25560                 }
25561             },
25562             pfout = function() {
25563                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25564
25565                 if (this.label) {
25566                     this.label[0].animate({ r: 5 }, 500, "bounce");
25567                     this.label[1].attr({ "font-weight": 400 });
25568                 }
25569             };
25570
25571         switch(graphtype){
25572             case 'bar':
25573                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25574                 break;
25575             case 'hbar':
25576                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25577                 break;
25578             case 'pie':
25579 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25580 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25581 //            
25582                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25583                 
25584                 break;
25585
25586         }
25587         
25588         if(this.title){
25589             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25590         }
25591         
25592     },
25593     
25594     setTitle: function(o)
25595     {
25596         this.title = o;
25597     },
25598     
25599     initEvents: function() {
25600         
25601         if(!this.href){
25602             this.el.on('click', this.onClick, this);
25603         }
25604     },
25605     
25606     onClick : function(e)
25607     {
25608         Roo.log('img onclick');
25609         this.fireEvent('click', this, e);
25610     }
25611    
25612 });
25613
25614  
25615 /*
25616  * - LGPL
25617  *
25618  * numberBox
25619  * 
25620  */
25621 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25622
25623 /**
25624  * @class Roo.bootstrap.dash.NumberBox
25625  * @extends Roo.bootstrap.Component
25626  * Bootstrap NumberBox class
25627  * @cfg {String} headline Box headline
25628  * @cfg {String} content Box content
25629  * @cfg {String} icon Box icon
25630  * @cfg {String} footer Footer text
25631  * @cfg {String} fhref Footer href
25632  * 
25633  * @constructor
25634  * Create a new NumberBox
25635  * @param {Object} config The config object
25636  */
25637
25638
25639 Roo.bootstrap.dash.NumberBox = function(config){
25640     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25641     
25642 };
25643
25644 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25645     
25646     headline : '',
25647     content : '',
25648     icon : '',
25649     footer : '',
25650     fhref : '',
25651     ficon : '',
25652     
25653     getAutoCreate : function(){
25654         
25655         var cfg = {
25656             tag : 'div',
25657             cls : 'small-box ',
25658             cn : [
25659                 {
25660                     tag : 'div',
25661                     cls : 'inner',
25662                     cn :[
25663                         {
25664                             tag : 'h3',
25665                             cls : 'roo-headline',
25666                             html : this.headline
25667                         },
25668                         {
25669                             tag : 'p',
25670                             cls : 'roo-content',
25671                             html : this.content
25672                         }
25673                     ]
25674                 }
25675             ]
25676         };
25677         
25678         if(this.icon){
25679             cfg.cn.push({
25680                 tag : 'div',
25681                 cls : 'icon',
25682                 cn :[
25683                     {
25684                         tag : 'i',
25685                         cls : 'ion ' + this.icon
25686                     }
25687                 ]
25688             });
25689         }
25690         
25691         if(this.footer){
25692             var footer = {
25693                 tag : 'a',
25694                 cls : 'small-box-footer',
25695                 href : this.fhref || '#',
25696                 html : this.footer
25697             };
25698             
25699             cfg.cn.push(footer);
25700             
25701         }
25702         
25703         return  cfg;
25704     },
25705
25706     onRender : function(ct,position){
25707         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25708
25709
25710        
25711                 
25712     },
25713
25714     setHeadline: function (value)
25715     {
25716         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25717     },
25718     
25719     setFooter: function (value, href)
25720     {
25721         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25722         
25723         if(href){
25724             this.el.select('a.small-box-footer',true).first().attr('href', href);
25725         }
25726         
25727     },
25728
25729     setContent: function (value)
25730     {
25731         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25732     },
25733
25734     initEvents: function() 
25735     {   
25736         
25737     }
25738     
25739 });
25740
25741  
25742 /*
25743  * - LGPL
25744  *
25745  * TabBox
25746  * 
25747  */
25748 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25749
25750 /**
25751  * @class Roo.bootstrap.dash.TabBox
25752  * @extends Roo.bootstrap.Component
25753  * Bootstrap TabBox class
25754  * @cfg {String} title Title of the TabBox
25755  * @cfg {String} icon Icon of the TabBox
25756  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25757  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25758  * 
25759  * @constructor
25760  * Create a new TabBox
25761  * @param {Object} config The config object
25762  */
25763
25764
25765 Roo.bootstrap.dash.TabBox = function(config){
25766     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25767     this.addEvents({
25768         // raw events
25769         /**
25770          * @event addpane
25771          * When a pane is added
25772          * @param {Roo.bootstrap.dash.TabPane} pane
25773          */
25774         "addpane" : true,
25775         /**
25776          * @event activatepane
25777          * When a pane is activated
25778          * @param {Roo.bootstrap.dash.TabPane} pane
25779          */
25780         "activatepane" : true
25781         
25782          
25783     });
25784     
25785     this.panes = [];
25786 };
25787
25788 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25789
25790     title : '',
25791     icon : false,
25792     showtabs : true,
25793     tabScrollable : false,
25794     
25795     getChildContainer : function()
25796     {
25797         return this.el.select('.tab-content', true).first();
25798     },
25799     
25800     getAutoCreate : function(){
25801         
25802         var header = {
25803             tag: 'li',
25804             cls: 'pull-left header',
25805             html: this.title,
25806             cn : []
25807         };
25808         
25809         if(this.icon){
25810             header.cn.push({
25811                 tag: 'i',
25812                 cls: 'fa ' + this.icon
25813             });
25814         }
25815         
25816         var h = {
25817             tag: 'ul',
25818             cls: 'nav nav-tabs pull-right',
25819             cn: [
25820                 header
25821             ]
25822         };
25823         
25824         if(this.tabScrollable){
25825             h = {
25826                 tag: 'div',
25827                 cls: 'tab-header',
25828                 cn: [
25829                     {
25830                         tag: 'ul',
25831                         cls: 'nav nav-tabs pull-right',
25832                         cn: [
25833                             header
25834                         ]
25835                     }
25836                 ]
25837             };
25838         }
25839         
25840         var cfg = {
25841             tag: 'div',
25842             cls: 'nav-tabs-custom',
25843             cn: [
25844                 h,
25845                 {
25846                     tag: 'div',
25847                     cls: 'tab-content no-padding',
25848                     cn: []
25849                 }
25850             ]
25851         };
25852
25853         return  cfg;
25854     },
25855     initEvents : function()
25856     {
25857         //Roo.log('add add pane handler');
25858         this.on('addpane', this.onAddPane, this);
25859     },
25860      /**
25861      * Updates the box title
25862      * @param {String} html to set the title to.
25863      */
25864     setTitle : function(value)
25865     {
25866         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25867     },
25868     onAddPane : function(pane)
25869     {
25870         this.panes.push(pane);
25871         //Roo.log('addpane');
25872         //Roo.log(pane);
25873         // tabs are rendere left to right..
25874         if(!this.showtabs){
25875             return;
25876         }
25877         
25878         var ctr = this.el.select('.nav-tabs', true).first();
25879          
25880          
25881         var existing = ctr.select('.nav-tab',true);
25882         var qty = existing.getCount();;
25883         
25884         
25885         var tab = ctr.createChild({
25886             tag : 'li',
25887             cls : 'nav-tab' + (qty ? '' : ' active'),
25888             cn : [
25889                 {
25890                     tag : 'a',
25891                     href:'#',
25892                     html : pane.title
25893                 }
25894             ]
25895         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25896         pane.tab = tab;
25897         
25898         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25899         if (!qty) {
25900             pane.el.addClass('active');
25901         }
25902         
25903                 
25904     },
25905     onTabClick : function(ev,un,ob,pane)
25906     {
25907         //Roo.log('tab - prev default');
25908         ev.preventDefault();
25909         
25910         
25911         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25912         pane.tab.addClass('active');
25913         //Roo.log(pane.title);
25914         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25915         // technically we should have a deactivate event.. but maybe add later.
25916         // and it should not de-activate the selected tab...
25917         this.fireEvent('activatepane', pane);
25918         pane.el.addClass('active');
25919         pane.fireEvent('activate');
25920         
25921         
25922     },
25923     
25924     getActivePane : function()
25925     {
25926         var r = false;
25927         Roo.each(this.panes, function(p) {
25928             if(p.el.hasClass('active')){
25929                 r = p;
25930                 return false;
25931             }
25932             
25933             return;
25934         });
25935         
25936         return r;
25937     }
25938     
25939     
25940 });
25941
25942  
25943 /*
25944  * - LGPL
25945  *
25946  * Tab pane
25947  * 
25948  */
25949 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25950 /**
25951  * @class Roo.bootstrap.TabPane
25952  * @extends Roo.bootstrap.Component
25953  * Bootstrap TabPane class
25954  * @cfg {Boolean} active (false | true) Default false
25955  * @cfg {String} title title of panel
25956
25957  * 
25958  * @constructor
25959  * Create a new TabPane
25960  * @param {Object} config The config object
25961  */
25962
25963 Roo.bootstrap.dash.TabPane = function(config){
25964     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25965     
25966     this.addEvents({
25967         // raw events
25968         /**
25969          * @event activate
25970          * When a pane is activated
25971          * @param {Roo.bootstrap.dash.TabPane} pane
25972          */
25973         "activate" : true
25974          
25975     });
25976 };
25977
25978 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25979     
25980     active : false,
25981     title : '',
25982     
25983     // the tabBox that this is attached to.
25984     tab : false,
25985      
25986     getAutoCreate : function() 
25987     {
25988         var cfg = {
25989             tag: 'div',
25990             cls: 'tab-pane'
25991         };
25992         
25993         if(this.active){
25994             cfg.cls += ' active';
25995         }
25996         
25997         return cfg;
25998     },
25999     initEvents  : function()
26000     {
26001         //Roo.log('trigger add pane handler');
26002         this.parent().fireEvent('addpane', this)
26003     },
26004     
26005      /**
26006      * Updates the tab title 
26007      * @param {String} html to set the title to.
26008      */
26009     setTitle: function(str)
26010     {
26011         if (!this.tab) {
26012             return;
26013         }
26014         this.title = str;
26015         this.tab.select('a', true).first().dom.innerHTML = str;
26016         
26017     }
26018     
26019     
26020     
26021 });
26022
26023  
26024
26025
26026  /*
26027  * - LGPL
26028  *
26029  * menu
26030  * 
26031  */
26032 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26033
26034 /**
26035  * @class Roo.bootstrap.menu.Menu
26036  * @extends Roo.bootstrap.Component
26037  * Bootstrap Menu class - container for Menu
26038  * @cfg {String} html Text of the menu
26039  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
26040  * @cfg {String} icon Font awesome icon
26041  * @cfg {String} pos Menu align to (top | bottom) default bottom
26042  * 
26043  * 
26044  * @constructor
26045  * Create a new Menu
26046  * @param {Object} config The config object
26047  */
26048
26049
26050 Roo.bootstrap.menu.Menu = function(config){
26051     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
26052     
26053     this.addEvents({
26054         /**
26055          * @event beforeshow
26056          * Fires before this menu is displayed
26057          * @param {Roo.bootstrap.menu.Menu} this
26058          */
26059         beforeshow : true,
26060         /**
26061          * @event beforehide
26062          * Fires before this menu is hidden
26063          * @param {Roo.bootstrap.menu.Menu} this
26064          */
26065         beforehide : true,
26066         /**
26067          * @event show
26068          * Fires after this menu is displayed
26069          * @param {Roo.bootstrap.menu.Menu} this
26070          */
26071         show : true,
26072         /**
26073          * @event hide
26074          * Fires after this menu is hidden
26075          * @param {Roo.bootstrap.menu.Menu} this
26076          */
26077         hide : true,
26078         /**
26079          * @event click
26080          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
26081          * @param {Roo.bootstrap.menu.Menu} this
26082          * @param {Roo.EventObject} e
26083          */
26084         click : true
26085     });
26086     
26087 };
26088
26089 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
26090     
26091     submenu : false,
26092     html : '',
26093     weight : 'default',
26094     icon : false,
26095     pos : 'bottom',
26096     
26097     
26098     getChildContainer : function() {
26099         if(this.isSubMenu){
26100             return this.el;
26101         }
26102         
26103         return this.el.select('ul.dropdown-menu', true).first();  
26104     },
26105     
26106     getAutoCreate : function()
26107     {
26108         var text = [
26109             {
26110                 tag : 'span',
26111                 cls : 'roo-menu-text',
26112                 html : this.html
26113             }
26114         ];
26115         
26116         if(this.icon){
26117             text.unshift({
26118                 tag : 'i',
26119                 cls : 'fa ' + this.icon
26120             })
26121         }
26122         
26123         
26124         var cfg = {
26125             tag : 'div',
26126             cls : 'btn-group',
26127             cn : [
26128                 {
26129                     tag : 'button',
26130                     cls : 'dropdown-button btn btn-' + this.weight,
26131                     cn : text
26132                 },
26133                 {
26134                     tag : 'button',
26135                     cls : 'dropdown-toggle btn btn-' + this.weight,
26136                     cn : [
26137                         {
26138                             tag : 'span',
26139                             cls : 'caret'
26140                         }
26141                     ]
26142                 },
26143                 {
26144                     tag : 'ul',
26145                     cls : 'dropdown-menu'
26146                 }
26147             ]
26148             
26149         };
26150         
26151         if(this.pos == 'top'){
26152             cfg.cls += ' dropup';
26153         }
26154         
26155         if(this.isSubMenu){
26156             cfg = {
26157                 tag : 'ul',
26158                 cls : 'dropdown-menu'
26159             }
26160         }
26161         
26162         return cfg;
26163     },
26164     
26165     onRender : function(ct, position)
26166     {
26167         this.isSubMenu = ct.hasClass('dropdown-submenu');
26168         
26169         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
26170     },
26171     
26172     initEvents : function() 
26173     {
26174         if(this.isSubMenu){
26175             return;
26176         }
26177         
26178         this.hidden = true;
26179         
26180         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
26181         this.triggerEl.on('click', this.onTriggerPress, this);
26182         
26183         this.buttonEl = this.el.select('button.dropdown-button', true).first();
26184         this.buttonEl.on('click', this.onClick, this);
26185         
26186     },
26187     
26188     list : function()
26189     {
26190         if(this.isSubMenu){
26191             return this.el;
26192         }
26193         
26194         return this.el.select('ul.dropdown-menu', true).first();
26195     },
26196     
26197     onClick : function(e)
26198     {
26199         this.fireEvent("click", this, e);
26200     },
26201     
26202     onTriggerPress  : function(e)
26203     {   
26204         if (this.isVisible()) {
26205             this.hide();
26206         } else {
26207             this.show();
26208         }
26209     },
26210     
26211     isVisible : function(){
26212         return !this.hidden;
26213     },
26214     
26215     show : function()
26216     {
26217         this.fireEvent("beforeshow", this);
26218         
26219         this.hidden = false;
26220         this.el.addClass('open');
26221         
26222         Roo.get(document).on("mouseup", this.onMouseUp, this);
26223         
26224         this.fireEvent("show", this);
26225         
26226         
26227     },
26228     
26229     hide : function()
26230     {
26231         this.fireEvent("beforehide", this);
26232         
26233         this.hidden = true;
26234         this.el.removeClass('open');
26235         
26236         Roo.get(document).un("mouseup", this.onMouseUp);
26237         
26238         this.fireEvent("hide", this);
26239     },
26240     
26241     onMouseUp : function()
26242     {
26243         this.hide();
26244     }
26245     
26246 });
26247
26248  
26249  /*
26250  * - LGPL
26251  *
26252  * menu item
26253  * 
26254  */
26255 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26256
26257 /**
26258  * @class Roo.bootstrap.menu.Item
26259  * @extends Roo.bootstrap.Component
26260  * Bootstrap MenuItem class
26261  * @cfg {Boolean} submenu (true | false) default false
26262  * @cfg {String} html text of the item
26263  * @cfg {String} href the link
26264  * @cfg {Boolean} disable (true | false) default false
26265  * @cfg {Boolean} preventDefault (true | false) default true
26266  * @cfg {String} icon Font awesome icon
26267  * @cfg {String} pos Submenu align to (left | right) default right 
26268  * 
26269  * 
26270  * @constructor
26271  * Create a new Item
26272  * @param {Object} config The config object
26273  */
26274
26275
26276 Roo.bootstrap.menu.Item = function(config){
26277     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26278     this.addEvents({
26279         /**
26280          * @event mouseover
26281          * Fires when the mouse is hovering over this menu
26282          * @param {Roo.bootstrap.menu.Item} this
26283          * @param {Roo.EventObject} e
26284          */
26285         mouseover : true,
26286         /**
26287          * @event mouseout
26288          * Fires when the mouse exits this menu
26289          * @param {Roo.bootstrap.menu.Item} this
26290          * @param {Roo.EventObject} e
26291          */
26292         mouseout : true,
26293         // raw events
26294         /**
26295          * @event click
26296          * The raw click event for the entire grid.
26297          * @param {Roo.EventObject} e
26298          */
26299         click : true
26300     });
26301 };
26302
26303 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26304     
26305     submenu : false,
26306     href : '',
26307     html : '',
26308     preventDefault: true,
26309     disable : false,
26310     icon : false,
26311     pos : 'right',
26312     
26313     getAutoCreate : function()
26314     {
26315         var text = [
26316             {
26317                 tag : 'span',
26318                 cls : 'roo-menu-item-text',
26319                 html : this.html
26320             }
26321         ];
26322         
26323         if(this.icon){
26324             text.unshift({
26325                 tag : 'i',
26326                 cls : 'fa ' + this.icon
26327             })
26328         }
26329         
26330         var cfg = {
26331             tag : 'li',
26332             cn : [
26333                 {
26334                     tag : 'a',
26335                     href : this.href || '#',
26336                     cn : text
26337                 }
26338             ]
26339         };
26340         
26341         if(this.disable){
26342             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26343         }
26344         
26345         if(this.submenu){
26346             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26347             
26348             if(this.pos == 'left'){
26349                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26350             }
26351         }
26352         
26353         return cfg;
26354     },
26355     
26356     initEvents : function() 
26357     {
26358         this.el.on('mouseover', this.onMouseOver, this);
26359         this.el.on('mouseout', this.onMouseOut, this);
26360         
26361         this.el.select('a', true).first().on('click', this.onClick, this);
26362         
26363     },
26364     
26365     onClick : function(e)
26366     {
26367         if(this.preventDefault){
26368             e.preventDefault();
26369         }
26370         
26371         this.fireEvent("click", this, e);
26372     },
26373     
26374     onMouseOver : function(e)
26375     {
26376         if(this.submenu && this.pos == 'left'){
26377             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26378         }
26379         
26380         this.fireEvent("mouseover", this, e);
26381     },
26382     
26383     onMouseOut : function(e)
26384     {
26385         this.fireEvent("mouseout", this, e);
26386     }
26387 });
26388
26389  
26390
26391  /*
26392  * - LGPL
26393  *
26394  * menu separator
26395  * 
26396  */
26397 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26398
26399 /**
26400  * @class Roo.bootstrap.menu.Separator
26401  * @extends Roo.bootstrap.Component
26402  * Bootstrap Separator class
26403  * 
26404  * @constructor
26405  * Create a new Separator
26406  * @param {Object} config The config object
26407  */
26408
26409
26410 Roo.bootstrap.menu.Separator = function(config){
26411     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26412 };
26413
26414 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26415     
26416     getAutoCreate : function(){
26417         var cfg = {
26418             tag : 'li',
26419             cls: 'divider'
26420         };
26421         
26422         return cfg;
26423     }
26424    
26425 });
26426
26427  
26428
26429  /*
26430  * - LGPL
26431  *
26432  * Tooltip
26433  * 
26434  */
26435
26436 /**
26437  * @class Roo.bootstrap.Tooltip
26438  * Bootstrap Tooltip class
26439  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26440  * to determine which dom element triggers the tooltip.
26441  * 
26442  * It needs to add support for additional attributes like tooltip-position
26443  * 
26444  * @constructor
26445  * Create a new Toolti
26446  * @param {Object} config The config object
26447  */
26448
26449 Roo.bootstrap.Tooltip = function(config){
26450     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26451     
26452     this.alignment = Roo.bootstrap.Tooltip.alignment;
26453     
26454     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26455         this.alignment = config.alignment;
26456     }
26457     
26458 };
26459
26460 Roo.apply(Roo.bootstrap.Tooltip, {
26461     /**
26462      * @function init initialize tooltip monitoring.
26463      * @static
26464      */
26465     currentEl : false,
26466     currentTip : false,
26467     currentRegion : false,
26468     
26469     //  init : delay?
26470     
26471     init : function()
26472     {
26473         Roo.get(document).on('mouseover', this.enter ,this);
26474         Roo.get(document).on('mouseout', this.leave, this);
26475          
26476         
26477         this.currentTip = new Roo.bootstrap.Tooltip();
26478     },
26479     
26480     enter : function(ev)
26481     {
26482         var dom = ev.getTarget();
26483         
26484         //Roo.log(['enter',dom]);
26485         var el = Roo.fly(dom);
26486         if (this.currentEl) {
26487             //Roo.log(dom);
26488             //Roo.log(this.currentEl);
26489             //Roo.log(this.currentEl.contains(dom));
26490             if (this.currentEl == el) {
26491                 return;
26492             }
26493             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26494                 return;
26495             }
26496
26497         }
26498         
26499         if (this.currentTip.el) {
26500             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26501         }    
26502         //Roo.log(ev);
26503         
26504         if(!el || el.dom == document){
26505             return;
26506         }
26507         
26508         var bindEl = el;
26509         
26510         // you can not look for children, as if el is the body.. then everythign is the child..
26511         if (!el.attr('tooltip')) { //
26512             if (!el.select("[tooltip]").elements.length) {
26513                 return;
26514             }
26515             // is the mouse over this child...?
26516             bindEl = el.select("[tooltip]").first();
26517             var xy = ev.getXY();
26518             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26519                 //Roo.log("not in region.");
26520                 return;
26521             }
26522             //Roo.log("child element over..");
26523             
26524         }
26525         this.currentEl = bindEl;
26526         this.currentTip.bind(bindEl);
26527         this.currentRegion = Roo.lib.Region.getRegion(dom);
26528         this.currentTip.enter();
26529         
26530     },
26531     leave : function(ev)
26532     {
26533         var dom = ev.getTarget();
26534         //Roo.log(['leave',dom]);
26535         if (!this.currentEl) {
26536             return;
26537         }
26538         
26539         
26540         if (dom != this.currentEl.dom) {
26541             return;
26542         }
26543         var xy = ev.getXY();
26544         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26545             return;
26546         }
26547         // only activate leave if mouse cursor is outside... bounding box..
26548         
26549         
26550         
26551         
26552         if (this.currentTip) {
26553             this.currentTip.leave();
26554         }
26555         //Roo.log('clear currentEl');
26556         this.currentEl = false;
26557         
26558         
26559     },
26560     alignment : {
26561         'left' : ['r-l', [-2,0], 'right'],
26562         'right' : ['l-r', [2,0], 'left'],
26563         'bottom' : ['t-b', [0,2], 'top'],
26564         'top' : [ 'b-t', [0,-2], 'bottom']
26565     }
26566     
26567 });
26568
26569
26570 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26571     
26572     
26573     bindEl : false,
26574     
26575     delay : null, // can be { show : 300 , hide: 500}
26576     
26577     timeout : null,
26578     
26579     hoverState : null, //???
26580     
26581     placement : 'bottom', 
26582     
26583     alignment : false,
26584     
26585     getAutoCreate : function(){
26586     
26587         var cfg = {
26588            cls : 'tooltip',
26589            role : 'tooltip',
26590            cn : [
26591                 {
26592                     cls : 'tooltip-arrow'
26593                 },
26594                 {
26595                     cls : 'tooltip-inner'
26596                 }
26597            ]
26598         };
26599         
26600         return cfg;
26601     },
26602     bind : function(el)
26603     {
26604         this.bindEl = el;
26605     },
26606       
26607     
26608     enter : function () {
26609        
26610         if (this.timeout != null) {
26611             clearTimeout(this.timeout);
26612         }
26613         
26614         this.hoverState = 'in';
26615          //Roo.log("enter - show");
26616         if (!this.delay || !this.delay.show) {
26617             this.show();
26618             return;
26619         }
26620         var _t = this;
26621         this.timeout = setTimeout(function () {
26622             if (_t.hoverState == 'in') {
26623                 _t.show();
26624             }
26625         }, this.delay.show);
26626     },
26627     leave : function()
26628     {
26629         clearTimeout(this.timeout);
26630     
26631         this.hoverState = 'out';
26632          if (!this.delay || !this.delay.hide) {
26633             this.hide();
26634             return;
26635         }
26636        
26637         var _t = this;
26638         this.timeout = setTimeout(function () {
26639             //Roo.log("leave - timeout");
26640             
26641             if (_t.hoverState == 'out') {
26642                 _t.hide();
26643                 Roo.bootstrap.Tooltip.currentEl = false;
26644             }
26645         }, delay);
26646     },
26647     
26648     show : function (msg)
26649     {
26650         if (!this.el) {
26651             this.render(document.body);
26652         }
26653         // set content.
26654         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26655         
26656         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26657         
26658         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26659         
26660         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26661         
26662         var placement = typeof this.placement == 'function' ?
26663             this.placement.call(this, this.el, on_el) :
26664             this.placement;
26665             
26666         var autoToken = /\s?auto?\s?/i;
26667         var autoPlace = autoToken.test(placement);
26668         if (autoPlace) {
26669             placement = placement.replace(autoToken, '') || 'top';
26670         }
26671         
26672         //this.el.detach()
26673         //this.el.setXY([0,0]);
26674         this.el.show();
26675         //this.el.dom.style.display='block';
26676         
26677         //this.el.appendTo(on_el);
26678         
26679         var p = this.getPosition();
26680         var box = this.el.getBox();
26681         
26682         if (autoPlace) {
26683             // fixme..
26684         }
26685         
26686         var align = this.alignment[placement];
26687         
26688         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26689         
26690         if(placement == 'top' || placement == 'bottom'){
26691             if(xy[0] < 0){
26692                 placement = 'right';
26693             }
26694             
26695             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26696                 placement = 'left';
26697             }
26698             
26699             var scroll = Roo.select('body', true).first().getScroll();
26700             
26701             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26702                 placement = 'top';
26703             }
26704             
26705             align = this.alignment[placement];
26706         }
26707         
26708         this.el.alignTo(this.bindEl, align[0],align[1]);
26709         //var arrow = this.el.select('.arrow',true).first();
26710         //arrow.set(align[2], 
26711         
26712         this.el.addClass(placement);
26713         
26714         this.el.addClass('in fade');
26715         
26716         this.hoverState = null;
26717         
26718         if (this.el.hasClass('fade')) {
26719             // fade it?
26720         }
26721         
26722     },
26723     hide : function()
26724     {
26725          
26726         if (!this.el) {
26727             return;
26728         }
26729         //this.el.setXY([0,0]);
26730         this.el.removeClass('in');
26731         //this.el.hide();
26732         
26733     }
26734     
26735 });
26736  
26737
26738  /*
26739  * - LGPL
26740  *
26741  * Location Picker
26742  * 
26743  */
26744
26745 /**
26746  * @class Roo.bootstrap.LocationPicker
26747  * @extends Roo.bootstrap.Component
26748  * Bootstrap LocationPicker class
26749  * @cfg {Number} latitude Position when init default 0
26750  * @cfg {Number} longitude Position when init default 0
26751  * @cfg {Number} zoom default 15
26752  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26753  * @cfg {Boolean} mapTypeControl default false
26754  * @cfg {Boolean} disableDoubleClickZoom default false
26755  * @cfg {Boolean} scrollwheel default true
26756  * @cfg {Boolean} streetViewControl default false
26757  * @cfg {Number} radius default 0
26758  * @cfg {String} locationName
26759  * @cfg {Boolean} draggable default true
26760  * @cfg {Boolean} enableAutocomplete default false
26761  * @cfg {Boolean} enableReverseGeocode default true
26762  * @cfg {String} markerTitle
26763  * 
26764  * @constructor
26765  * Create a new LocationPicker
26766  * @param {Object} config The config object
26767  */
26768
26769
26770 Roo.bootstrap.LocationPicker = function(config){
26771     
26772     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26773     
26774     this.addEvents({
26775         /**
26776          * @event initial
26777          * Fires when the picker initialized.
26778          * @param {Roo.bootstrap.LocationPicker} this
26779          * @param {Google Location} location
26780          */
26781         initial : true,
26782         /**
26783          * @event positionchanged
26784          * Fires when the picker position changed.
26785          * @param {Roo.bootstrap.LocationPicker} this
26786          * @param {Google Location} location
26787          */
26788         positionchanged : true,
26789         /**
26790          * @event resize
26791          * Fires when the map resize.
26792          * @param {Roo.bootstrap.LocationPicker} this
26793          */
26794         resize : true,
26795         /**
26796          * @event show
26797          * Fires when the map show.
26798          * @param {Roo.bootstrap.LocationPicker} this
26799          */
26800         show : true,
26801         /**
26802          * @event hide
26803          * Fires when the map hide.
26804          * @param {Roo.bootstrap.LocationPicker} this
26805          */
26806         hide : true,
26807         /**
26808          * @event mapClick
26809          * Fires when click the map.
26810          * @param {Roo.bootstrap.LocationPicker} this
26811          * @param {Map event} e
26812          */
26813         mapClick : true,
26814         /**
26815          * @event mapRightClick
26816          * Fires when right click the map.
26817          * @param {Roo.bootstrap.LocationPicker} this
26818          * @param {Map event} e
26819          */
26820         mapRightClick : true,
26821         /**
26822          * @event markerClick
26823          * Fires when click the marker.
26824          * @param {Roo.bootstrap.LocationPicker} this
26825          * @param {Map event} e
26826          */
26827         markerClick : true,
26828         /**
26829          * @event markerRightClick
26830          * Fires when right click the marker.
26831          * @param {Roo.bootstrap.LocationPicker} this
26832          * @param {Map event} e
26833          */
26834         markerRightClick : true,
26835         /**
26836          * @event OverlayViewDraw
26837          * Fires when OverlayView Draw
26838          * @param {Roo.bootstrap.LocationPicker} this
26839          */
26840         OverlayViewDraw : true,
26841         /**
26842          * @event OverlayViewOnAdd
26843          * Fires when OverlayView Draw
26844          * @param {Roo.bootstrap.LocationPicker} this
26845          */
26846         OverlayViewOnAdd : true,
26847         /**
26848          * @event OverlayViewOnRemove
26849          * Fires when OverlayView Draw
26850          * @param {Roo.bootstrap.LocationPicker} this
26851          */
26852         OverlayViewOnRemove : true,
26853         /**
26854          * @event OverlayViewShow
26855          * Fires when OverlayView Draw
26856          * @param {Roo.bootstrap.LocationPicker} this
26857          * @param {Pixel} cpx
26858          */
26859         OverlayViewShow : true,
26860         /**
26861          * @event OverlayViewHide
26862          * Fires when OverlayView Draw
26863          * @param {Roo.bootstrap.LocationPicker} this
26864          */
26865         OverlayViewHide : true,
26866         /**
26867          * @event loadexception
26868          * Fires when load google lib failed.
26869          * @param {Roo.bootstrap.LocationPicker} this
26870          */
26871         loadexception : true
26872     });
26873         
26874 };
26875
26876 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26877     
26878     gMapContext: false,
26879     
26880     latitude: 0,
26881     longitude: 0,
26882     zoom: 15,
26883     mapTypeId: false,
26884     mapTypeControl: false,
26885     disableDoubleClickZoom: false,
26886     scrollwheel: true,
26887     streetViewControl: false,
26888     radius: 0,
26889     locationName: '',
26890     draggable: true,
26891     enableAutocomplete: false,
26892     enableReverseGeocode: true,
26893     markerTitle: '',
26894     
26895     getAutoCreate: function()
26896     {
26897
26898         var cfg = {
26899             tag: 'div',
26900             cls: 'roo-location-picker'
26901         };
26902         
26903         return cfg
26904     },
26905     
26906     initEvents: function(ct, position)
26907     {       
26908         if(!this.el.getWidth() || this.isApplied()){
26909             return;
26910         }
26911         
26912         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26913         
26914         this.initial();
26915     },
26916     
26917     initial: function()
26918     {
26919         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26920             this.fireEvent('loadexception', this);
26921             return;
26922         }
26923         
26924         if(!this.mapTypeId){
26925             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26926         }
26927         
26928         this.gMapContext = this.GMapContext();
26929         
26930         this.initOverlayView();
26931         
26932         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26933         
26934         var _this = this;
26935                 
26936         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26937             _this.setPosition(_this.gMapContext.marker.position);
26938         });
26939         
26940         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26941             _this.fireEvent('mapClick', this, event);
26942             
26943         });
26944
26945         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26946             _this.fireEvent('mapRightClick', this, event);
26947             
26948         });
26949         
26950         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26951             _this.fireEvent('markerClick', this, event);
26952             
26953         });
26954
26955         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26956             _this.fireEvent('markerRightClick', this, event);
26957             
26958         });
26959         
26960         this.setPosition(this.gMapContext.location);
26961         
26962         this.fireEvent('initial', this, this.gMapContext.location);
26963     },
26964     
26965     initOverlayView: function()
26966     {
26967         var _this = this;
26968         
26969         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26970             
26971             draw: function()
26972             {
26973                 _this.fireEvent('OverlayViewDraw', _this);
26974             },
26975             
26976             onAdd: function()
26977             {
26978                 _this.fireEvent('OverlayViewOnAdd', _this);
26979             },
26980             
26981             onRemove: function()
26982             {
26983                 _this.fireEvent('OverlayViewOnRemove', _this);
26984             },
26985             
26986             show: function(cpx)
26987             {
26988                 _this.fireEvent('OverlayViewShow', _this, cpx);
26989             },
26990             
26991             hide: function()
26992             {
26993                 _this.fireEvent('OverlayViewHide', _this);
26994             }
26995             
26996         });
26997     },
26998     
26999     fromLatLngToContainerPixel: function(event)
27000     {
27001         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
27002     },
27003     
27004     isApplied: function() 
27005     {
27006         return this.getGmapContext() == false ? false : true;
27007     },
27008     
27009     getGmapContext: function() 
27010     {
27011         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
27012     },
27013     
27014     GMapContext: function() 
27015     {
27016         var position = new google.maps.LatLng(this.latitude, this.longitude);
27017         
27018         var _map = new google.maps.Map(this.el.dom, {
27019             center: position,
27020             zoom: this.zoom,
27021             mapTypeId: this.mapTypeId,
27022             mapTypeControl: this.mapTypeControl,
27023             disableDoubleClickZoom: this.disableDoubleClickZoom,
27024             scrollwheel: this.scrollwheel,
27025             streetViewControl: this.streetViewControl,
27026             locationName: this.locationName,
27027             draggable: this.draggable,
27028             enableAutocomplete: this.enableAutocomplete,
27029             enableReverseGeocode: this.enableReverseGeocode
27030         });
27031         
27032         var _marker = new google.maps.Marker({
27033             position: position,
27034             map: _map,
27035             title: this.markerTitle,
27036             draggable: this.draggable
27037         });
27038         
27039         return {
27040             map: _map,
27041             marker: _marker,
27042             circle: null,
27043             location: position,
27044             radius: this.radius,
27045             locationName: this.locationName,
27046             addressComponents: {
27047                 formatted_address: null,
27048                 addressLine1: null,
27049                 addressLine2: null,
27050                 streetName: null,
27051                 streetNumber: null,
27052                 city: null,
27053                 district: null,
27054                 state: null,
27055                 stateOrProvince: null
27056             },
27057             settings: this,
27058             domContainer: this.el.dom,
27059             geodecoder: new google.maps.Geocoder()
27060         };
27061     },
27062     
27063     drawCircle: function(center, radius, options) 
27064     {
27065         if (this.gMapContext.circle != null) {
27066             this.gMapContext.circle.setMap(null);
27067         }
27068         if (radius > 0) {
27069             radius *= 1;
27070             options = Roo.apply({}, options, {
27071                 strokeColor: "#0000FF",
27072                 strokeOpacity: .35,
27073                 strokeWeight: 2,
27074                 fillColor: "#0000FF",
27075                 fillOpacity: .2
27076             });
27077             
27078             options.map = this.gMapContext.map;
27079             options.radius = radius;
27080             options.center = center;
27081             this.gMapContext.circle = new google.maps.Circle(options);
27082             return this.gMapContext.circle;
27083         }
27084         
27085         return null;
27086     },
27087     
27088     setPosition: function(location) 
27089     {
27090         this.gMapContext.location = location;
27091         this.gMapContext.marker.setPosition(location);
27092         this.gMapContext.map.panTo(location);
27093         this.drawCircle(location, this.gMapContext.radius, {});
27094         
27095         var _this = this;
27096         
27097         if (this.gMapContext.settings.enableReverseGeocode) {
27098             this.gMapContext.geodecoder.geocode({
27099                 latLng: this.gMapContext.location
27100             }, function(results, status) {
27101                 
27102                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
27103                     _this.gMapContext.locationName = results[0].formatted_address;
27104                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
27105                     
27106                     _this.fireEvent('positionchanged', this, location);
27107                 }
27108             });
27109             
27110             return;
27111         }
27112         
27113         this.fireEvent('positionchanged', this, location);
27114     },
27115     
27116     resize: function()
27117     {
27118         google.maps.event.trigger(this.gMapContext.map, "resize");
27119         
27120         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
27121         
27122         this.fireEvent('resize', this);
27123     },
27124     
27125     setPositionByLatLng: function(latitude, longitude)
27126     {
27127         this.setPosition(new google.maps.LatLng(latitude, longitude));
27128     },
27129     
27130     getCurrentPosition: function() 
27131     {
27132         return {
27133             latitude: this.gMapContext.location.lat(),
27134             longitude: this.gMapContext.location.lng()
27135         };
27136     },
27137     
27138     getAddressName: function() 
27139     {
27140         return this.gMapContext.locationName;
27141     },
27142     
27143     getAddressComponents: function() 
27144     {
27145         return this.gMapContext.addressComponents;
27146     },
27147     
27148     address_component_from_google_geocode: function(address_components) 
27149     {
27150         var result = {};
27151         
27152         for (var i = 0; i < address_components.length; i++) {
27153             var component = address_components[i];
27154             if (component.types.indexOf("postal_code") >= 0) {
27155                 result.postalCode = component.short_name;
27156             } else if (component.types.indexOf("street_number") >= 0) {
27157                 result.streetNumber = component.short_name;
27158             } else if (component.types.indexOf("route") >= 0) {
27159                 result.streetName = component.short_name;
27160             } else if (component.types.indexOf("neighborhood") >= 0) {
27161                 result.city = component.short_name;
27162             } else if (component.types.indexOf("locality") >= 0) {
27163                 result.city = component.short_name;
27164             } else if (component.types.indexOf("sublocality") >= 0) {
27165                 result.district = component.short_name;
27166             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
27167                 result.stateOrProvince = component.short_name;
27168             } else if (component.types.indexOf("country") >= 0) {
27169                 result.country = component.short_name;
27170             }
27171         }
27172         
27173         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
27174         result.addressLine2 = "";
27175         return result;
27176     },
27177     
27178     setZoomLevel: function(zoom)
27179     {
27180         this.gMapContext.map.setZoom(zoom);
27181     },
27182     
27183     show: function()
27184     {
27185         if(!this.el){
27186             return;
27187         }
27188         
27189         this.el.show();
27190         
27191         this.resize();
27192         
27193         this.fireEvent('show', this);
27194     },
27195     
27196     hide: function()
27197     {
27198         if(!this.el){
27199             return;
27200         }
27201         
27202         this.el.hide();
27203         
27204         this.fireEvent('hide', this);
27205     }
27206     
27207 });
27208
27209 Roo.apply(Roo.bootstrap.LocationPicker, {
27210     
27211     OverlayView : function(map, options)
27212     {
27213         options = options || {};
27214         
27215         this.setMap(map);
27216     }
27217     
27218     
27219 });/**
27220  * @class Roo.bootstrap.Alert
27221  * @extends Roo.bootstrap.Component
27222  * Bootstrap Alert class - shows an alert area box
27223  * eg
27224  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
27225   Enter a valid email address
27226 </div>
27227  * @licence LGPL
27228  * @cfg {String} title The title of alert
27229  * @cfg {String} html The content of alert
27230  * @cfg {String} weight (  success | info | warning | danger )
27231  * @cfg {String} faicon font-awesomeicon
27232  * 
27233  * @constructor
27234  * Create a new alert
27235  * @param {Object} config The config object
27236  */
27237
27238
27239 Roo.bootstrap.Alert = function(config){
27240     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27241     
27242 };
27243
27244 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
27245     
27246     title: '',
27247     html: '',
27248     weight: false,
27249     faicon: false,
27250     
27251     getAutoCreate : function()
27252     {
27253         
27254         var cfg = {
27255             tag : 'div',
27256             cls : 'alert',
27257             cn : [
27258                 {
27259                     tag : 'i',
27260                     cls : 'roo-alert-icon'
27261                     
27262                 },
27263                 {
27264                     tag : 'b',
27265                     cls : 'roo-alert-title',
27266                     html : this.title
27267                 },
27268                 {
27269                     tag : 'span',
27270                     cls : 'roo-alert-text',
27271                     html : this.html
27272                 }
27273             ]
27274         };
27275         
27276         if(this.faicon){
27277             cfg.cn[0].cls += ' fa ' + this.faicon;
27278         }
27279         
27280         if(this.weight){
27281             cfg.cls += ' alert-' + this.weight;
27282         }
27283         
27284         return cfg;
27285     },
27286     
27287     initEvents: function() 
27288     {
27289         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27290     },
27291     
27292     setTitle : function(str)
27293     {
27294         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27295     },
27296     
27297     setText : function(str)
27298     {
27299         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27300     },
27301     
27302     setWeight : function(weight)
27303     {
27304         if(this.weight){
27305             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27306         }
27307         
27308         this.weight = weight;
27309         
27310         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27311     },
27312     
27313     setIcon : function(icon)
27314     {
27315         if(this.faicon){
27316             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27317         }
27318         
27319         this.faicon = icon;
27320         
27321         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27322     },
27323     
27324     hide: function() 
27325     {
27326         this.el.hide();   
27327     },
27328     
27329     show: function() 
27330     {  
27331         this.el.show();   
27332     }
27333     
27334 });
27335
27336  
27337 /*
27338 * Licence: LGPL
27339 */
27340
27341 /**
27342  * @class Roo.bootstrap.UploadCropbox
27343  * @extends Roo.bootstrap.Component
27344  * Bootstrap UploadCropbox class
27345  * @cfg {String} emptyText show when image has been loaded
27346  * @cfg {String} rotateNotify show when image too small to rotate
27347  * @cfg {Number} errorTimeout default 3000
27348  * @cfg {Number} minWidth default 300
27349  * @cfg {Number} minHeight default 300
27350  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27351  * @cfg {Boolean} isDocument (true|false) default false
27352  * @cfg {String} url action url
27353  * @cfg {String} paramName default 'imageUpload'
27354  * @cfg {String} method default POST
27355  * @cfg {Boolean} loadMask (true|false) default true
27356  * @cfg {Boolean} loadingText default 'Loading...'
27357  * 
27358  * @constructor
27359  * Create a new UploadCropbox
27360  * @param {Object} config The config object
27361  */
27362
27363 Roo.bootstrap.UploadCropbox = function(config){
27364     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27365     
27366     this.addEvents({
27367         /**
27368          * @event beforeselectfile
27369          * Fire before select file
27370          * @param {Roo.bootstrap.UploadCropbox} this
27371          */
27372         "beforeselectfile" : true,
27373         /**
27374          * @event initial
27375          * Fire after initEvent
27376          * @param {Roo.bootstrap.UploadCropbox} this
27377          */
27378         "initial" : true,
27379         /**
27380          * @event crop
27381          * Fire after initEvent
27382          * @param {Roo.bootstrap.UploadCropbox} this
27383          * @param {String} data
27384          */
27385         "crop" : true,
27386         /**
27387          * @event prepare
27388          * Fire when preparing the file data
27389          * @param {Roo.bootstrap.UploadCropbox} this
27390          * @param {Object} file
27391          */
27392         "prepare" : true,
27393         /**
27394          * @event exception
27395          * Fire when get exception
27396          * @param {Roo.bootstrap.UploadCropbox} this
27397          * @param {XMLHttpRequest} xhr
27398          */
27399         "exception" : true,
27400         /**
27401          * @event beforeloadcanvas
27402          * Fire before load the canvas
27403          * @param {Roo.bootstrap.UploadCropbox} this
27404          * @param {String} src
27405          */
27406         "beforeloadcanvas" : true,
27407         /**
27408          * @event trash
27409          * Fire when trash image
27410          * @param {Roo.bootstrap.UploadCropbox} this
27411          */
27412         "trash" : true,
27413         /**
27414          * @event download
27415          * Fire when download the image
27416          * @param {Roo.bootstrap.UploadCropbox} this
27417          */
27418         "download" : true,
27419         /**
27420          * @event footerbuttonclick
27421          * Fire when footerbuttonclick
27422          * @param {Roo.bootstrap.UploadCropbox} this
27423          * @param {String} type
27424          */
27425         "footerbuttonclick" : true,
27426         /**
27427          * @event resize
27428          * Fire when resize
27429          * @param {Roo.bootstrap.UploadCropbox} this
27430          */
27431         "resize" : true,
27432         /**
27433          * @event rotate
27434          * Fire when rotate the image
27435          * @param {Roo.bootstrap.UploadCropbox} this
27436          * @param {String} pos
27437          */
27438         "rotate" : true,
27439         /**
27440          * @event inspect
27441          * Fire when inspect the file
27442          * @param {Roo.bootstrap.UploadCropbox} this
27443          * @param {Object} file
27444          */
27445         "inspect" : true,
27446         /**
27447          * @event upload
27448          * Fire when xhr upload the file
27449          * @param {Roo.bootstrap.UploadCropbox} this
27450          * @param {Object} data
27451          */
27452         "upload" : true,
27453         /**
27454          * @event arrange
27455          * Fire when arrange the file data
27456          * @param {Roo.bootstrap.UploadCropbox} this
27457          * @param {Object} formData
27458          */
27459         "arrange" : true
27460     });
27461     
27462     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27463 };
27464
27465 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27466     
27467     emptyText : 'Click to upload image',
27468     rotateNotify : 'Image is too small to rotate',
27469     errorTimeout : 3000,
27470     scale : 0,
27471     baseScale : 1,
27472     rotate : 0,
27473     dragable : false,
27474     pinching : false,
27475     mouseX : 0,
27476     mouseY : 0,
27477     cropData : false,
27478     minWidth : 300,
27479     minHeight : 300,
27480     file : false,
27481     exif : {},
27482     baseRotate : 1,
27483     cropType : 'image/jpeg',
27484     buttons : false,
27485     canvasLoaded : false,
27486     isDocument : false,
27487     method : 'POST',
27488     paramName : 'imageUpload',
27489     loadMask : true,
27490     loadingText : 'Loading...',
27491     maskEl : false,
27492     
27493     getAutoCreate : function()
27494     {
27495         var cfg = {
27496             tag : 'div',
27497             cls : 'roo-upload-cropbox',
27498             cn : [
27499                 {
27500                     tag : 'input',
27501                     cls : 'roo-upload-cropbox-selector',
27502                     type : 'file'
27503                 },
27504                 {
27505                     tag : 'div',
27506                     cls : 'roo-upload-cropbox-body',
27507                     style : 'cursor:pointer',
27508                     cn : [
27509                         {
27510                             tag : 'div',
27511                             cls : 'roo-upload-cropbox-preview'
27512                         },
27513                         {
27514                             tag : 'div',
27515                             cls : 'roo-upload-cropbox-thumb'
27516                         },
27517                         {
27518                             tag : 'div',
27519                             cls : 'roo-upload-cropbox-empty-notify',
27520                             html : this.emptyText
27521                         },
27522                         {
27523                             tag : 'div',
27524                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27525                             html : this.rotateNotify
27526                         }
27527                     ]
27528                 },
27529                 {
27530                     tag : 'div',
27531                     cls : 'roo-upload-cropbox-footer',
27532                     cn : {
27533                         tag : 'div',
27534                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27535                         cn : []
27536                     }
27537                 }
27538             ]
27539         };
27540         
27541         return cfg;
27542     },
27543     
27544     onRender : function(ct, position)
27545     {
27546         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27547         
27548         if (this.buttons.length) {
27549             
27550             Roo.each(this.buttons, function(bb) {
27551                 
27552                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27553                 
27554                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27555                 
27556             }, this);
27557         }
27558         
27559         if(this.loadMask){
27560             this.maskEl = this.el;
27561         }
27562     },
27563     
27564     initEvents : function()
27565     {
27566         this.urlAPI = (window.createObjectURL && window) || 
27567                                 (window.URL && URL.revokeObjectURL && URL) || 
27568                                 (window.webkitURL && webkitURL);
27569                         
27570         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27571         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27572         
27573         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27574         this.selectorEl.hide();
27575         
27576         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27577         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27578         
27579         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27580         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27581         this.thumbEl.hide();
27582         
27583         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27584         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27585         
27586         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27587         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27588         this.errorEl.hide();
27589         
27590         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27591         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27592         this.footerEl.hide();
27593         
27594         this.setThumbBoxSize();
27595         
27596         this.bind();
27597         
27598         this.resize();
27599         
27600         this.fireEvent('initial', this);
27601     },
27602
27603     bind : function()
27604     {
27605         var _this = this;
27606         
27607         window.addEventListener("resize", function() { _this.resize(); } );
27608         
27609         this.bodyEl.on('click', this.beforeSelectFile, this);
27610         
27611         if(Roo.isTouch){
27612             this.bodyEl.on('touchstart', this.onTouchStart, this);
27613             this.bodyEl.on('touchmove', this.onTouchMove, this);
27614             this.bodyEl.on('touchend', this.onTouchEnd, this);
27615         }
27616         
27617         if(!Roo.isTouch){
27618             this.bodyEl.on('mousedown', this.onMouseDown, this);
27619             this.bodyEl.on('mousemove', this.onMouseMove, this);
27620             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27621             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27622             Roo.get(document).on('mouseup', this.onMouseUp, this);
27623         }
27624         
27625         this.selectorEl.on('change', this.onFileSelected, this);
27626     },
27627     
27628     reset : function()
27629     {    
27630         this.scale = 0;
27631         this.baseScale = 1;
27632         this.rotate = 0;
27633         this.baseRotate = 1;
27634         this.dragable = false;
27635         this.pinching = false;
27636         this.mouseX = 0;
27637         this.mouseY = 0;
27638         this.cropData = false;
27639         this.notifyEl.dom.innerHTML = this.emptyText;
27640         
27641         this.selectorEl.dom.value = '';
27642         
27643     },
27644     
27645     resize : function()
27646     {
27647         if(this.fireEvent('resize', this) != false){
27648             this.setThumbBoxPosition();
27649             this.setCanvasPosition();
27650         }
27651     },
27652     
27653     onFooterButtonClick : function(e, el, o, type)
27654     {
27655         switch (type) {
27656             case 'rotate-left' :
27657                 this.onRotateLeft(e);
27658                 break;
27659             case 'rotate-right' :
27660                 this.onRotateRight(e);
27661                 break;
27662             case 'picture' :
27663                 this.beforeSelectFile(e);
27664                 break;
27665             case 'trash' :
27666                 this.trash(e);
27667                 break;
27668             case 'crop' :
27669                 this.crop(e);
27670                 break;
27671             case 'download' :
27672                 this.download(e);
27673                 break;
27674             default :
27675                 break;
27676         }
27677         
27678         this.fireEvent('footerbuttonclick', this, type);
27679     },
27680     
27681     beforeSelectFile : function(e)
27682     {
27683         e.preventDefault();
27684         
27685         if(this.fireEvent('beforeselectfile', this) != false){
27686             this.selectorEl.dom.click();
27687         }
27688     },
27689     
27690     onFileSelected : function(e)
27691     {
27692         e.preventDefault();
27693         
27694         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27695             return;
27696         }
27697         
27698         var file = this.selectorEl.dom.files[0];
27699         
27700         if(this.fireEvent('inspect', this, file) != false){
27701             this.prepare(file);
27702         }
27703         
27704     },
27705     
27706     trash : function(e)
27707     {
27708         this.fireEvent('trash', this);
27709     },
27710     
27711     download : function(e)
27712     {
27713         this.fireEvent('download', this);
27714     },
27715     
27716     loadCanvas : function(src)
27717     {   
27718         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27719             
27720             this.reset();
27721             
27722             this.imageEl = document.createElement('img');
27723             
27724             var _this = this;
27725             
27726             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27727             
27728             this.imageEl.src = src;
27729         }
27730     },
27731     
27732     onLoadCanvas : function()
27733     {   
27734         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27735         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27736         
27737         this.bodyEl.un('click', this.beforeSelectFile, this);
27738         
27739         this.notifyEl.hide();
27740         this.thumbEl.show();
27741         this.footerEl.show();
27742         
27743         this.baseRotateLevel();
27744         
27745         if(this.isDocument){
27746             this.setThumbBoxSize();
27747         }
27748         
27749         this.setThumbBoxPosition();
27750         
27751         this.baseScaleLevel();
27752         
27753         this.draw();
27754         
27755         this.resize();
27756         
27757         this.canvasLoaded = true;
27758         
27759         if(this.loadMask){
27760             this.maskEl.unmask();
27761         }
27762         
27763     },
27764     
27765     setCanvasPosition : function()
27766     {   
27767         if(!this.canvasEl){
27768             return;
27769         }
27770         
27771         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27772         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27773         
27774         this.previewEl.setLeft(pw);
27775         this.previewEl.setTop(ph);
27776         
27777     },
27778     
27779     onMouseDown : function(e)
27780     {   
27781         e.stopEvent();
27782         
27783         this.dragable = true;
27784         this.pinching = false;
27785         
27786         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27787             this.dragable = false;
27788             return;
27789         }
27790         
27791         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27792         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27793         
27794     },
27795     
27796     onMouseMove : function(e)
27797     {   
27798         e.stopEvent();
27799         
27800         if(!this.canvasLoaded){
27801             return;
27802         }
27803         
27804         if (!this.dragable){
27805             return;
27806         }
27807         
27808         var minX = Math.ceil(this.thumbEl.getLeft(true));
27809         var minY = Math.ceil(this.thumbEl.getTop(true));
27810         
27811         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27812         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27813         
27814         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27815         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27816         
27817         x = x - this.mouseX;
27818         y = y - this.mouseY;
27819         
27820         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27821         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27822         
27823         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27824         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27825         
27826         this.previewEl.setLeft(bgX);
27827         this.previewEl.setTop(bgY);
27828         
27829         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27830         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27831     },
27832     
27833     onMouseUp : function(e)
27834     {   
27835         e.stopEvent();
27836         
27837         this.dragable = false;
27838     },
27839     
27840     onMouseWheel : function(e)
27841     {   
27842         e.stopEvent();
27843         
27844         this.startScale = this.scale;
27845         
27846         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27847         
27848         if(!this.zoomable()){
27849             this.scale = this.startScale;
27850             return;
27851         }
27852         
27853         this.draw();
27854         
27855         return;
27856     },
27857     
27858     zoomable : function()
27859     {
27860         var minScale = this.thumbEl.getWidth() / this.minWidth;
27861         
27862         if(this.minWidth < this.minHeight){
27863             minScale = this.thumbEl.getHeight() / this.minHeight;
27864         }
27865         
27866         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27867         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27868         
27869         if(
27870                 this.isDocument &&
27871                 (this.rotate == 0 || this.rotate == 180) && 
27872                 (
27873                     width > this.imageEl.OriginWidth || 
27874                     height > this.imageEl.OriginHeight ||
27875                     (width < this.minWidth && height < this.minHeight)
27876                 )
27877         ){
27878             return false;
27879         }
27880         
27881         if(
27882                 this.isDocument &&
27883                 (this.rotate == 90 || this.rotate == 270) && 
27884                 (
27885                     width > this.imageEl.OriginWidth || 
27886                     height > this.imageEl.OriginHeight ||
27887                     (width < this.minHeight && height < this.minWidth)
27888                 )
27889         ){
27890             return false;
27891         }
27892         
27893         if(
27894                 !this.isDocument &&
27895                 (this.rotate == 0 || this.rotate == 180) && 
27896                 (
27897                     width < this.minWidth || 
27898                     width > this.imageEl.OriginWidth || 
27899                     height < this.minHeight || 
27900                     height > this.imageEl.OriginHeight
27901                 )
27902         ){
27903             return false;
27904         }
27905         
27906         if(
27907                 !this.isDocument &&
27908                 (this.rotate == 90 || this.rotate == 270) && 
27909                 (
27910                     width < this.minHeight || 
27911                     width > this.imageEl.OriginWidth || 
27912                     height < this.minWidth || 
27913                     height > this.imageEl.OriginHeight
27914                 )
27915         ){
27916             return false;
27917         }
27918         
27919         return true;
27920         
27921     },
27922     
27923     onRotateLeft : function(e)
27924     {   
27925         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27926             
27927             var minScale = this.thumbEl.getWidth() / this.minWidth;
27928             
27929             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27930             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27931             
27932             this.startScale = this.scale;
27933             
27934             while (this.getScaleLevel() < minScale){
27935             
27936                 this.scale = this.scale + 1;
27937                 
27938                 if(!this.zoomable()){
27939                     break;
27940                 }
27941                 
27942                 if(
27943                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27944                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27945                 ){
27946                     continue;
27947                 }
27948                 
27949                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27950
27951                 this.draw();
27952                 
27953                 return;
27954             }
27955             
27956             this.scale = this.startScale;
27957             
27958             this.onRotateFail();
27959             
27960             return false;
27961         }
27962         
27963         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27964
27965         if(this.isDocument){
27966             this.setThumbBoxSize();
27967             this.setThumbBoxPosition();
27968             this.setCanvasPosition();
27969         }
27970         
27971         this.draw();
27972         
27973         this.fireEvent('rotate', this, 'left');
27974         
27975     },
27976     
27977     onRotateRight : function(e)
27978     {
27979         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27980             
27981             var minScale = this.thumbEl.getWidth() / this.minWidth;
27982         
27983             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27984             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27985             
27986             this.startScale = this.scale;
27987             
27988             while (this.getScaleLevel() < minScale){
27989             
27990                 this.scale = this.scale + 1;
27991                 
27992                 if(!this.zoomable()){
27993                     break;
27994                 }
27995                 
27996                 if(
27997                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27998                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27999                 ){
28000                     continue;
28001                 }
28002                 
28003                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28004
28005                 this.draw();
28006                 
28007                 return;
28008             }
28009             
28010             this.scale = this.startScale;
28011             
28012             this.onRotateFail();
28013             
28014             return false;
28015         }
28016         
28017         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28018
28019         if(this.isDocument){
28020             this.setThumbBoxSize();
28021             this.setThumbBoxPosition();
28022             this.setCanvasPosition();
28023         }
28024         
28025         this.draw();
28026         
28027         this.fireEvent('rotate', this, 'right');
28028     },
28029     
28030     onRotateFail : function()
28031     {
28032         this.errorEl.show(true);
28033         
28034         var _this = this;
28035         
28036         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
28037     },
28038     
28039     draw : function()
28040     {
28041         this.previewEl.dom.innerHTML = '';
28042         
28043         var canvasEl = document.createElement("canvas");
28044         
28045         var contextEl = canvasEl.getContext("2d");
28046         
28047         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28048         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28049         var center = this.imageEl.OriginWidth / 2;
28050         
28051         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
28052             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28053             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28054             center = this.imageEl.OriginHeight / 2;
28055         }
28056         
28057         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
28058         
28059         contextEl.translate(center, center);
28060         contextEl.rotate(this.rotate * Math.PI / 180);
28061
28062         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28063         
28064         this.canvasEl = document.createElement("canvas");
28065         
28066         this.contextEl = this.canvasEl.getContext("2d");
28067         
28068         switch (this.rotate) {
28069             case 0 :
28070                 
28071                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28072                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28073                 
28074                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28075                 
28076                 break;
28077             case 90 : 
28078                 
28079                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28080                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28081                 
28082                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28083                     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);
28084                     break;
28085                 }
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 180 :
28091                 
28092                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28093                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28094                 
28095                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28096                     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);
28097                     break;
28098                 }
28099                 
28100                 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);
28101                 
28102                 break;
28103             case 270 :
28104                 
28105                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28106                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28107         
28108                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28109                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28110                     break;
28111                 }
28112                 
28113                 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);
28114                 
28115                 break;
28116             default : 
28117                 break;
28118         }
28119         
28120         this.previewEl.appendChild(this.canvasEl);
28121         
28122         this.setCanvasPosition();
28123     },
28124     
28125     crop : function()
28126     {
28127         if(!this.canvasLoaded){
28128             return;
28129         }
28130         
28131         var imageCanvas = document.createElement("canvas");
28132         
28133         var imageContext = imageCanvas.getContext("2d");
28134         
28135         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28136         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28137         
28138         var center = imageCanvas.width / 2;
28139         
28140         imageContext.translate(center, center);
28141         
28142         imageContext.rotate(this.rotate * Math.PI / 180);
28143         
28144         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28145         
28146         var canvas = document.createElement("canvas");
28147         
28148         var context = canvas.getContext("2d");
28149                 
28150         canvas.width = this.minWidth;
28151         canvas.height = this.minHeight;
28152
28153         switch (this.rotate) {
28154             case 0 :
28155                 
28156                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28157                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28158                 
28159                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28160                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28161                 
28162                 var targetWidth = this.minWidth - 2 * x;
28163                 var targetHeight = this.minHeight - 2 * y;
28164                 
28165                 var scale = 1;
28166                 
28167                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28168                     scale = targetWidth / width;
28169                 }
28170                 
28171                 if(x > 0 && y == 0){
28172                     scale = targetHeight / height;
28173                 }
28174                 
28175                 if(x > 0 && y > 0){
28176                     scale = targetWidth / width;
28177                     
28178                     if(width < height){
28179                         scale = targetHeight / height;
28180                     }
28181                 }
28182                 
28183                 context.scale(scale, scale);
28184                 
28185                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28186                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28187
28188                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28189                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28190
28191                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28192                 
28193                 break;
28194             case 90 : 
28195                 
28196                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28197                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28198                 
28199                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28200                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28201                 
28202                 var targetWidth = this.minWidth - 2 * x;
28203                 var targetHeight = this.minHeight - 2 * y;
28204                 
28205                 var scale = 1;
28206                 
28207                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28208                     scale = targetWidth / width;
28209                 }
28210                 
28211                 if(x > 0 && y == 0){
28212                     scale = targetHeight / height;
28213                 }
28214                 
28215                 if(x > 0 && y > 0){
28216                     scale = targetWidth / width;
28217                     
28218                     if(width < height){
28219                         scale = targetHeight / height;
28220                     }
28221                 }
28222                 
28223                 context.scale(scale, scale);
28224                 
28225                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28226                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28227
28228                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28229                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28230                 
28231                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28232                 
28233                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28234                 
28235                 break;
28236             case 180 :
28237                 
28238                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28239                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28240                 
28241                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28242                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28243                 
28244                 var targetWidth = this.minWidth - 2 * x;
28245                 var targetHeight = this.minHeight - 2 * y;
28246                 
28247                 var scale = 1;
28248                 
28249                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28250                     scale = targetWidth / width;
28251                 }
28252                 
28253                 if(x > 0 && y == 0){
28254                     scale = targetHeight / height;
28255                 }
28256                 
28257                 if(x > 0 && y > 0){
28258                     scale = targetWidth / width;
28259                     
28260                     if(width < height){
28261                         scale = targetHeight / height;
28262                     }
28263                 }
28264                 
28265                 context.scale(scale, scale);
28266                 
28267                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28268                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28269
28270                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28271                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28272
28273                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28274                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28275                 
28276                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28277                 
28278                 break;
28279             case 270 :
28280                 
28281                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28282                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28283                 
28284                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28285                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28286                 
28287                 var targetWidth = this.minWidth - 2 * x;
28288                 var targetHeight = this.minHeight - 2 * y;
28289                 
28290                 var scale = 1;
28291                 
28292                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28293                     scale = targetWidth / width;
28294                 }
28295                 
28296                 if(x > 0 && y == 0){
28297                     scale = targetHeight / height;
28298                 }
28299                 
28300                 if(x > 0 && y > 0){
28301                     scale = targetWidth / width;
28302                     
28303                     if(width < height){
28304                         scale = targetHeight / height;
28305                     }
28306                 }
28307                 
28308                 context.scale(scale, scale);
28309                 
28310                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28311                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28312
28313                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28314                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28315                 
28316                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28317                 
28318                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28319                 
28320                 break;
28321             default : 
28322                 break;
28323         }
28324         
28325         this.cropData = canvas.toDataURL(this.cropType);
28326         
28327         if(this.fireEvent('crop', this, this.cropData) !== false){
28328             this.process(this.file, this.cropData);
28329         }
28330         
28331         return;
28332         
28333     },
28334     
28335     setThumbBoxSize : function()
28336     {
28337         var width, height;
28338         
28339         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28340             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28341             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28342             
28343             this.minWidth = width;
28344             this.minHeight = height;
28345             
28346             if(this.rotate == 90 || this.rotate == 270){
28347                 this.minWidth = height;
28348                 this.minHeight = width;
28349             }
28350         }
28351         
28352         height = 300;
28353         width = Math.ceil(this.minWidth * height / this.minHeight);
28354         
28355         if(this.minWidth > this.minHeight){
28356             width = 300;
28357             height = Math.ceil(this.minHeight * width / this.minWidth);
28358         }
28359         
28360         this.thumbEl.setStyle({
28361             width : width + 'px',
28362             height : height + 'px'
28363         });
28364
28365         return;
28366             
28367     },
28368     
28369     setThumbBoxPosition : function()
28370     {
28371         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28372         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28373         
28374         this.thumbEl.setLeft(x);
28375         this.thumbEl.setTop(y);
28376         
28377     },
28378     
28379     baseRotateLevel : function()
28380     {
28381         this.baseRotate = 1;
28382         
28383         if(
28384                 typeof(this.exif) != 'undefined' &&
28385                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28386                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28387         ){
28388             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28389         }
28390         
28391         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28392         
28393     },
28394     
28395     baseScaleLevel : function()
28396     {
28397         var width, height;
28398         
28399         if(this.isDocument){
28400             
28401             if(this.baseRotate == 6 || this.baseRotate == 8){
28402             
28403                 height = this.thumbEl.getHeight();
28404                 this.baseScale = height / this.imageEl.OriginWidth;
28405
28406                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28407                     width = this.thumbEl.getWidth();
28408                     this.baseScale = width / this.imageEl.OriginHeight;
28409                 }
28410
28411                 return;
28412             }
28413
28414             height = this.thumbEl.getHeight();
28415             this.baseScale = height / this.imageEl.OriginHeight;
28416
28417             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28418                 width = this.thumbEl.getWidth();
28419                 this.baseScale = width / this.imageEl.OriginWidth;
28420             }
28421
28422             return;
28423         }
28424         
28425         if(this.baseRotate == 6 || this.baseRotate == 8){
28426             
28427             width = this.thumbEl.getHeight();
28428             this.baseScale = width / this.imageEl.OriginHeight;
28429             
28430             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28431                 height = this.thumbEl.getWidth();
28432                 this.baseScale = height / this.imageEl.OriginHeight;
28433             }
28434             
28435             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28436                 height = this.thumbEl.getWidth();
28437                 this.baseScale = height / this.imageEl.OriginHeight;
28438                 
28439                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28440                     width = this.thumbEl.getHeight();
28441                     this.baseScale = width / this.imageEl.OriginWidth;
28442                 }
28443             }
28444             
28445             return;
28446         }
28447         
28448         width = this.thumbEl.getWidth();
28449         this.baseScale = width / this.imageEl.OriginWidth;
28450         
28451         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28452             height = this.thumbEl.getHeight();
28453             this.baseScale = height / this.imageEl.OriginHeight;
28454         }
28455         
28456         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28457             
28458             height = this.thumbEl.getHeight();
28459             this.baseScale = height / this.imageEl.OriginHeight;
28460             
28461             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28462                 width = this.thumbEl.getWidth();
28463                 this.baseScale = width / this.imageEl.OriginWidth;
28464             }
28465             
28466         }
28467         
28468         return;
28469     },
28470     
28471     getScaleLevel : function()
28472     {
28473         return this.baseScale * Math.pow(1.1, this.scale);
28474     },
28475     
28476     onTouchStart : function(e)
28477     {
28478         if(!this.canvasLoaded){
28479             this.beforeSelectFile(e);
28480             return;
28481         }
28482         
28483         var touches = e.browserEvent.touches;
28484         
28485         if(!touches){
28486             return;
28487         }
28488         
28489         if(touches.length == 1){
28490             this.onMouseDown(e);
28491             return;
28492         }
28493         
28494         if(touches.length != 2){
28495             return;
28496         }
28497         
28498         var coords = [];
28499         
28500         for(var i = 0, finger; finger = touches[i]; i++){
28501             coords.push(finger.pageX, finger.pageY);
28502         }
28503         
28504         var x = Math.pow(coords[0] - coords[2], 2);
28505         var y = Math.pow(coords[1] - coords[3], 2);
28506         
28507         this.startDistance = Math.sqrt(x + y);
28508         
28509         this.startScale = this.scale;
28510         
28511         this.pinching = true;
28512         this.dragable = false;
28513         
28514     },
28515     
28516     onTouchMove : function(e)
28517     {
28518         if(!this.pinching && !this.dragable){
28519             return;
28520         }
28521         
28522         var touches = e.browserEvent.touches;
28523         
28524         if(!touches){
28525             return;
28526         }
28527         
28528         if(this.dragable){
28529             this.onMouseMove(e);
28530             return;
28531         }
28532         
28533         var coords = [];
28534         
28535         for(var i = 0, finger; finger = touches[i]; i++){
28536             coords.push(finger.pageX, finger.pageY);
28537         }
28538         
28539         var x = Math.pow(coords[0] - coords[2], 2);
28540         var y = Math.pow(coords[1] - coords[3], 2);
28541         
28542         this.endDistance = Math.sqrt(x + y);
28543         
28544         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28545         
28546         if(!this.zoomable()){
28547             this.scale = this.startScale;
28548             return;
28549         }
28550         
28551         this.draw();
28552         
28553     },
28554     
28555     onTouchEnd : function(e)
28556     {
28557         this.pinching = false;
28558         this.dragable = false;
28559         
28560     },
28561     
28562     process : function(file, crop)
28563     {
28564         if(this.loadMask){
28565             this.maskEl.mask(this.loadingText);
28566         }
28567         
28568         this.xhr = new XMLHttpRequest();
28569         
28570         file.xhr = this.xhr;
28571
28572         this.xhr.open(this.method, this.url, true);
28573         
28574         var headers = {
28575             "Accept": "application/json",
28576             "Cache-Control": "no-cache",
28577             "X-Requested-With": "XMLHttpRequest"
28578         };
28579         
28580         for (var headerName in headers) {
28581             var headerValue = headers[headerName];
28582             if (headerValue) {
28583                 this.xhr.setRequestHeader(headerName, headerValue);
28584             }
28585         }
28586         
28587         var _this = this;
28588         
28589         this.xhr.onload = function()
28590         {
28591             _this.xhrOnLoad(_this.xhr);
28592         }
28593         
28594         this.xhr.onerror = function()
28595         {
28596             _this.xhrOnError(_this.xhr);
28597         }
28598         
28599         var formData = new FormData();
28600
28601         formData.append('returnHTML', 'NO');
28602         
28603         if(crop){
28604             formData.append('crop', crop);
28605         }
28606         
28607         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28608             formData.append(this.paramName, file, file.name);
28609         }
28610         
28611         if(typeof(file.filename) != 'undefined'){
28612             formData.append('filename', file.filename);
28613         }
28614         
28615         if(typeof(file.mimetype) != 'undefined'){
28616             formData.append('mimetype', file.mimetype);
28617         }
28618         
28619         if(this.fireEvent('arrange', this, formData) != false){
28620             this.xhr.send(formData);
28621         };
28622     },
28623     
28624     xhrOnLoad : function(xhr)
28625     {
28626         if(this.loadMask){
28627             this.maskEl.unmask();
28628         }
28629         
28630         if (xhr.readyState !== 4) {
28631             this.fireEvent('exception', this, xhr);
28632             return;
28633         }
28634
28635         var response = Roo.decode(xhr.responseText);
28636         
28637         if(!response.success){
28638             this.fireEvent('exception', this, xhr);
28639             return;
28640         }
28641         
28642         var response = Roo.decode(xhr.responseText);
28643         
28644         this.fireEvent('upload', this, response);
28645         
28646     },
28647     
28648     xhrOnError : function()
28649     {
28650         if(this.loadMask){
28651             this.maskEl.unmask();
28652         }
28653         
28654         Roo.log('xhr on error');
28655         
28656         var response = Roo.decode(xhr.responseText);
28657           
28658         Roo.log(response);
28659         
28660     },
28661     
28662     prepare : function(file)
28663     {   
28664         if(this.loadMask){
28665             this.maskEl.mask(this.loadingText);
28666         }
28667         
28668         this.file = false;
28669         this.exif = {};
28670         
28671         if(typeof(file) === 'string'){
28672             this.loadCanvas(file);
28673             return;
28674         }
28675         
28676         if(!file || !this.urlAPI){
28677             return;
28678         }
28679         
28680         this.file = file;
28681         this.cropType = file.type;
28682         
28683         var _this = this;
28684         
28685         if(this.fireEvent('prepare', this, this.file) != false){
28686             
28687             var reader = new FileReader();
28688             
28689             reader.onload = function (e) {
28690                 if (e.target.error) {
28691                     Roo.log(e.target.error);
28692                     return;
28693                 }
28694                 
28695                 var buffer = e.target.result,
28696                     dataView = new DataView(buffer),
28697                     offset = 2,
28698                     maxOffset = dataView.byteLength - 4,
28699                     markerBytes,
28700                     markerLength;
28701                 
28702                 if (dataView.getUint16(0) === 0xffd8) {
28703                     while (offset < maxOffset) {
28704                         markerBytes = dataView.getUint16(offset);
28705                         
28706                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28707                             markerLength = dataView.getUint16(offset + 2) + 2;
28708                             if (offset + markerLength > dataView.byteLength) {
28709                                 Roo.log('Invalid meta data: Invalid segment size.');
28710                                 break;
28711                             }
28712                             
28713                             if(markerBytes == 0xffe1){
28714                                 _this.parseExifData(
28715                                     dataView,
28716                                     offset,
28717                                     markerLength
28718                                 );
28719                             }
28720                             
28721                             offset += markerLength;
28722                             
28723                             continue;
28724                         }
28725                         
28726                         break;
28727                     }
28728                     
28729                 }
28730                 
28731                 var url = _this.urlAPI.createObjectURL(_this.file);
28732                 
28733                 _this.loadCanvas(url);
28734                 
28735                 return;
28736             }
28737             
28738             reader.readAsArrayBuffer(this.file);
28739             
28740         }
28741         
28742     },
28743     
28744     parseExifData : function(dataView, offset, length)
28745     {
28746         var tiffOffset = offset + 10,
28747             littleEndian,
28748             dirOffset;
28749     
28750         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28751             // No Exif data, might be XMP data instead
28752             return;
28753         }
28754         
28755         // Check for the ASCII code for "Exif" (0x45786966):
28756         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28757             // No Exif data, might be XMP data instead
28758             return;
28759         }
28760         if (tiffOffset + 8 > dataView.byteLength) {
28761             Roo.log('Invalid Exif data: Invalid segment size.');
28762             return;
28763         }
28764         // Check for the two null bytes:
28765         if (dataView.getUint16(offset + 8) !== 0x0000) {
28766             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28767             return;
28768         }
28769         // Check the byte alignment:
28770         switch (dataView.getUint16(tiffOffset)) {
28771         case 0x4949:
28772             littleEndian = true;
28773             break;
28774         case 0x4D4D:
28775             littleEndian = false;
28776             break;
28777         default:
28778             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28779             return;
28780         }
28781         // Check for the TIFF tag marker (0x002A):
28782         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28783             Roo.log('Invalid Exif data: Missing TIFF marker.');
28784             return;
28785         }
28786         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28787         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28788         
28789         this.parseExifTags(
28790             dataView,
28791             tiffOffset,
28792             tiffOffset + dirOffset,
28793             littleEndian
28794         );
28795     },
28796     
28797     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28798     {
28799         var tagsNumber,
28800             dirEndOffset,
28801             i;
28802         if (dirOffset + 6 > dataView.byteLength) {
28803             Roo.log('Invalid Exif data: Invalid directory offset.');
28804             return;
28805         }
28806         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28807         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28808         if (dirEndOffset + 4 > dataView.byteLength) {
28809             Roo.log('Invalid Exif data: Invalid directory size.');
28810             return;
28811         }
28812         for (i = 0; i < tagsNumber; i += 1) {
28813             this.parseExifTag(
28814                 dataView,
28815                 tiffOffset,
28816                 dirOffset + 2 + 12 * i, // tag offset
28817                 littleEndian
28818             );
28819         }
28820         // Return the offset to the next directory:
28821         return dataView.getUint32(dirEndOffset, littleEndian);
28822     },
28823     
28824     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28825     {
28826         var tag = dataView.getUint16(offset, littleEndian);
28827         
28828         this.exif[tag] = this.getExifValue(
28829             dataView,
28830             tiffOffset,
28831             offset,
28832             dataView.getUint16(offset + 2, littleEndian), // tag type
28833             dataView.getUint32(offset + 4, littleEndian), // tag length
28834             littleEndian
28835         );
28836     },
28837     
28838     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28839     {
28840         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28841             tagSize,
28842             dataOffset,
28843             values,
28844             i,
28845             str,
28846             c;
28847     
28848         if (!tagType) {
28849             Roo.log('Invalid Exif data: Invalid tag type.');
28850             return;
28851         }
28852         
28853         tagSize = tagType.size * length;
28854         // Determine if the value is contained in the dataOffset bytes,
28855         // or if the value at the dataOffset is a pointer to the actual data:
28856         dataOffset = tagSize > 4 ?
28857                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28858         if (dataOffset + tagSize > dataView.byteLength) {
28859             Roo.log('Invalid Exif data: Invalid data offset.');
28860             return;
28861         }
28862         if (length === 1) {
28863             return tagType.getValue(dataView, dataOffset, littleEndian);
28864         }
28865         values = [];
28866         for (i = 0; i < length; i += 1) {
28867             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28868         }
28869         
28870         if (tagType.ascii) {
28871             str = '';
28872             // Concatenate the chars:
28873             for (i = 0; i < values.length; i += 1) {
28874                 c = values[i];
28875                 // Ignore the terminating NULL byte(s):
28876                 if (c === '\u0000') {
28877                     break;
28878                 }
28879                 str += c;
28880             }
28881             return str;
28882         }
28883         return values;
28884     }
28885     
28886 });
28887
28888 Roo.apply(Roo.bootstrap.UploadCropbox, {
28889     tags : {
28890         'Orientation': 0x0112
28891     },
28892     
28893     Orientation: {
28894             1: 0, //'top-left',
28895 //            2: 'top-right',
28896             3: 180, //'bottom-right',
28897 //            4: 'bottom-left',
28898 //            5: 'left-top',
28899             6: 90, //'right-top',
28900 //            7: 'right-bottom',
28901             8: 270 //'left-bottom'
28902     },
28903     
28904     exifTagTypes : {
28905         // byte, 8-bit unsigned int:
28906         1: {
28907             getValue: function (dataView, dataOffset) {
28908                 return dataView.getUint8(dataOffset);
28909             },
28910             size: 1
28911         },
28912         // ascii, 8-bit byte:
28913         2: {
28914             getValue: function (dataView, dataOffset) {
28915                 return String.fromCharCode(dataView.getUint8(dataOffset));
28916             },
28917             size: 1,
28918             ascii: true
28919         },
28920         // short, 16 bit int:
28921         3: {
28922             getValue: function (dataView, dataOffset, littleEndian) {
28923                 return dataView.getUint16(dataOffset, littleEndian);
28924             },
28925             size: 2
28926         },
28927         // long, 32 bit int:
28928         4: {
28929             getValue: function (dataView, dataOffset, littleEndian) {
28930                 return dataView.getUint32(dataOffset, littleEndian);
28931             },
28932             size: 4
28933         },
28934         // rational = two long values, first is numerator, second is denominator:
28935         5: {
28936             getValue: function (dataView, dataOffset, littleEndian) {
28937                 return dataView.getUint32(dataOffset, littleEndian) /
28938                     dataView.getUint32(dataOffset + 4, littleEndian);
28939             },
28940             size: 8
28941         },
28942         // slong, 32 bit signed int:
28943         9: {
28944             getValue: function (dataView, dataOffset, littleEndian) {
28945                 return dataView.getInt32(dataOffset, littleEndian);
28946             },
28947             size: 4
28948         },
28949         // srational, two slongs, first is numerator, second is denominator:
28950         10: {
28951             getValue: function (dataView, dataOffset, littleEndian) {
28952                 return dataView.getInt32(dataOffset, littleEndian) /
28953                     dataView.getInt32(dataOffset + 4, littleEndian);
28954             },
28955             size: 8
28956         }
28957     },
28958     
28959     footer : {
28960         STANDARD : [
28961             {
28962                 tag : 'div',
28963                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28964                 action : 'rotate-left',
28965                 cn : [
28966                     {
28967                         tag : 'button',
28968                         cls : 'btn btn-default',
28969                         html : '<i class="fa fa-undo"></i>'
28970                     }
28971                 ]
28972             },
28973             {
28974                 tag : 'div',
28975                 cls : 'btn-group roo-upload-cropbox-picture',
28976                 action : 'picture',
28977                 cn : [
28978                     {
28979                         tag : 'button',
28980                         cls : 'btn btn-default',
28981                         html : '<i class="fa fa-picture-o"></i>'
28982                     }
28983                 ]
28984             },
28985             {
28986                 tag : 'div',
28987                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28988                 action : 'rotate-right',
28989                 cn : [
28990                     {
28991                         tag : 'button',
28992                         cls : 'btn btn-default',
28993                         html : '<i class="fa fa-repeat"></i>'
28994                     }
28995                 ]
28996             }
28997         ],
28998         DOCUMENT : [
28999             {
29000                 tag : 'div',
29001                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29002                 action : 'rotate-left',
29003                 cn : [
29004                     {
29005                         tag : 'button',
29006                         cls : 'btn btn-default',
29007                         html : '<i class="fa fa-undo"></i>'
29008                     }
29009                 ]
29010             },
29011             {
29012                 tag : 'div',
29013                 cls : 'btn-group roo-upload-cropbox-download',
29014                 action : 'download',
29015                 cn : [
29016                     {
29017                         tag : 'button',
29018                         cls : 'btn btn-default',
29019                         html : '<i class="fa fa-download"></i>'
29020                     }
29021                 ]
29022             },
29023             {
29024                 tag : 'div',
29025                 cls : 'btn-group roo-upload-cropbox-crop',
29026                 action : 'crop',
29027                 cn : [
29028                     {
29029                         tag : 'button',
29030                         cls : 'btn btn-default',
29031                         html : '<i class="fa fa-crop"></i>'
29032                     }
29033                 ]
29034             },
29035             {
29036                 tag : 'div',
29037                 cls : 'btn-group roo-upload-cropbox-trash',
29038                 action : 'trash',
29039                 cn : [
29040                     {
29041                         tag : 'button',
29042                         cls : 'btn btn-default',
29043                         html : '<i class="fa fa-trash"></i>'
29044                     }
29045                 ]
29046             },
29047             {
29048                 tag : 'div',
29049                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29050                 action : 'rotate-right',
29051                 cn : [
29052                     {
29053                         tag : 'button',
29054                         cls : 'btn btn-default',
29055                         html : '<i class="fa fa-repeat"></i>'
29056                     }
29057                 ]
29058             }
29059         ],
29060         ROTATOR : [
29061             {
29062                 tag : 'div',
29063                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29064                 action : 'rotate-left',
29065                 cn : [
29066                     {
29067                         tag : 'button',
29068                         cls : 'btn btn-default',
29069                         html : '<i class="fa fa-undo"></i>'
29070                     }
29071                 ]
29072             },
29073             {
29074                 tag : 'div',
29075                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29076                 action : 'rotate-right',
29077                 cn : [
29078                     {
29079                         tag : 'button',
29080                         cls : 'btn btn-default',
29081                         html : '<i class="fa fa-repeat"></i>'
29082                     }
29083                 ]
29084             }
29085         ]
29086     }
29087 });
29088
29089 /*
29090 * Licence: LGPL
29091 */
29092
29093 /**
29094  * @class Roo.bootstrap.DocumentManager
29095  * @extends Roo.bootstrap.Component
29096  * Bootstrap DocumentManager class
29097  * @cfg {String} paramName default 'imageUpload'
29098  * @cfg {String} toolTipName default 'filename'
29099  * @cfg {String} method default POST
29100  * @cfg {String} url action url
29101  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
29102  * @cfg {Boolean} multiple multiple upload default true
29103  * @cfg {Number} thumbSize default 300
29104  * @cfg {String} fieldLabel
29105  * @cfg {Number} labelWidth default 4
29106  * @cfg {String} labelAlign (left|top) default left
29107  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
29108 * @cfg {Number} labellg set the width of label (1-12)
29109  * @cfg {Number} labelmd set the width of label (1-12)
29110  * @cfg {Number} labelsm set the width of label (1-12)
29111  * @cfg {Number} labelxs set the width of label (1-12)
29112  * 
29113  * @constructor
29114  * Create a new DocumentManager
29115  * @param {Object} config The config object
29116  */
29117
29118 Roo.bootstrap.DocumentManager = function(config){
29119     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
29120     
29121     this.files = [];
29122     this.delegates = [];
29123     
29124     this.addEvents({
29125         /**
29126          * @event initial
29127          * Fire when initial the DocumentManager
29128          * @param {Roo.bootstrap.DocumentManager} this
29129          */
29130         "initial" : true,
29131         /**
29132          * @event inspect
29133          * inspect selected file
29134          * @param {Roo.bootstrap.DocumentManager} this
29135          * @param {File} file
29136          */
29137         "inspect" : true,
29138         /**
29139          * @event exception
29140          * Fire when xhr load exception
29141          * @param {Roo.bootstrap.DocumentManager} this
29142          * @param {XMLHttpRequest} xhr
29143          */
29144         "exception" : true,
29145         /**
29146          * @event afterupload
29147          * Fire when xhr load exception
29148          * @param {Roo.bootstrap.DocumentManager} this
29149          * @param {XMLHttpRequest} xhr
29150          */
29151         "afterupload" : true,
29152         /**
29153          * @event prepare
29154          * prepare the form data
29155          * @param {Roo.bootstrap.DocumentManager} this
29156          * @param {Object} formData
29157          */
29158         "prepare" : true,
29159         /**
29160          * @event remove
29161          * Fire when remove the file
29162          * @param {Roo.bootstrap.DocumentManager} this
29163          * @param {Object} file
29164          */
29165         "remove" : true,
29166         /**
29167          * @event refresh
29168          * Fire after refresh the file
29169          * @param {Roo.bootstrap.DocumentManager} this
29170          */
29171         "refresh" : true,
29172         /**
29173          * @event click
29174          * Fire after click the image
29175          * @param {Roo.bootstrap.DocumentManager} this
29176          * @param {Object} file
29177          */
29178         "click" : true,
29179         /**
29180          * @event edit
29181          * Fire when upload a image and editable set to true
29182          * @param {Roo.bootstrap.DocumentManager} this
29183          * @param {Object} file
29184          */
29185         "edit" : true,
29186         /**
29187          * @event beforeselectfile
29188          * Fire before select file
29189          * @param {Roo.bootstrap.DocumentManager} this
29190          */
29191         "beforeselectfile" : true,
29192         /**
29193          * @event process
29194          * Fire before process file
29195          * @param {Roo.bootstrap.DocumentManager} this
29196          * @param {Object} file
29197          */
29198         "process" : true,
29199         /**
29200          * @event previewrendered
29201          * Fire when preview rendered
29202          * @param {Roo.bootstrap.DocumentManager} this
29203          * @param {Object} file
29204          */
29205         "previewrendered" : true,
29206         /**
29207          */
29208         "previewResize" : true
29209         
29210     });
29211 };
29212
29213 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
29214     
29215     boxes : 0,
29216     inputName : '',
29217     thumbSize : 300,
29218     multiple : true,
29219     files : false,
29220     method : 'POST',
29221     url : '',
29222     paramName : 'imageUpload',
29223     toolTipName : 'filename',
29224     fieldLabel : '',
29225     labelWidth : 4,
29226     labelAlign : 'left',
29227     editable : true,
29228     delegates : false,
29229     xhr : false, 
29230     
29231     labellg : 0,
29232     labelmd : 0,
29233     labelsm : 0,
29234     labelxs : 0,
29235     
29236     getAutoCreate : function()
29237     {   
29238         var managerWidget = {
29239             tag : 'div',
29240             cls : 'roo-document-manager',
29241             cn : [
29242                 {
29243                     tag : 'input',
29244                     cls : 'roo-document-manager-selector',
29245                     type : 'file'
29246                 },
29247                 {
29248                     tag : 'div',
29249                     cls : 'roo-document-manager-uploader',
29250                     cn : [
29251                         {
29252                             tag : 'div',
29253                             cls : 'roo-document-manager-upload-btn',
29254                             html : '<i class="fa fa-plus"></i>'
29255                         }
29256                     ]
29257                     
29258                 }
29259             ]
29260         };
29261         
29262         var content = [
29263             {
29264                 tag : 'div',
29265                 cls : 'column col-md-12',
29266                 cn : managerWidget
29267             }
29268         ];
29269         
29270         if(this.fieldLabel.length){
29271             
29272             content = [
29273                 {
29274                     tag : 'div',
29275                     cls : 'column col-md-12',
29276                     html : this.fieldLabel
29277                 },
29278                 {
29279                     tag : 'div',
29280                     cls : 'column col-md-12',
29281                     cn : managerWidget
29282                 }
29283             ];
29284
29285             if(this.labelAlign == 'left'){
29286                 content = [
29287                     {
29288                         tag : 'div',
29289                         cls : 'column',
29290                         html : this.fieldLabel
29291                     },
29292                     {
29293                         tag : 'div',
29294                         cls : 'column',
29295                         cn : managerWidget
29296                     }
29297                 ];
29298                 
29299                 if(this.labelWidth > 12){
29300                     content[0].style = "width: " + this.labelWidth + 'px';
29301                 }
29302
29303                 if(this.labelWidth < 13 && this.labelmd == 0){
29304                     this.labelmd = this.labelWidth;
29305                 }
29306
29307                 if(this.labellg > 0){
29308                     content[0].cls += ' col-lg-' + this.labellg;
29309                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29310                 }
29311
29312                 if(this.labelmd > 0){
29313                     content[0].cls += ' col-md-' + this.labelmd;
29314                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29315                 }
29316
29317                 if(this.labelsm > 0){
29318                     content[0].cls += ' col-sm-' + this.labelsm;
29319                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29320                 }
29321
29322                 if(this.labelxs > 0){
29323                     content[0].cls += ' col-xs-' + this.labelxs;
29324                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29325                 }
29326                 
29327             }
29328         }
29329         
29330         var cfg = {
29331             tag : 'div',
29332             cls : 'row clearfix',
29333             cn : content
29334         };
29335         
29336         return cfg;
29337         
29338     },
29339     
29340     initEvents : function()
29341     {
29342         this.managerEl = this.el.select('.roo-document-manager', true).first();
29343         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29344         
29345         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29346         this.selectorEl.hide();
29347         
29348         if(this.multiple){
29349             this.selectorEl.attr('multiple', 'multiple');
29350         }
29351         
29352         this.selectorEl.on('change', this.onFileSelected, this);
29353         
29354         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29355         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29356         
29357         this.uploader.on('click', this.onUploaderClick, this);
29358         
29359         this.renderProgressDialog();
29360         
29361         var _this = this;
29362         
29363         window.addEventListener("resize", function() { _this.refresh(); } );
29364         
29365         this.fireEvent('initial', this);
29366     },
29367     
29368     renderProgressDialog : function()
29369     {
29370         var _this = this;
29371         
29372         this.progressDialog = new Roo.bootstrap.Modal({
29373             cls : 'roo-document-manager-progress-dialog',
29374             allow_close : false,
29375             animate : false,
29376             title : '',
29377             buttons : [
29378                 {
29379                     name  :'cancel',
29380                     weight : 'danger',
29381                     html : 'Cancel'
29382                 }
29383             ], 
29384             listeners : { 
29385                 btnclick : function() {
29386                     _this.uploadCancel();
29387                     this.hide();
29388                 }
29389             }
29390         });
29391          
29392         this.progressDialog.render(Roo.get(document.body));
29393          
29394         this.progress = new Roo.bootstrap.Progress({
29395             cls : 'roo-document-manager-progress',
29396             active : true,
29397             striped : true
29398         });
29399         
29400         this.progress.render(this.progressDialog.getChildContainer());
29401         
29402         this.progressBar = new Roo.bootstrap.ProgressBar({
29403             cls : 'roo-document-manager-progress-bar',
29404             aria_valuenow : 0,
29405             aria_valuemin : 0,
29406             aria_valuemax : 12,
29407             panel : 'success'
29408         });
29409         
29410         this.progressBar.render(this.progress.getChildContainer());
29411     },
29412     
29413     onUploaderClick : function(e)
29414     {
29415         e.preventDefault();
29416      
29417         if(this.fireEvent('beforeselectfile', this) != false){
29418             this.selectorEl.dom.click();
29419         }
29420         
29421     },
29422     
29423     onFileSelected : function(e)
29424     {
29425         e.preventDefault();
29426         
29427         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29428             return;
29429         }
29430         
29431         Roo.each(this.selectorEl.dom.files, function(file){
29432             if(this.fireEvent('inspect', this, file) != false){
29433                 this.files.push(file);
29434             }
29435         }, this);
29436         
29437         this.queue();
29438         
29439     },
29440     
29441     queue : function()
29442     {
29443         this.selectorEl.dom.value = '';
29444         
29445         if(!this.files || !this.files.length){
29446             return;
29447         }
29448         
29449         if(this.boxes > 0 && this.files.length > this.boxes){
29450             this.files = this.files.slice(0, this.boxes);
29451         }
29452         
29453         this.uploader.show();
29454         
29455         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29456             this.uploader.hide();
29457         }
29458         
29459         var _this = this;
29460         
29461         var files = [];
29462         
29463         var docs = [];
29464         
29465         Roo.each(this.files, function(file){
29466             
29467             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29468                 var f = this.renderPreview(file);
29469                 files.push(f);
29470                 return;
29471             }
29472             
29473             if(file.type.indexOf('image') != -1){
29474                 this.delegates.push(
29475                     (function(){
29476                         _this.process(file);
29477                     }).createDelegate(this)
29478                 );
29479         
29480                 return;
29481             }
29482             
29483             docs.push(
29484                 (function(){
29485                     _this.process(file);
29486                 }).createDelegate(this)
29487             );
29488             
29489         }, this);
29490         
29491         this.files = files;
29492         
29493         this.delegates = this.delegates.concat(docs);
29494         
29495         if(!this.delegates.length){
29496             this.refresh();
29497             return;
29498         }
29499         
29500         this.progressBar.aria_valuemax = this.delegates.length;
29501         
29502         this.arrange();
29503         
29504         return;
29505     },
29506     
29507     arrange : function()
29508     {
29509         if(!this.delegates.length){
29510             this.progressDialog.hide();
29511             this.refresh();
29512             return;
29513         }
29514         
29515         var delegate = this.delegates.shift();
29516         
29517         this.progressDialog.show();
29518         
29519         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29520         
29521         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29522         
29523         delegate();
29524     },
29525     
29526     refresh : function()
29527     {
29528         this.uploader.show();
29529         
29530         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29531             this.uploader.hide();
29532         }
29533         
29534         Roo.isTouch ? this.closable(false) : this.closable(true);
29535         
29536         this.fireEvent('refresh', this);
29537     },
29538     
29539     onRemove : function(e, el, o)
29540     {
29541         e.preventDefault();
29542         
29543         this.fireEvent('remove', this, o);
29544         
29545     },
29546     
29547     remove : function(o)
29548     {
29549         var files = [];
29550         
29551         Roo.each(this.files, function(file){
29552             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29553                 files.push(file);
29554                 return;
29555             }
29556
29557             o.target.remove();
29558
29559         }, this);
29560         
29561         this.files = files;
29562         
29563         this.refresh();
29564     },
29565     
29566     clear : function()
29567     {
29568         Roo.each(this.files, function(file){
29569             if(!file.target){
29570                 return;
29571             }
29572             
29573             file.target.remove();
29574
29575         }, this);
29576         
29577         this.files = [];
29578         
29579         this.refresh();
29580     },
29581     
29582     onClick : function(e, el, o)
29583     {
29584         e.preventDefault();
29585         
29586         this.fireEvent('click', this, o);
29587         
29588     },
29589     
29590     closable : function(closable)
29591     {
29592         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29593             
29594             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29595             
29596             if(closable){
29597                 el.show();
29598                 return;
29599             }
29600             
29601             el.hide();
29602             
29603         }, this);
29604     },
29605     
29606     xhrOnLoad : function(xhr)
29607     {
29608         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29609             el.remove();
29610         }, this);
29611         
29612         if (xhr.readyState !== 4) {
29613             this.arrange();
29614             this.fireEvent('exception', this, xhr);
29615             return;
29616         }
29617
29618         var response = Roo.decode(xhr.responseText);
29619         
29620         if(!response.success){
29621             this.arrange();
29622             this.fireEvent('exception', this, xhr);
29623             return;
29624         }
29625         
29626         var file = this.renderPreview(response.data);
29627         
29628         this.files.push(file);
29629         
29630         this.arrange();
29631         
29632         this.fireEvent('afterupload', this, xhr);
29633         
29634     },
29635     
29636     xhrOnError : function(xhr)
29637     {
29638         Roo.log('xhr on error');
29639         
29640         var response = Roo.decode(xhr.responseText);
29641           
29642         Roo.log(response);
29643         
29644         this.arrange();
29645     },
29646     
29647     process : function(file)
29648     {
29649         if(this.fireEvent('process', this, file) !== false){
29650             if(this.editable && file.type.indexOf('image') != -1){
29651                 this.fireEvent('edit', this, file);
29652                 return;
29653             }
29654
29655             this.uploadStart(file, false);
29656
29657             return;
29658         }
29659         
29660     },
29661     
29662     uploadStart : function(file, crop)
29663     {
29664         this.xhr = new XMLHttpRequest();
29665         
29666         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29667             this.arrange();
29668             return;
29669         }
29670         
29671         file.xhr = this.xhr;
29672             
29673         this.managerEl.createChild({
29674             tag : 'div',
29675             cls : 'roo-document-manager-loading',
29676             cn : [
29677                 {
29678                     tag : 'div',
29679                     tooltip : file.name,
29680                     cls : 'roo-document-manager-thumb',
29681                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29682                 }
29683             ]
29684
29685         });
29686
29687         this.xhr.open(this.method, this.url, true);
29688         
29689         var headers = {
29690             "Accept": "application/json",
29691             "Cache-Control": "no-cache",
29692             "X-Requested-With": "XMLHttpRequest"
29693         };
29694         
29695         for (var headerName in headers) {
29696             var headerValue = headers[headerName];
29697             if (headerValue) {
29698                 this.xhr.setRequestHeader(headerName, headerValue);
29699             }
29700         }
29701         
29702         var _this = this;
29703         
29704         this.xhr.onload = function()
29705         {
29706             _this.xhrOnLoad(_this.xhr);
29707         }
29708         
29709         this.xhr.onerror = function()
29710         {
29711             _this.xhrOnError(_this.xhr);
29712         }
29713         
29714         var formData = new FormData();
29715
29716         formData.append('returnHTML', 'NO');
29717         
29718         if(crop){
29719             formData.append('crop', crop);
29720         }
29721         
29722         formData.append(this.paramName, file, file.name);
29723         
29724         var options = {
29725             file : file, 
29726             manually : false
29727         };
29728         
29729         if(this.fireEvent('prepare', this, formData, options) != false){
29730             
29731             if(options.manually){
29732                 return;
29733             }
29734             
29735             this.xhr.send(formData);
29736             return;
29737         };
29738         
29739         this.uploadCancel();
29740     },
29741     
29742     uploadCancel : function()
29743     {
29744         if (this.xhr) {
29745             this.xhr.abort();
29746         }
29747         
29748         this.delegates = [];
29749         
29750         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29751             el.remove();
29752         }, this);
29753         
29754         this.arrange();
29755     },
29756     
29757     renderPreview : function(file)
29758     {
29759         if(typeof(file.target) != 'undefined' && file.target){
29760             return file;
29761         }
29762         
29763         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29764         
29765         var previewEl = this.managerEl.createChild({
29766             tag : 'div',
29767             cls : 'roo-document-manager-preview',
29768             cn : [
29769                 {
29770                     tag : 'div',
29771                     tooltip : file[this.toolTipName],
29772                     cls : 'roo-document-manager-thumb',
29773                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29774                 },
29775                 {
29776                     tag : 'button',
29777                     cls : 'close',
29778                     html : '<i class="fa fa-times-circle"></i>'
29779                 }
29780             ]
29781         });
29782
29783         var close = previewEl.select('button.close', true).first();
29784
29785         close.on('click', this.onRemove, this, file);
29786
29787         file.target = previewEl;
29788
29789         var image = previewEl.select('img', true).first();
29790         
29791         var _this = this;
29792         
29793         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29794         
29795         image.on('click', this.onClick, this, file);
29796         
29797         this.fireEvent('previewrendered', this, file);
29798         
29799         return file;
29800         
29801     },
29802     
29803     onPreviewLoad : function(file, image)
29804     {
29805         if(typeof(file.target) == 'undefined' || !file.target){
29806             return;
29807         }
29808         
29809         var width = image.dom.naturalWidth || image.dom.width;
29810         var height = image.dom.naturalHeight || image.dom.height;
29811         
29812         if(!this.previewResize) {
29813             return;
29814         }
29815         
29816         if(width > height){
29817             file.target.addClass('wide');
29818             return;
29819         }
29820         
29821         file.target.addClass('tall');
29822         return;
29823         
29824     },
29825     
29826     uploadFromSource : function(file, crop)
29827     {
29828         this.xhr = new XMLHttpRequest();
29829         
29830         this.managerEl.createChild({
29831             tag : 'div',
29832             cls : 'roo-document-manager-loading',
29833             cn : [
29834                 {
29835                     tag : 'div',
29836                     tooltip : file.name,
29837                     cls : 'roo-document-manager-thumb',
29838                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29839                 }
29840             ]
29841
29842         });
29843
29844         this.xhr.open(this.method, this.url, true);
29845         
29846         var headers = {
29847             "Accept": "application/json",
29848             "Cache-Control": "no-cache",
29849             "X-Requested-With": "XMLHttpRequest"
29850         };
29851         
29852         for (var headerName in headers) {
29853             var headerValue = headers[headerName];
29854             if (headerValue) {
29855                 this.xhr.setRequestHeader(headerName, headerValue);
29856             }
29857         }
29858         
29859         var _this = this;
29860         
29861         this.xhr.onload = function()
29862         {
29863             _this.xhrOnLoad(_this.xhr);
29864         }
29865         
29866         this.xhr.onerror = function()
29867         {
29868             _this.xhrOnError(_this.xhr);
29869         }
29870         
29871         var formData = new FormData();
29872
29873         formData.append('returnHTML', 'NO');
29874         
29875         formData.append('crop', crop);
29876         
29877         if(typeof(file.filename) != 'undefined'){
29878             formData.append('filename', file.filename);
29879         }
29880         
29881         if(typeof(file.mimetype) != 'undefined'){
29882             formData.append('mimetype', file.mimetype);
29883         }
29884         
29885         Roo.log(formData);
29886         
29887         if(this.fireEvent('prepare', this, formData) != false){
29888             this.xhr.send(formData);
29889         };
29890     }
29891 });
29892
29893 /*
29894 * Licence: LGPL
29895 */
29896
29897 /**
29898  * @class Roo.bootstrap.DocumentViewer
29899  * @extends Roo.bootstrap.Component
29900  * Bootstrap DocumentViewer class
29901  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29902  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29903  * 
29904  * @constructor
29905  * Create a new DocumentViewer
29906  * @param {Object} config The config object
29907  */
29908
29909 Roo.bootstrap.DocumentViewer = function(config){
29910     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29911     
29912     this.addEvents({
29913         /**
29914          * @event initial
29915          * Fire after initEvent
29916          * @param {Roo.bootstrap.DocumentViewer} this
29917          */
29918         "initial" : true,
29919         /**
29920          * @event click
29921          * Fire after click
29922          * @param {Roo.bootstrap.DocumentViewer} this
29923          */
29924         "click" : true,
29925         /**
29926          * @event download
29927          * Fire after download button
29928          * @param {Roo.bootstrap.DocumentViewer} this
29929          */
29930         "download" : true,
29931         /**
29932          * @event trash
29933          * Fire after trash button
29934          * @param {Roo.bootstrap.DocumentViewer} this
29935          */
29936         "trash" : true
29937         
29938     });
29939 };
29940
29941 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29942     
29943     showDownload : true,
29944     
29945     showTrash : true,
29946     
29947     getAutoCreate : function()
29948     {
29949         var cfg = {
29950             tag : 'div',
29951             cls : 'roo-document-viewer',
29952             cn : [
29953                 {
29954                     tag : 'div',
29955                     cls : 'roo-document-viewer-body',
29956                     cn : [
29957                         {
29958                             tag : 'div',
29959                             cls : 'roo-document-viewer-thumb',
29960                             cn : [
29961                                 {
29962                                     tag : 'img',
29963                                     cls : 'roo-document-viewer-image'
29964                                 }
29965                             ]
29966                         }
29967                     ]
29968                 },
29969                 {
29970                     tag : 'div',
29971                     cls : 'roo-document-viewer-footer',
29972                     cn : {
29973                         tag : 'div',
29974                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29975                         cn : [
29976                             {
29977                                 tag : 'div',
29978                                 cls : 'btn-group roo-document-viewer-download',
29979                                 cn : [
29980                                     {
29981                                         tag : 'button',
29982                                         cls : 'btn btn-default',
29983                                         html : '<i class="fa fa-download"></i>'
29984                                     }
29985                                 ]
29986                             },
29987                             {
29988                                 tag : 'div',
29989                                 cls : 'btn-group roo-document-viewer-trash',
29990                                 cn : [
29991                                     {
29992                                         tag : 'button',
29993                                         cls : 'btn btn-default',
29994                                         html : '<i class="fa fa-trash"></i>'
29995                                     }
29996                                 ]
29997                             }
29998                         ]
29999                     }
30000                 }
30001             ]
30002         };
30003         
30004         return cfg;
30005     },
30006     
30007     initEvents : function()
30008     {
30009         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
30010         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
30011         
30012         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
30013         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
30014         
30015         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
30016         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
30017         
30018         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
30019         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
30020         
30021         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
30022         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
30023         
30024         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
30025         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
30026         
30027         this.bodyEl.on('click', this.onClick, this);
30028         this.downloadBtn.on('click', this.onDownload, this);
30029         this.trashBtn.on('click', this.onTrash, this);
30030         
30031         this.downloadBtn.hide();
30032         this.trashBtn.hide();
30033         
30034         if(this.showDownload){
30035             this.downloadBtn.show();
30036         }
30037         
30038         if(this.showTrash){
30039             this.trashBtn.show();
30040         }
30041         
30042         if(!this.showDownload && !this.showTrash) {
30043             this.footerEl.hide();
30044         }
30045         
30046     },
30047     
30048     initial : function()
30049     {
30050         this.fireEvent('initial', this);
30051         
30052     },
30053     
30054     onClick : function(e)
30055     {
30056         e.preventDefault();
30057         
30058         this.fireEvent('click', this);
30059     },
30060     
30061     onDownload : function(e)
30062     {
30063         e.preventDefault();
30064         
30065         this.fireEvent('download', this);
30066     },
30067     
30068     onTrash : function(e)
30069     {
30070         e.preventDefault();
30071         
30072         this.fireEvent('trash', this);
30073     }
30074     
30075 });
30076 /*
30077  * - LGPL
30078  *
30079  * nav progress bar
30080  * 
30081  */
30082
30083 /**
30084  * @class Roo.bootstrap.NavProgressBar
30085  * @extends Roo.bootstrap.Component
30086  * Bootstrap NavProgressBar class
30087  * 
30088  * @constructor
30089  * Create a new nav progress bar
30090  * @param {Object} config The config object
30091  */
30092
30093 Roo.bootstrap.NavProgressBar = function(config){
30094     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
30095
30096     this.bullets = this.bullets || [];
30097    
30098 //    Roo.bootstrap.NavProgressBar.register(this);
30099      this.addEvents({
30100         /**
30101              * @event changed
30102              * Fires when the active item changes
30103              * @param {Roo.bootstrap.NavProgressBar} this
30104              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
30105              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
30106          */
30107         'changed': true
30108      });
30109     
30110 };
30111
30112 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
30113     
30114     bullets : [],
30115     barItems : [],
30116     
30117     getAutoCreate : function()
30118     {
30119         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
30120         
30121         cfg = {
30122             tag : 'div',
30123             cls : 'roo-navigation-bar-group',
30124             cn : [
30125                 {
30126                     tag : 'div',
30127                     cls : 'roo-navigation-top-bar'
30128                 },
30129                 {
30130                     tag : 'div',
30131                     cls : 'roo-navigation-bullets-bar',
30132                     cn : [
30133                         {
30134                             tag : 'ul',
30135                             cls : 'roo-navigation-bar'
30136                         }
30137                     ]
30138                 },
30139                 
30140                 {
30141                     tag : 'div',
30142                     cls : 'roo-navigation-bottom-bar'
30143                 }
30144             ]
30145             
30146         };
30147         
30148         return cfg;
30149         
30150     },
30151     
30152     initEvents: function() 
30153     {
30154         
30155     },
30156     
30157     onRender : function(ct, position) 
30158     {
30159         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30160         
30161         if(this.bullets.length){
30162             Roo.each(this.bullets, function(b){
30163                this.addItem(b);
30164             }, this);
30165         }
30166         
30167         this.format();
30168         
30169     },
30170     
30171     addItem : function(cfg)
30172     {
30173         var item = new Roo.bootstrap.NavProgressItem(cfg);
30174         
30175         item.parentId = this.id;
30176         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
30177         
30178         if(cfg.html){
30179             var top = new Roo.bootstrap.Element({
30180                 tag : 'div',
30181                 cls : 'roo-navigation-bar-text'
30182             });
30183             
30184             var bottom = new Roo.bootstrap.Element({
30185                 tag : 'div',
30186                 cls : 'roo-navigation-bar-text'
30187             });
30188             
30189             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
30190             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
30191             
30192             var topText = new Roo.bootstrap.Element({
30193                 tag : 'span',
30194                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
30195             });
30196             
30197             var bottomText = new Roo.bootstrap.Element({
30198                 tag : 'span',
30199                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30200             });
30201             
30202             topText.onRender(top.el, null);
30203             bottomText.onRender(bottom.el, null);
30204             
30205             item.topEl = top;
30206             item.bottomEl = bottom;
30207         }
30208         
30209         this.barItems.push(item);
30210         
30211         return item;
30212     },
30213     
30214     getActive : function()
30215     {
30216         var active = false;
30217         
30218         Roo.each(this.barItems, function(v){
30219             
30220             if (!v.isActive()) {
30221                 return;
30222             }
30223             
30224             active = v;
30225             return false;
30226             
30227         });
30228         
30229         return active;
30230     },
30231     
30232     setActiveItem : function(item)
30233     {
30234         var prev = false;
30235         
30236         Roo.each(this.barItems, function(v){
30237             if (v.rid == item.rid) {
30238                 return ;
30239             }
30240             
30241             if (v.isActive()) {
30242                 v.setActive(false);
30243                 prev = v;
30244             }
30245         });
30246
30247         item.setActive(true);
30248         
30249         this.fireEvent('changed', this, item, prev);
30250     },
30251     
30252     getBarItem: function(rid)
30253     {
30254         var ret = false;
30255         
30256         Roo.each(this.barItems, function(e) {
30257             if (e.rid != rid) {
30258                 return;
30259             }
30260             
30261             ret =  e;
30262             return false;
30263         });
30264         
30265         return ret;
30266     },
30267     
30268     indexOfItem : function(item)
30269     {
30270         var index = false;
30271         
30272         Roo.each(this.barItems, function(v, i){
30273             
30274             if (v.rid != item.rid) {
30275                 return;
30276             }
30277             
30278             index = i;
30279             return false
30280         });
30281         
30282         return index;
30283     },
30284     
30285     setActiveNext : function()
30286     {
30287         var i = this.indexOfItem(this.getActive());
30288         
30289         if (i > this.barItems.length) {
30290             return;
30291         }
30292         
30293         this.setActiveItem(this.barItems[i+1]);
30294     },
30295     
30296     setActivePrev : function()
30297     {
30298         var i = this.indexOfItem(this.getActive());
30299         
30300         if (i  < 1) {
30301             return;
30302         }
30303         
30304         this.setActiveItem(this.barItems[i-1]);
30305     },
30306     
30307     format : function()
30308     {
30309         if(!this.barItems.length){
30310             return;
30311         }
30312      
30313         var width = 100 / this.barItems.length;
30314         
30315         Roo.each(this.barItems, function(i){
30316             i.el.setStyle('width', width + '%');
30317             i.topEl.el.setStyle('width', width + '%');
30318             i.bottomEl.el.setStyle('width', width + '%');
30319         }, this);
30320         
30321     }
30322     
30323 });
30324 /*
30325  * - LGPL
30326  *
30327  * Nav Progress Item
30328  * 
30329  */
30330
30331 /**
30332  * @class Roo.bootstrap.NavProgressItem
30333  * @extends Roo.bootstrap.Component
30334  * Bootstrap NavProgressItem class
30335  * @cfg {String} rid the reference id
30336  * @cfg {Boolean} active (true|false) Is item active default false
30337  * @cfg {Boolean} disabled (true|false) Is item active default false
30338  * @cfg {String} html
30339  * @cfg {String} position (top|bottom) text position default bottom
30340  * @cfg {String} icon show icon instead of number
30341  * 
30342  * @constructor
30343  * Create a new NavProgressItem
30344  * @param {Object} config The config object
30345  */
30346 Roo.bootstrap.NavProgressItem = function(config){
30347     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30348     this.addEvents({
30349         // raw events
30350         /**
30351          * @event click
30352          * The raw click event for the entire grid.
30353          * @param {Roo.bootstrap.NavProgressItem} this
30354          * @param {Roo.EventObject} e
30355          */
30356         "click" : true
30357     });
30358    
30359 };
30360
30361 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30362     
30363     rid : '',
30364     active : false,
30365     disabled : false,
30366     html : '',
30367     position : 'bottom',
30368     icon : false,
30369     
30370     getAutoCreate : function()
30371     {
30372         var iconCls = 'roo-navigation-bar-item-icon';
30373         
30374         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30375         
30376         var cfg = {
30377             tag: 'li',
30378             cls: 'roo-navigation-bar-item',
30379             cn : [
30380                 {
30381                     tag : 'i',
30382                     cls : iconCls
30383                 }
30384             ]
30385         };
30386         
30387         if(this.active){
30388             cfg.cls += ' active';
30389         }
30390         if(this.disabled){
30391             cfg.cls += ' disabled';
30392         }
30393         
30394         return cfg;
30395     },
30396     
30397     disable : function()
30398     {
30399         this.setDisabled(true);
30400     },
30401     
30402     enable : function()
30403     {
30404         this.setDisabled(false);
30405     },
30406     
30407     initEvents: function() 
30408     {
30409         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30410         
30411         this.iconEl.on('click', this.onClick, this);
30412     },
30413     
30414     onClick : function(e)
30415     {
30416         e.preventDefault();
30417         
30418         if(this.disabled){
30419             return;
30420         }
30421         
30422         if(this.fireEvent('click', this, e) === false){
30423             return;
30424         };
30425         
30426         this.parent().setActiveItem(this);
30427     },
30428     
30429     isActive: function () 
30430     {
30431         return this.active;
30432     },
30433     
30434     setActive : function(state)
30435     {
30436         if(this.active == state){
30437             return;
30438         }
30439         
30440         this.active = state;
30441         
30442         if (state) {
30443             this.el.addClass('active');
30444             return;
30445         }
30446         
30447         this.el.removeClass('active');
30448         
30449         return;
30450     },
30451     
30452     setDisabled : function(state)
30453     {
30454         if(this.disabled == state){
30455             return;
30456         }
30457         
30458         this.disabled = state;
30459         
30460         if (state) {
30461             this.el.addClass('disabled');
30462             return;
30463         }
30464         
30465         this.el.removeClass('disabled');
30466     },
30467     
30468     tooltipEl : function()
30469     {
30470         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30471     }
30472 });
30473  
30474
30475  /*
30476  * - LGPL
30477  *
30478  * FieldLabel
30479  * 
30480  */
30481
30482 /**
30483  * @class Roo.bootstrap.FieldLabel
30484  * @extends Roo.bootstrap.Component
30485  * Bootstrap FieldLabel class
30486  * @cfg {String} html contents of the element
30487  * @cfg {String} tag tag of the element default label
30488  * @cfg {String} cls class of the element
30489  * @cfg {String} target label target 
30490  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30491  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
30492  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
30493  * @cfg {String} iconTooltip default "This field is required"
30494  * @cfg {String} indicatorpos (left|right) default left
30495  * 
30496  * @constructor
30497  * Create a new FieldLabel
30498  * @param {Object} config The config object
30499  */
30500
30501 Roo.bootstrap.FieldLabel = function(config){
30502     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30503     
30504     this.addEvents({
30505             /**
30506              * @event invalid
30507              * Fires after the field has been marked as invalid.
30508              * @param {Roo.form.FieldLabel} this
30509              * @param {String} msg The validation message
30510              */
30511             invalid : true,
30512             /**
30513              * @event valid
30514              * Fires after the field has been validated with no errors.
30515              * @param {Roo.form.FieldLabel} this
30516              */
30517             valid : true
30518         });
30519 };
30520
30521 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30522     
30523     tag: 'label',
30524     cls: '',
30525     html: '',
30526     target: '',
30527     allowBlank : true,
30528     invalidClass : 'has-warning',
30529     validClass : 'has-success',
30530     iconTooltip : 'This field is required',
30531     indicatorpos : 'left',
30532     
30533     getAutoCreate : function(){
30534         
30535         var cls = "";
30536         if (!this.allowBlank) {
30537             cls  = "visible";
30538         }
30539         
30540         var cfg = {
30541             tag : this.tag,
30542             cls : 'roo-bootstrap-field-label ' + this.cls,
30543             for : this.target,
30544             cn : [
30545                 {
30546                     tag : 'i',
30547                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30548                     tooltip : this.iconTooltip
30549                 },
30550                 {
30551                     tag : 'span',
30552                     html : this.html
30553                 }
30554             ] 
30555         };
30556         
30557         if(this.indicatorpos == 'right'){
30558             var cfg = {
30559                 tag : this.tag,
30560                 cls : 'roo-bootstrap-field-label ' + this.cls,
30561                 for : this.target,
30562                 cn : [
30563                     {
30564                         tag : 'span',
30565                         html : this.html
30566                     },
30567                     {
30568                         tag : 'i',
30569                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30570                         tooltip : this.iconTooltip
30571                     }
30572                 ] 
30573             };
30574         }
30575         
30576         return cfg;
30577     },
30578     
30579     initEvents: function() 
30580     {
30581         Roo.bootstrap.Element.superclass.initEvents.call(this);
30582         
30583         this.indicator = this.indicatorEl();
30584         
30585         if(this.indicator){
30586             this.indicator.removeClass('visible');
30587             this.indicator.addClass('invisible');
30588         }
30589         
30590         Roo.bootstrap.FieldLabel.register(this);
30591     },
30592     
30593     indicatorEl : function()
30594     {
30595         var indicator = this.el.select('i.roo-required-indicator',true).first();
30596         
30597         if(!indicator){
30598             return false;
30599         }
30600         
30601         return indicator;
30602         
30603     },
30604     
30605     /**
30606      * Mark this field as valid
30607      */
30608     markValid : function()
30609     {
30610         if(this.indicator){
30611             this.indicator.removeClass('visible');
30612             this.indicator.addClass('invisible');
30613         }
30614         if (Roo.bootstrap.version == 3) {
30615             this.el.removeClass(this.invalidClass);
30616             this.el.addClass(this.validClass);
30617         } else {
30618             this.el.removeClass('is-invalid');
30619             this.el.addClass('is-valid');
30620         }
30621         
30622         
30623         this.fireEvent('valid', this);
30624     },
30625     
30626     /**
30627      * Mark this field as invalid
30628      * @param {String} msg The validation message
30629      */
30630     markInvalid : function(msg)
30631     {
30632         if(this.indicator){
30633             this.indicator.removeClass('invisible');
30634             this.indicator.addClass('visible');
30635         }
30636           if (Roo.bootstrap.version == 3) {
30637             this.el.removeClass(this.validClass);
30638             this.el.addClass(this.invalidClass);
30639         } else {
30640             this.el.removeClass('is-valid');
30641             this.el.addClass('is-invalid');
30642         }
30643         
30644         
30645         this.fireEvent('invalid', this, msg);
30646     }
30647     
30648    
30649 });
30650
30651 Roo.apply(Roo.bootstrap.FieldLabel, {
30652     
30653     groups: {},
30654     
30655      /**
30656     * register a FieldLabel Group
30657     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30658     */
30659     register : function(label)
30660     {
30661         if(this.groups.hasOwnProperty(label.target)){
30662             return;
30663         }
30664      
30665         this.groups[label.target] = label;
30666         
30667     },
30668     /**
30669     * fetch a FieldLabel Group based on the target
30670     * @param {string} target
30671     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30672     */
30673     get: function(target) {
30674         if (typeof(this.groups[target]) == 'undefined') {
30675             return false;
30676         }
30677         
30678         return this.groups[target] ;
30679     }
30680 });
30681
30682  
30683
30684  /*
30685  * - LGPL
30686  *
30687  * page DateSplitField.
30688  * 
30689  */
30690
30691
30692 /**
30693  * @class Roo.bootstrap.DateSplitField
30694  * @extends Roo.bootstrap.Component
30695  * Bootstrap DateSplitField class
30696  * @cfg {string} fieldLabel - the label associated
30697  * @cfg {Number} labelWidth set the width of label (0-12)
30698  * @cfg {String} labelAlign (top|left)
30699  * @cfg {Boolean} dayAllowBlank (true|false) default false
30700  * @cfg {Boolean} monthAllowBlank (true|false) default false
30701  * @cfg {Boolean} yearAllowBlank (true|false) default false
30702  * @cfg {string} dayPlaceholder 
30703  * @cfg {string} monthPlaceholder
30704  * @cfg {string} yearPlaceholder
30705  * @cfg {string} dayFormat default 'd'
30706  * @cfg {string} monthFormat default 'm'
30707  * @cfg {string} yearFormat default 'Y'
30708  * @cfg {Number} labellg set the width of label (1-12)
30709  * @cfg {Number} labelmd set the width of label (1-12)
30710  * @cfg {Number} labelsm set the width of label (1-12)
30711  * @cfg {Number} labelxs set the width of label (1-12)
30712
30713  *     
30714  * @constructor
30715  * Create a new DateSplitField
30716  * @param {Object} config The config object
30717  */
30718
30719 Roo.bootstrap.DateSplitField = function(config){
30720     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30721     
30722     this.addEvents({
30723         // raw events
30724          /**
30725          * @event years
30726          * getting the data of years
30727          * @param {Roo.bootstrap.DateSplitField} this
30728          * @param {Object} years
30729          */
30730         "years" : true,
30731         /**
30732          * @event days
30733          * getting the data of days
30734          * @param {Roo.bootstrap.DateSplitField} this
30735          * @param {Object} days
30736          */
30737         "days" : true,
30738         /**
30739          * @event invalid
30740          * Fires after the field has been marked as invalid.
30741          * @param {Roo.form.Field} this
30742          * @param {String} msg The validation message
30743          */
30744         invalid : true,
30745        /**
30746          * @event valid
30747          * Fires after the field has been validated with no errors.
30748          * @param {Roo.form.Field} this
30749          */
30750         valid : true
30751     });
30752 };
30753
30754 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30755     
30756     fieldLabel : '',
30757     labelAlign : 'top',
30758     labelWidth : 3,
30759     dayAllowBlank : false,
30760     monthAllowBlank : false,
30761     yearAllowBlank : false,
30762     dayPlaceholder : '',
30763     monthPlaceholder : '',
30764     yearPlaceholder : '',
30765     dayFormat : 'd',
30766     monthFormat : 'm',
30767     yearFormat : 'Y',
30768     isFormField : true,
30769     labellg : 0,
30770     labelmd : 0,
30771     labelsm : 0,
30772     labelxs : 0,
30773     
30774     getAutoCreate : function()
30775     {
30776         var cfg = {
30777             tag : 'div',
30778             cls : 'row roo-date-split-field-group',
30779             cn : [
30780                 {
30781                     tag : 'input',
30782                     type : 'hidden',
30783                     cls : 'form-hidden-field roo-date-split-field-group-value',
30784                     name : this.name
30785                 }
30786             ]
30787         };
30788         
30789         var labelCls = 'col-md-12';
30790         var contentCls = 'col-md-4';
30791         
30792         if(this.fieldLabel){
30793             
30794             var label = {
30795                 tag : 'div',
30796                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30797                 cn : [
30798                     {
30799                         tag : 'label',
30800                         html : this.fieldLabel
30801                     }
30802                 ]
30803             };
30804             
30805             if(this.labelAlign == 'left'){
30806             
30807                 if(this.labelWidth > 12){
30808                     label.style = "width: " + this.labelWidth + 'px';
30809                 }
30810
30811                 if(this.labelWidth < 13 && this.labelmd == 0){
30812                     this.labelmd = this.labelWidth;
30813                 }
30814
30815                 if(this.labellg > 0){
30816                     labelCls = ' col-lg-' + this.labellg;
30817                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30818                 }
30819
30820                 if(this.labelmd > 0){
30821                     labelCls = ' col-md-' + this.labelmd;
30822                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30823                 }
30824
30825                 if(this.labelsm > 0){
30826                     labelCls = ' col-sm-' + this.labelsm;
30827                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30828                 }
30829
30830                 if(this.labelxs > 0){
30831                     labelCls = ' col-xs-' + this.labelxs;
30832                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30833                 }
30834             }
30835             
30836             label.cls += ' ' + labelCls;
30837             
30838             cfg.cn.push(label);
30839         }
30840         
30841         Roo.each(['day', 'month', 'year'], function(t){
30842             cfg.cn.push({
30843                 tag : 'div',
30844                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30845             });
30846         }, this);
30847         
30848         return cfg;
30849     },
30850     
30851     inputEl: function ()
30852     {
30853         return this.el.select('.roo-date-split-field-group-value', true).first();
30854     },
30855     
30856     onRender : function(ct, position) 
30857     {
30858         var _this = this;
30859         
30860         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30861         
30862         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30863         
30864         this.dayField = new Roo.bootstrap.ComboBox({
30865             allowBlank : this.dayAllowBlank,
30866             alwaysQuery : true,
30867             displayField : 'value',
30868             editable : false,
30869             fieldLabel : '',
30870             forceSelection : true,
30871             mode : 'local',
30872             placeholder : this.dayPlaceholder,
30873             selectOnFocus : true,
30874             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30875             triggerAction : 'all',
30876             typeAhead : true,
30877             valueField : 'value',
30878             store : new Roo.data.SimpleStore({
30879                 data : (function() {    
30880                     var days = [];
30881                     _this.fireEvent('days', _this, days);
30882                     return days;
30883                 })(),
30884                 fields : [ 'value' ]
30885             }),
30886             listeners : {
30887                 select : function (_self, record, index)
30888                 {
30889                     _this.setValue(_this.getValue());
30890                 }
30891             }
30892         });
30893
30894         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30895         
30896         this.monthField = new Roo.bootstrap.MonthField({
30897             after : '<i class=\"fa fa-calendar\"></i>',
30898             allowBlank : this.monthAllowBlank,
30899             placeholder : this.monthPlaceholder,
30900             readOnly : true,
30901             listeners : {
30902                 render : function (_self)
30903                 {
30904                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30905                         e.preventDefault();
30906                         _self.focus();
30907                     });
30908                 },
30909                 select : function (_self, oldvalue, newvalue)
30910                 {
30911                     _this.setValue(_this.getValue());
30912                 }
30913             }
30914         });
30915         
30916         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30917         
30918         this.yearField = new Roo.bootstrap.ComboBox({
30919             allowBlank : this.yearAllowBlank,
30920             alwaysQuery : true,
30921             displayField : 'value',
30922             editable : false,
30923             fieldLabel : '',
30924             forceSelection : true,
30925             mode : 'local',
30926             placeholder : this.yearPlaceholder,
30927             selectOnFocus : true,
30928             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30929             triggerAction : 'all',
30930             typeAhead : true,
30931             valueField : 'value',
30932             store : new Roo.data.SimpleStore({
30933                 data : (function() {
30934                     var years = [];
30935                     _this.fireEvent('years', _this, years);
30936                     return years;
30937                 })(),
30938                 fields : [ 'value' ]
30939             }),
30940             listeners : {
30941                 select : function (_self, record, index)
30942                 {
30943                     _this.setValue(_this.getValue());
30944                 }
30945             }
30946         });
30947
30948         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30949     },
30950     
30951     setValue : function(v, format)
30952     {
30953         this.inputEl.dom.value = v;
30954         
30955         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30956         
30957         var d = Date.parseDate(v, f);
30958         
30959         if(!d){
30960             this.validate();
30961             return;
30962         }
30963         
30964         this.setDay(d.format(this.dayFormat));
30965         this.setMonth(d.format(this.monthFormat));
30966         this.setYear(d.format(this.yearFormat));
30967         
30968         this.validate();
30969         
30970         return;
30971     },
30972     
30973     setDay : function(v)
30974     {
30975         this.dayField.setValue(v);
30976         this.inputEl.dom.value = this.getValue();
30977         this.validate();
30978         return;
30979     },
30980     
30981     setMonth : function(v)
30982     {
30983         this.monthField.setValue(v, true);
30984         this.inputEl.dom.value = this.getValue();
30985         this.validate();
30986         return;
30987     },
30988     
30989     setYear : function(v)
30990     {
30991         this.yearField.setValue(v);
30992         this.inputEl.dom.value = this.getValue();
30993         this.validate();
30994         return;
30995     },
30996     
30997     getDay : function()
30998     {
30999         return this.dayField.getValue();
31000     },
31001     
31002     getMonth : function()
31003     {
31004         return this.monthField.getValue();
31005     },
31006     
31007     getYear : function()
31008     {
31009         return this.yearField.getValue();
31010     },
31011     
31012     getValue : function()
31013     {
31014         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
31015         
31016         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
31017         
31018         return date;
31019     },
31020     
31021     reset : function()
31022     {
31023         this.setDay('');
31024         this.setMonth('');
31025         this.setYear('');
31026         this.inputEl.dom.value = '';
31027         this.validate();
31028         return;
31029     },
31030     
31031     validate : function()
31032     {
31033         var d = this.dayField.validate();
31034         var m = this.monthField.validate();
31035         var y = this.yearField.validate();
31036         
31037         var valid = true;
31038         
31039         if(
31040                 (!this.dayAllowBlank && !d) ||
31041                 (!this.monthAllowBlank && !m) ||
31042                 (!this.yearAllowBlank && !y)
31043         ){
31044             valid = false;
31045         }
31046         
31047         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
31048             return valid;
31049         }
31050         
31051         if(valid){
31052             this.markValid();
31053             return valid;
31054         }
31055         
31056         this.markInvalid();
31057         
31058         return valid;
31059     },
31060     
31061     markValid : function()
31062     {
31063         
31064         var label = this.el.select('label', true).first();
31065         var icon = this.el.select('i.fa-star', true).first();
31066
31067         if(label && icon){
31068             icon.remove();
31069         }
31070         
31071         this.fireEvent('valid', this);
31072     },
31073     
31074      /**
31075      * Mark this field as invalid
31076      * @param {String} msg The validation message
31077      */
31078     markInvalid : function(msg)
31079     {
31080         
31081         var label = this.el.select('label', true).first();
31082         var icon = this.el.select('i.fa-star', true).first();
31083
31084         if(label && !icon){
31085             this.el.select('.roo-date-split-field-label', true).createChild({
31086                 tag : 'i',
31087                 cls : 'text-danger fa fa-lg fa-star',
31088                 tooltip : 'This field is required',
31089                 style : 'margin-right:5px;'
31090             }, label, true);
31091         }
31092         
31093         this.fireEvent('invalid', this, msg);
31094     },
31095     
31096     clearInvalid : function()
31097     {
31098         var label = this.el.select('label', true).first();
31099         var icon = this.el.select('i.fa-star', true).first();
31100
31101         if(label && icon){
31102             icon.remove();
31103         }
31104         
31105         this.fireEvent('valid', this);
31106     },
31107     
31108     getName: function()
31109     {
31110         return this.name;
31111     }
31112     
31113 });
31114
31115  /**
31116  *
31117  * This is based on 
31118  * http://masonry.desandro.com
31119  *
31120  * The idea is to render all the bricks based on vertical width...
31121  *
31122  * The original code extends 'outlayer' - we might need to use that....
31123  * 
31124  */
31125
31126
31127 /**
31128  * @class Roo.bootstrap.LayoutMasonry
31129  * @extends Roo.bootstrap.Component
31130  * Bootstrap Layout Masonry class
31131  * 
31132  * @constructor
31133  * Create a new Element
31134  * @param {Object} config The config object
31135  */
31136
31137 Roo.bootstrap.LayoutMasonry = function(config){
31138     
31139     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
31140     
31141     this.bricks = [];
31142     
31143     Roo.bootstrap.LayoutMasonry.register(this);
31144     
31145     this.addEvents({
31146         // raw events
31147         /**
31148          * @event layout
31149          * Fire after layout the items
31150          * @param {Roo.bootstrap.LayoutMasonry} this
31151          * @param {Roo.EventObject} e
31152          */
31153         "layout" : true
31154     });
31155     
31156 };
31157
31158 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
31159     
31160     /**
31161      * @cfg {Boolean} isLayoutInstant = no animation?
31162      */   
31163     isLayoutInstant : false, // needed?
31164    
31165     /**
31166      * @cfg {Number} boxWidth  width of the columns
31167      */   
31168     boxWidth : 450,
31169     
31170       /**
31171      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
31172      */   
31173     boxHeight : 0,
31174     
31175     /**
31176      * @cfg {Number} padWidth padding below box..
31177      */   
31178     padWidth : 10, 
31179     
31180     /**
31181      * @cfg {Number} gutter gutter width..
31182      */   
31183     gutter : 10,
31184     
31185      /**
31186      * @cfg {Number} maxCols maximum number of columns
31187      */   
31188     
31189     maxCols: 0,
31190     
31191     /**
31192      * @cfg {Boolean} isAutoInitial defalut true
31193      */   
31194     isAutoInitial : true, 
31195     
31196     containerWidth: 0,
31197     
31198     /**
31199      * @cfg {Boolean} isHorizontal defalut false
31200      */   
31201     isHorizontal : false, 
31202
31203     currentSize : null,
31204     
31205     tag: 'div',
31206     
31207     cls: '',
31208     
31209     bricks: null, //CompositeElement
31210     
31211     cols : 1,
31212     
31213     _isLayoutInited : false,
31214     
31215 //    isAlternative : false, // only use for vertical layout...
31216     
31217     /**
31218      * @cfg {Number} alternativePadWidth padding below box..
31219      */   
31220     alternativePadWidth : 50,
31221     
31222     selectedBrick : [],
31223     
31224     getAutoCreate : function(){
31225         
31226         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31227         
31228         var cfg = {
31229             tag: this.tag,
31230             cls: 'blog-masonary-wrapper ' + this.cls,
31231             cn : {
31232                 cls : 'mas-boxes masonary'
31233             }
31234         };
31235         
31236         return cfg;
31237     },
31238     
31239     getChildContainer: function( )
31240     {
31241         if (this.boxesEl) {
31242             return this.boxesEl;
31243         }
31244         
31245         this.boxesEl = this.el.select('.mas-boxes').first();
31246         
31247         return this.boxesEl;
31248     },
31249     
31250     
31251     initEvents : function()
31252     {
31253         var _this = this;
31254         
31255         if(this.isAutoInitial){
31256             Roo.log('hook children rendered');
31257             this.on('childrenrendered', function() {
31258                 Roo.log('children rendered');
31259                 _this.initial();
31260             } ,this);
31261         }
31262     },
31263     
31264     initial : function()
31265     {
31266         this.selectedBrick = [];
31267         
31268         this.currentSize = this.el.getBox(true);
31269         
31270         Roo.EventManager.onWindowResize(this.resize, this); 
31271
31272         if(!this.isAutoInitial){
31273             this.layout();
31274             return;
31275         }
31276         
31277         this.layout();
31278         
31279         return;
31280         //this.layout.defer(500,this);
31281         
31282     },
31283     
31284     resize : function()
31285     {
31286         var cs = this.el.getBox(true);
31287         
31288         if (
31289                 this.currentSize.width == cs.width && 
31290                 this.currentSize.x == cs.x && 
31291                 this.currentSize.height == cs.height && 
31292                 this.currentSize.y == cs.y 
31293         ) {
31294             Roo.log("no change in with or X or Y");
31295             return;
31296         }
31297         
31298         this.currentSize = cs;
31299         
31300         this.layout();
31301         
31302     },
31303     
31304     layout : function()
31305     {   
31306         this._resetLayout();
31307         
31308         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31309         
31310         this.layoutItems( isInstant );
31311       
31312         this._isLayoutInited = true;
31313         
31314         this.fireEvent('layout', this);
31315         
31316     },
31317     
31318     _resetLayout : function()
31319     {
31320         if(this.isHorizontal){
31321             this.horizontalMeasureColumns();
31322             return;
31323         }
31324         
31325         this.verticalMeasureColumns();
31326         
31327     },
31328     
31329     verticalMeasureColumns : function()
31330     {
31331         this.getContainerWidth();
31332         
31333 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31334 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31335 //            return;
31336 //        }
31337         
31338         var boxWidth = this.boxWidth + this.padWidth;
31339         
31340         if(this.containerWidth < this.boxWidth){
31341             boxWidth = this.containerWidth
31342         }
31343         
31344         var containerWidth = this.containerWidth;
31345         
31346         var cols = Math.floor(containerWidth / boxWidth);
31347         
31348         this.cols = Math.max( cols, 1 );
31349         
31350         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31351         
31352         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31353         
31354         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31355         
31356         this.colWidth = boxWidth + avail - this.padWidth;
31357         
31358         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31359         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31360     },
31361     
31362     horizontalMeasureColumns : function()
31363     {
31364         this.getContainerWidth();
31365         
31366         var boxWidth = this.boxWidth;
31367         
31368         if(this.containerWidth < boxWidth){
31369             boxWidth = this.containerWidth;
31370         }
31371         
31372         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31373         
31374         this.el.setHeight(boxWidth);
31375         
31376     },
31377     
31378     getContainerWidth : function()
31379     {
31380         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31381     },
31382     
31383     layoutItems : function( isInstant )
31384     {
31385         Roo.log(this.bricks);
31386         
31387         var items = Roo.apply([], this.bricks);
31388         
31389         if(this.isHorizontal){
31390             this._horizontalLayoutItems( items , isInstant );
31391             return;
31392         }
31393         
31394 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31395 //            this._verticalAlternativeLayoutItems( items , isInstant );
31396 //            return;
31397 //        }
31398         
31399         this._verticalLayoutItems( items , isInstant );
31400         
31401     },
31402     
31403     _verticalLayoutItems : function ( items , isInstant)
31404     {
31405         if ( !items || !items.length ) {
31406             return;
31407         }
31408         
31409         var standard = [
31410             ['xs', 'xs', 'xs', 'tall'],
31411             ['xs', 'xs', 'tall'],
31412             ['xs', 'xs', 'sm'],
31413             ['xs', 'xs', 'xs'],
31414             ['xs', 'tall'],
31415             ['xs', 'sm'],
31416             ['xs', 'xs'],
31417             ['xs'],
31418             
31419             ['sm', 'xs', 'xs'],
31420             ['sm', 'xs'],
31421             ['sm'],
31422             
31423             ['tall', 'xs', 'xs', 'xs'],
31424             ['tall', 'xs', 'xs'],
31425             ['tall', 'xs'],
31426             ['tall']
31427             
31428         ];
31429         
31430         var queue = [];
31431         
31432         var boxes = [];
31433         
31434         var box = [];
31435         
31436         Roo.each(items, function(item, k){
31437             
31438             switch (item.size) {
31439                 // these layouts take up a full box,
31440                 case 'md' :
31441                 case 'md-left' :
31442                 case 'md-right' :
31443                 case 'wide' :
31444                     
31445                     if(box.length){
31446                         boxes.push(box);
31447                         box = [];
31448                     }
31449                     
31450                     boxes.push([item]);
31451                     
31452                     break;
31453                     
31454                 case 'xs' :
31455                 case 'sm' :
31456                 case 'tall' :
31457                     
31458                     box.push(item);
31459                     
31460                     break;
31461                 default :
31462                     break;
31463                     
31464             }
31465             
31466         }, this);
31467         
31468         if(box.length){
31469             boxes.push(box);
31470             box = [];
31471         }
31472         
31473         var filterPattern = function(box, length)
31474         {
31475             if(!box.length){
31476                 return;
31477             }
31478             
31479             var match = false;
31480             
31481             var pattern = box.slice(0, length);
31482             
31483             var format = [];
31484             
31485             Roo.each(pattern, function(i){
31486                 format.push(i.size);
31487             }, this);
31488             
31489             Roo.each(standard, function(s){
31490                 
31491                 if(String(s) != String(format)){
31492                     return;
31493                 }
31494                 
31495                 match = true;
31496                 return false;
31497                 
31498             }, this);
31499             
31500             if(!match && length == 1){
31501                 return;
31502             }
31503             
31504             if(!match){
31505                 filterPattern(box, length - 1);
31506                 return;
31507             }
31508                 
31509             queue.push(pattern);
31510
31511             box = box.slice(length, box.length);
31512
31513             filterPattern(box, 4);
31514
31515             return;
31516             
31517         }
31518         
31519         Roo.each(boxes, function(box, k){
31520             
31521             if(!box.length){
31522                 return;
31523             }
31524             
31525             if(box.length == 1){
31526                 queue.push(box);
31527                 return;
31528             }
31529             
31530             filterPattern(box, 4);
31531             
31532         }, this);
31533         
31534         this._processVerticalLayoutQueue( queue, isInstant );
31535         
31536     },
31537     
31538 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31539 //    {
31540 //        if ( !items || !items.length ) {
31541 //            return;
31542 //        }
31543 //
31544 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31545 //        
31546 //    },
31547     
31548     _horizontalLayoutItems : function ( items , isInstant)
31549     {
31550         if ( !items || !items.length || items.length < 3) {
31551             return;
31552         }
31553         
31554         items.reverse();
31555         
31556         var eItems = items.slice(0, 3);
31557         
31558         items = items.slice(3, items.length);
31559         
31560         var standard = [
31561             ['xs', 'xs', 'xs', 'wide'],
31562             ['xs', 'xs', 'wide'],
31563             ['xs', 'xs', 'sm'],
31564             ['xs', 'xs', 'xs'],
31565             ['xs', 'wide'],
31566             ['xs', 'sm'],
31567             ['xs', 'xs'],
31568             ['xs'],
31569             
31570             ['sm', 'xs', 'xs'],
31571             ['sm', 'xs'],
31572             ['sm'],
31573             
31574             ['wide', 'xs', 'xs', 'xs'],
31575             ['wide', 'xs', 'xs'],
31576             ['wide', 'xs'],
31577             ['wide'],
31578             
31579             ['wide-thin']
31580         ];
31581         
31582         var queue = [];
31583         
31584         var boxes = [];
31585         
31586         var box = [];
31587         
31588         Roo.each(items, function(item, k){
31589             
31590             switch (item.size) {
31591                 case 'md' :
31592                 case 'md-left' :
31593                 case 'md-right' :
31594                 case 'tall' :
31595                     
31596                     if(box.length){
31597                         boxes.push(box);
31598                         box = [];
31599                     }
31600                     
31601                     boxes.push([item]);
31602                     
31603                     break;
31604                     
31605                 case 'xs' :
31606                 case 'sm' :
31607                 case 'wide' :
31608                 case 'wide-thin' :
31609                     
31610                     box.push(item);
31611                     
31612                     break;
31613                 default :
31614                     break;
31615                     
31616             }
31617             
31618         }, this);
31619         
31620         if(box.length){
31621             boxes.push(box);
31622             box = [];
31623         }
31624         
31625         var filterPattern = function(box, length)
31626         {
31627             if(!box.length){
31628                 return;
31629             }
31630             
31631             var match = false;
31632             
31633             var pattern = box.slice(0, length);
31634             
31635             var format = [];
31636             
31637             Roo.each(pattern, function(i){
31638                 format.push(i.size);
31639             }, this);
31640             
31641             Roo.each(standard, function(s){
31642                 
31643                 if(String(s) != String(format)){
31644                     return;
31645                 }
31646                 
31647                 match = true;
31648                 return false;
31649                 
31650             }, this);
31651             
31652             if(!match && length == 1){
31653                 return;
31654             }
31655             
31656             if(!match){
31657                 filterPattern(box, length - 1);
31658                 return;
31659             }
31660                 
31661             queue.push(pattern);
31662
31663             box = box.slice(length, box.length);
31664
31665             filterPattern(box, 4);
31666
31667             return;
31668             
31669         }
31670         
31671         Roo.each(boxes, function(box, k){
31672             
31673             if(!box.length){
31674                 return;
31675             }
31676             
31677             if(box.length == 1){
31678                 queue.push(box);
31679                 return;
31680             }
31681             
31682             filterPattern(box, 4);
31683             
31684         }, this);
31685         
31686         
31687         var prune = [];
31688         
31689         var pos = this.el.getBox(true);
31690         
31691         var minX = pos.x;
31692         
31693         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31694         
31695         var hit_end = false;
31696         
31697         Roo.each(queue, function(box){
31698             
31699             if(hit_end){
31700                 
31701                 Roo.each(box, function(b){
31702                 
31703                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31704                     b.el.hide();
31705
31706                 }, this);
31707
31708                 return;
31709             }
31710             
31711             var mx = 0;
31712             
31713             Roo.each(box, function(b){
31714                 
31715                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31716                 b.el.show();
31717
31718                 mx = Math.max(mx, b.x);
31719                 
31720             }, this);
31721             
31722             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31723             
31724             if(maxX < minX){
31725                 
31726                 Roo.each(box, function(b){
31727                 
31728                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31729                     b.el.hide();
31730                     
31731                 }, this);
31732                 
31733                 hit_end = true;
31734                 
31735                 return;
31736             }
31737             
31738             prune.push(box);
31739             
31740         }, this);
31741         
31742         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31743     },
31744     
31745     /** Sets position of item in DOM
31746     * @param {Element} item
31747     * @param {Number} x - horizontal position
31748     * @param {Number} y - vertical position
31749     * @param {Boolean} isInstant - disables transitions
31750     */
31751     _processVerticalLayoutQueue : function( queue, isInstant )
31752     {
31753         var pos = this.el.getBox(true);
31754         var x = pos.x;
31755         var y = pos.y;
31756         var maxY = [];
31757         
31758         for (var i = 0; i < this.cols; i++){
31759             maxY[i] = pos.y;
31760         }
31761         
31762         Roo.each(queue, function(box, k){
31763             
31764             var col = k % this.cols;
31765             
31766             Roo.each(box, function(b,kk){
31767                 
31768                 b.el.position('absolute');
31769                 
31770                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31771                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31772                 
31773                 if(b.size == 'md-left' || b.size == 'md-right'){
31774                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31775                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31776                 }
31777                 
31778                 b.el.setWidth(width);
31779                 b.el.setHeight(height);
31780                 // iframe?
31781                 b.el.select('iframe',true).setSize(width,height);
31782                 
31783             }, this);
31784             
31785             for (var i = 0; i < this.cols; i++){
31786                 
31787                 if(maxY[i] < maxY[col]){
31788                     col = i;
31789                     continue;
31790                 }
31791                 
31792                 col = Math.min(col, i);
31793                 
31794             }
31795             
31796             x = pos.x + col * (this.colWidth + this.padWidth);
31797             
31798             y = maxY[col];
31799             
31800             var positions = [];
31801             
31802             switch (box.length){
31803                 case 1 :
31804                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31805                     break;
31806                 case 2 :
31807                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31808                     break;
31809                 case 3 :
31810                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31811                     break;
31812                 case 4 :
31813                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31814                     break;
31815                 default :
31816                     break;
31817             }
31818             
31819             Roo.each(box, function(b,kk){
31820                 
31821                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31822                 
31823                 var sz = b.el.getSize();
31824                 
31825                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31826                 
31827             }, this);
31828             
31829         }, this);
31830         
31831         var mY = 0;
31832         
31833         for (var i = 0; i < this.cols; i++){
31834             mY = Math.max(mY, maxY[i]);
31835         }
31836         
31837         this.el.setHeight(mY - pos.y);
31838         
31839     },
31840     
31841 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31842 //    {
31843 //        var pos = this.el.getBox(true);
31844 //        var x = pos.x;
31845 //        var y = pos.y;
31846 //        var maxX = pos.right;
31847 //        
31848 //        var maxHeight = 0;
31849 //        
31850 //        Roo.each(items, function(item, k){
31851 //            
31852 //            var c = k % 2;
31853 //            
31854 //            item.el.position('absolute');
31855 //                
31856 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31857 //
31858 //            item.el.setWidth(width);
31859 //
31860 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31861 //
31862 //            item.el.setHeight(height);
31863 //            
31864 //            if(c == 0){
31865 //                item.el.setXY([x, y], isInstant ? false : true);
31866 //            } else {
31867 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31868 //            }
31869 //            
31870 //            y = y + height + this.alternativePadWidth;
31871 //            
31872 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31873 //            
31874 //        }, this);
31875 //        
31876 //        this.el.setHeight(maxHeight);
31877 //        
31878 //    },
31879     
31880     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31881     {
31882         var pos = this.el.getBox(true);
31883         
31884         var minX = pos.x;
31885         var minY = pos.y;
31886         
31887         var maxX = pos.right;
31888         
31889         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31890         
31891         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31892         
31893         Roo.each(queue, function(box, k){
31894             
31895             Roo.each(box, function(b, kk){
31896                 
31897                 b.el.position('absolute');
31898                 
31899                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31900                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31901                 
31902                 if(b.size == 'md-left' || b.size == 'md-right'){
31903                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31904                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31905                 }
31906                 
31907                 b.el.setWidth(width);
31908                 b.el.setHeight(height);
31909                 
31910             }, this);
31911             
31912             if(!box.length){
31913                 return;
31914             }
31915             
31916             var positions = [];
31917             
31918             switch (box.length){
31919                 case 1 :
31920                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31921                     break;
31922                 case 2 :
31923                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31924                     break;
31925                 case 3 :
31926                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31927                     break;
31928                 case 4 :
31929                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31930                     break;
31931                 default :
31932                     break;
31933             }
31934             
31935             Roo.each(box, function(b,kk){
31936                 
31937                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31938                 
31939                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31940                 
31941             }, this);
31942             
31943         }, this);
31944         
31945     },
31946     
31947     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31948     {
31949         Roo.each(eItems, function(b,k){
31950             
31951             b.size = (k == 0) ? 'sm' : 'xs';
31952             b.x = (k == 0) ? 2 : 1;
31953             b.y = (k == 0) ? 2 : 1;
31954             
31955             b.el.position('absolute');
31956             
31957             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31958                 
31959             b.el.setWidth(width);
31960             
31961             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31962             
31963             b.el.setHeight(height);
31964             
31965         }, this);
31966
31967         var positions = [];
31968         
31969         positions.push({
31970             x : maxX - this.unitWidth * 2 - this.gutter,
31971             y : minY
31972         });
31973         
31974         positions.push({
31975             x : maxX - this.unitWidth,
31976             y : minY + (this.unitWidth + this.gutter) * 2
31977         });
31978         
31979         positions.push({
31980             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31981             y : minY
31982         });
31983         
31984         Roo.each(eItems, function(b,k){
31985             
31986             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31987
31988         }, this);
31989         
31990     },
31991     
31992     getVerticalOneBoxColPositions : function(x, y, box)
31993     {
31994         var pos = [];
31995         
31996         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31997         
31998         if(box[0].size == 'md-left'){
31999             rand = 0;
32000         }
32001         
32002         if(box[0].size == 'md-right'){
32003             rand = 1;
32004         }
32005         
32006         pos.push({
32007             x : x + (this.unitWidth + this.gutter) * rand,
32008             y : y
32009         });
32010         
32011         return pos;
32012     },
32013     
32014     getVerticalTwoBoxColPositions : function(x, y, box)
32015     {
32016         var pos = [];
32017         
32018         if(box[0].size == 'xs'){
32019             
32020             pos.push({
32021                 x : x,
32022                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
32023             });
32024
32025             pos.push({
32026                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
32027                 y : y
32028             });
32029             
32030             return pos;
32031             
32032         }
32033         
32034         pos.push({
32035             x : x,
32036             y : y
32037         });
32038
32039         pos.push({
32040             x : x + (this.unitWidth + this.gutter) * 2,
32041             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
32042         });
32043         
32044         return pos;
32045         
32046     },
32047     
32048     getVerticalThreeBoxColPositions : function(x, y, box)
32049     {
32050         var pos = [];
32051         
32052         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32053             
32054             pos.push({
32055                 x : x,
32056                 y : y
32057             });
32058
32059             pos.push({
32060                 x : x + (this.unitWidth + this.gutter) * 1,
32061                 y : y
32062             });
32063             
32064             pos.push({
32065                 x : x + (this.unitWidth + this.gutter) * 2,
32066                 y : y
32067             });
32068             
32069             return pos;
32070             
32071         }
32072         
32073         if(box[0].size == 'xs' && box[1].size == 'xs'){
32074             
32075             pos.push({
32076                 x : x,
32077                 y : y
32078             });
32079
32080             pos.push({
32081                 x : x,
32082                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
32083             });
32084             
32085             pos.push({
32086                 x : x + (this.unitWidth + this.gutter) * 1,
32087                 y : y
32088             });
32089             
32090             return pos;
32091             
32092         }
32093         
32094         pos.push({
32095             x : x,
32096             y : y
32097         });
32098
32099         pos.push({
32100             x : x + (this.unitWidth + this.gutter) * 2,
32101             y : y
32102         });
32103
32104         pos.push({
32105             x : x + (this.unitWidth + this.gutter) * 2,
32106             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
32107         });
32108             
32109         return pos;
32110         
32111     },
32112     
32113     getVerticalFourBoxColPositions : function(x, y, box)
32114     {
32115         var pos = [];
32116         
32117         if(box[0].size == 'xs'){
32118             
32119             pos.push({
32120                 x : x,
32121                 y : y
32122             });
32123
32124             pos.push({
32125                 x : x,
32126                 y : y + (this.unitHeight + this.gutter) * 1
32127             });
32128             
32129             pos.push({
32130                 x : x,
32131                 y : y + (this.unitHeight + this.gutter) * 2
32132             });
32133             
32134             pos.push({
32135                 x : x + (this.unitWidth + this.gutter) * 1,
32136                 y : y
32137             });
32138             
32139             return pos;
32140             
32141         }
32142         
32143         pos.push({
32144             x : x,
32145             y : y
32146         });
32147
32148         pos.push({
32149             x : x + (this.unitWidth + this.gutter) * 2,
32150             y : y
32151         });
32152
32153         pos.push({
32154             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
32155             y : y + (this.unitHeight + this.gutter) * 1
32156         });
32157
32158         pos.push({
32159             x : x + (this.unitWidth + this.gutter) * 2,
32160             y : y + (this.unitWidth + this.gutter) * 2
32161         });
32162
32163         return pos;
32164         
32165     },
32166     
32167     getHorizontalOneBoxColPositions : function(maxX, minY, box)
32168     {
32169         var pos = [];
32170         
32171         if(box[0].size == 'md-left'){
32172             pos.push({
32173                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32174                 y : minY
32175             });
32176             
32177             return pos;
32178         }
32179         
32180         if(box[0].size == 'md-right'){
32181             pos.push({
32182                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32183                 y : minY + (this.unitWidth + this.gutter) * 1
32184             });
32185             
32186             return pos;
32187         }
32188         
32189         var rand = Math.floor(Math.random() * (4 - box[0].y));
32190         
32191         pos.push({
32192             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32193             y : minY + (this.unitWidth + this.gutter) * rand
32194         });
32195         
32196         return pos;
32197         
32198     },
32199     
32200     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
32201     {
32202         var pos = [];
32203         
32204         if(box[0].size == 'xs'){
32205             
32206             pos.push({
32207                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32208                 y : minY
32209             });
32210
32211             pos.push({
32212                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32213                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32214             });
32215             
32216             return pos;
32217             
32218         }
32219         
32220         pos.push({
32221             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32222             y : minY
32223         });
32224
32225         pos.push({
32226             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32227             y : minY + (this.unitWidth + this.gutter) * 2
32228         });
32229         
32230         return pos;
32231         
32232     },
32233     
32234     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32235     {
32236         var pos = [];
32237         
32238         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32239             
32240             pos.push({
32241                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32242                 y : minY
32243             });
32244
32245             pos.push({
32246                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32247                 y : minY + (this.unitWidth + this.gutter) * 1
32248             });
32249             
32250             pos.push({
32251                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32252                 y : minY + (this.unitWidth + this.gutter) * 2
32253             });
32254             
32255             return pos;
32256             
32257         }
32258         
32259         if(box[0].size == 'xs' && box[1].size == 'xs'){
32260             
32261             pos.push({
32262                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32263                 y : minY
32264             });
32265
32266             pos.push({
32267                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32268                 y : minY
32269             });
32270             
32271             pos.push({
32272                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32273                 y : minY + (this.unitWidth + this.gutter) * 1
32274             });
32275             
32276             return pos;
32277             
32278         }
32279         
32280         pos.push({
32281             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32282             y : minY
32283         });
32284
32285         pos.push({
32286             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32287             y : minY + (this.unitWidth + this.gutter) * 2
32288         });
32289
32290         pos.push({
32291             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32292             y : minY + (this.unitWidth + this.gutter) * 2
32293         });
32294             
32295         return pos;
32296         
32297     },
32298     
32299     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32300     {
32301         var pos = [];
32302         
32303         if(box[0].size == 'xs'){
32304             
32305             pos.push({
32306                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32307                 y : minY
32308             });
32309
32310             pos.push({
32311                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32312                 y : minY
32313             });
32314             
32315             pos.push({
32316                 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),
32317                 y : minY
32318             });
32319             
32320             pos.push({
32321                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32322                 y : minY + (this.unitWidth + this.gutter) * 1
32323             });
32324             
32325             return pos;
32326             
32327         }
32328         
32329         pos.push({
32330             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32331             y : minY
32332         });
32333         
32334         pos.push({
32335             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32336             y : minY + (this.unitWidth + this.gutter) * 2
32337         });
32338         
32339         pos.push({
32340             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32341             y : minY + (this.unitWidth + this.gutter) * 2
32342         });
32343         
32344         pos.push({
32345             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),
32346             y : minY + (this.unitWidth + this.gutter) * 2
32347         });
32348
32349         return pos;
32350         
32351     },
32352     
32353     /**
32354     * remove a Masonry Brick
32355     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32356     */
32357     removeBrick : function(brick_id)
32358     {
32359         if (!brick_id) {
32360             return;
32361         }
32362         
32363         for (var i = 0; i<this.bricks.length; i++) {
32364             if (this.bricks[i].id == brick_id) {
32365                 this.bricks.splice(i,1);
32366                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32367                 this.initial();
32368             }
32369         }
32370     },
32371     
32372     /**
32373     * adds a Masonry Brick
32374     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32375     */
32376     addBrick : function(cfg)
32377     {
32378         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32379         //this.register(cn);
32380         cn.parentId = this.id;
32381         cn.render(this.el);
32382         return cn;
32383     },
32384     
32385     /**
32386     * register a Masonry Brick
32387     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32388     */
32389     
32390     register : function(brick)
32391     {
32392         this.bricks.push(brick);
32393         brick.masonryId = this.id;
32394     },
32395     
32396     /**
32397     * clear all the Masonry Brick
32398     */
32399     clearAll : function()
32400     {
32401         this.bricks = [];
32402         //this.getChildContainer().dom.innerHTML = "";
32403         this.el.dom.innerHTML = '';
32404     },
32405     
32406     getSelected : function()
32407     {
32408         if (!this.selectedBrick) {
32409             return false;
32410         }
32411         
32412         return this.selectedBrick;
32413     }
32414 });
32415
32416 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32417     
32418     groups: {},
32419      /**
32420     * register a Masonry Layout
32421     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32422     */
32423     
32424     register : function(layout)
32425     {
32426         this.groups[layout.id] = layout;
32427     },
32428     /**
32429     * fetch a  Masonry Layout based on the masonry layout ID
32430     * @param {string} the masonry layout to add
32431     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32432     */
32433     
32434     get: function(layout_id) {
32435         if (typeof(this.groups[layout_id]) == 'undefined') {
32436             return false;
32437         }
32438         return this.groups[layout_id] ;
32439     }
32440     
32441     
32442     
32443 });
32444
32445  
32446
32447  /**
32448  *
32449  * This is based on 
32450  * http://masonry.desandro.com
32451  *
32452  * The idea is to render all the bricks based on vertical width...
32453  *
32454  * The original code extends 'outlayer' - we might need to use that....
32455  * 
32456  */
32457
32458
32459 /**
32460  * @class Roo.bootstrap.LayoutMasonryAuto
32461  * @extends Roo.bootstrap.Component
32462  * Bootstrap Layout Masonry class
32463  * 
32464  * @constructor
32465  * Create a new Element
32466  * @param {Object} config The config object
32467  */
32468
32469 Roo.bootstrap.LayoutMasonryAuto = function(config){
32470     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32471 };
32472
32473 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32474     
32475       /**
32476      * @cfg {Boolean} isFitWidth  - resize the width..
32477      */   
32478     isFitWidth : false,  // options..
32479     /**
32480      * @cfg {Boolean} isOriginLeft = left align?
32481      */   
32482     isOriginLeft : true,
32483     /**
32484      * @cfg {Boolean} isOriginTop = top align?
32485      */   
32486     isOriginTop : false,
32487     /**
32488      * @cfg {Boolean} isLayoutInstant = no animation?
32489      */   
32490     isLayoutInstant : false, // needed?
32491     /**
32492      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32493      */   
32494     isResizingContainer : true,
32495     /**
32496      * @cfg {Number} columnWidth  width of the columns 
32497      */   
32498     
32499     columnWidth : 0,
32500     
32501     /**
32502      * @cfg {Number} maxCols maximum number of columns
32503      */   
32504     
32505     maxCols: 0,
32506     /**
32507      * @cfg {Number} padHeight padding below box..
32508      */   
32509     
32510     padHeight : 10, 
32511     
32512     /**
32513      * @cfg {Boolean} isAutoInitial defalut true
32514      */   
32515     
32516     isAutoInitial : true, 
32517     
32518     // private?
32519     gutter : 0,
32520     
32521     containerWidth: 0,
32522     initialColumnWidth : 0,
32523     currentSize : null,
32524     
32525     colYs : null, // array.
32526     maxY : 0,
32527     padWidth: 10,
32528     
32529     
32530     tag: 'div',
32531     cls: '',
32532     bricks: null, //CompositeElement
32533     cols : 0, // array?
32534     // element : null, // wrapped now this.el
32535     _isLayoutInited : null, 
32536     
32537     
32538     getAutoCreate : function(){
32539         
32540         var cfg = {
32541             tag: this.tag,
32542             cls: 'blog-masonary-wrapper ' + this.cls,
32543             cn : {
32544                 cls : 'mas-boxes masonary'
32545             }
32546         };
32547         
32548         return cfg;
32549     },
32550     
32551     getChildContainer: function( )
32552     {
32553         if (this.boxesEl) {
32554             return this.boxesEl;
32555         }
32556         
32557         this.boxesEl = this.el.select('.mas-boxes').first();
32558         
32559         return this.boxesEl;
32560     },
32561     
32562     
32563     initEvents : function()
32564     {
32565         var _this = this;
32566         
32567         if(this.isAutoInitial){
32568             Roo.log('hook children rendered');
32569             this.on('childrenrendered', function() {
32570                 Roo.log('children rendered');
32571                 _this.initial();
32572             } ,this);
32573         }
32574         
32575     },
32576     
32577     initial : function()
32578     {
32579         this.reloadItems();
32580
32581         this.currentSize = this.el.getBox(true);
32582
32583         /// was window resize... - let's see if this works..
32584         Roo.EventManager.onWindowResize(this.resize, this); 
32585
32586         if(!this.isAutoInitial){
32587             this.layout();
32588             return;
32589         }
32590         
32591         this.layout.defer(500,this);
32592     },
32593     
32594     reloadItems: function()
32595     {
32596         this.bricks = this.el.select('.masonry-brick', true);
32597         
32598         this.bricks.each(function(b) {
32599             //Roo.log(b.getSize());
32600             if (!b.attr('originalwidth')) {
32601                 b.attr('originalwidth',  b.getSize().width);
32602             }
32603             
32604         });
32605         
32606         Roo.log(this.bricks.elements.length);
32607     },
32608     
32609     resize : function()
32610     {
32611         Roo.log('resize');
32612         var cs = this.el.getBox(true);
32613         
32614         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32615             Roo.log("no change in with or X");
32616             return;
32617         }
32618         this.currentSize = cs;
32619         this.layout();
32620     },
32621     
32622     layout : function()
32623     {
32624          Roo.log('layout');
32625         this._resetLayout();
32626         //this._manageStamps();
32627       
32628         // don't animate first layout
32629         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32630         this.layoutItems( isInstant );
32631       
32632         // flag for initalized
32633         this._isLayoutInited = true;
32634     },
32635     
32636     layoutItems : function( isInstant )
32637     {
32638         //var items = this._getItemsForLayout( this.items );
32639         // original code supports filtering layout items.. we just ignore it..
32640         
32641         this._layoutItems( this.bricks , isInstant );
32642       
32643         this._postLayout();
32644     },
32645     _layoutItems : function ( items , isInstant)
32646     {
32647        //this.fireEvent( 'layout', this, items );
32648     
32649
32650         if ( !items || !items.elements.length ) {
32651           // no items, emit event with empty array
32652             return;
32653         }
32654
32655         var queue = [];
32656         items.each(function(item) {
32657             Roo.log("layout item");
32658             Roo.log(item);
32659             // get x/y object from method
32660             var position = this._getItemLayoutPosition( item );
32661             // enqueue
32662             position.item = item;
32663             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32664             queue.push( position );
32665         }, this);
32666       
32667         this._processLayoutQueue( queue );
32668     },
32669     /** Sets position of item in DOM
32670     * @param {Element} item
32671     * @param {Number} x - horizontal position
32672     * @param {Number} y - vertical position
32673     * @param {Boolean} isInstant - disables transitions
32674     */
32675     _processLayoutQueue : function( queue )
32676     {
32677         for ( var i=0, len = queue.length; i < len; i++ ) {
32678             var obj = queue[i];
32679             obj.item.position('absolute');
32680             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32681         }
32682     },
32683       
32684     
32685     /**
32686     * Any logic you want to do after each layout,
32687     * i.e. size the container
32688     */
32689     _postLayout : function()
32690     {
32691         this.resizeContainer();
32692     },
32693     
32694     resizeContainer : function()
32695     {
32696         if ( !this.isResizingContainer ) {
32697             return;
32698         }
32699         var size = this._getContainerSize();
32700         if ( size ) {
32701             this.el.setSize(size.width,size.height);
32702             this.boxesEl.setSize(size.width,size.height);
32703         }
32704     },
32705     
32706     
32707     
32708     _resetLayout : function()
32709     {
32710         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32711         this.colWidth = this.el.getWidth();
32712         //this.gutter = this.el.getWidth(); 
32713         
32714         this.measureColumns();
32715
32716         // reset column Y
32717         var i = this.cols;
32718         this.colYs = [];
32719         while (i--) {
32720             this.colYs.push( 0 );
32721         }
32722     
32723         this.maxY = 0;
32724     },
32725
32726     measureColumns : function()
32727     {
32728         this.getContainerWidth();
32729       // if columnWidth is 0, default to outerWidth of first item
32730         if ( !this.columnWidth ) {
32731             var firstItem = this.bricks.first();
32732             Roo.log(firstItem);
32733             this.columnWidth  = this.containerWidth;
32734             if (firstItem && firstItem.attr('originalwidth') ) {
32735                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32736             }
32737             // columnWidth fall back to item of first element
32738             Roo.log("set column width?");
32739                         this.initialColumnWidth = this.columnWidth  ;
32740
32741             // if first elem has no width, default to size of container
32742             
32743         }
32744         
32745         
32746         if (this.initialColumnWidth) {
32747             this.columnWidth = this.initialColumnWidth;
32748         }
32749         
32750         
32751             
32752         // column width is fixed at the top - however if container width get's smaller we should
32753         // reduce it...
32754         
32755         // this bit calcs how man columns..
32756             
32757         var columnWidth = this.columnWidth += this.gutter;
32758       
32759         // calculate columns
32760         var containerWidth = this.containerWidth + this.gutter;
32761         
32762         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32763         // fix rounding errors, typically with gutters
32764         var excess = columnWidth - containerWidth % columnWidth;
32765         
32766         
32767         // if overshoot is less than a pixel, round up, otherwise floor it
32768         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32769         cols = Math[ mathMethod ]( cols );
32770         this.cols = Math.max( cols, 1 );
32771         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32772         
32773          // padding positioning..
32774         var totalColWidth = this.cols * this.columnWidth;
32775         var padavail = this.containerWidth - totalColWidth;
32776         // so for 2 columns - we need 3 'pads'
32777         
32778         var padNeeded = (1+this.cols) * this.padWidth;
32779         
32780         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32781         
32782         this.columnWidth += padExtra
32783         //this.padWidth = Math.floor(padavail /  ( this.cols));
32784         
32785         // adjust colum width so that padding is fixed??
32786         
32787         // we have 3 columns ... total = width * 3
32788         // we have X left over... that should be used by 
32789         
32790         //if (this.expandC) {
32791             
32792         //}
32793         
32794         
32795         
32796     },
32797     
32798     getContainerWidth : function()
32799     {
32800        /* // container is parent if fit width
32801         var container = this.isFitWidth ? this.element.parentNode : this.element;
32802         // check that this.size and size are there
32803         // IE8 triggers resize on body size change, so they might not be
32804         
32805         var size = getSize( container );  //FIXME
32806         this.containerWidth = size && size.innerWidth; //FIXME
32807         */
32808          
32809         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32810         
32811     },
32812     
32813     _getItemLayoutPosition : function( item )  // what is item?
32814     {
32815         // we resize the item to our columnWidth..
32816       
32817         item.setWidth(this.columnWidth);
32818         item.autoBoxAdjust  = false;
32819         
32820         var sz = item.getSize();
32821  
32822         // how many columns does this brick span
32823         var remainder = this.containerWidth % this.columnWidth;
32824         
32825         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32826         // round if off by 1 pixel, otherwise use ceil
32827         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32828         colSpan = Math.min( colSpan, this.cols );
32829         
32830         // normally this should be '1' as we dont' currently allow multi width columns..
32831         
32832         var colGroup = this._getColGroup( colSpan );
32833         // get the minimum Y value from the columns
32834         var minimumY = Math.min.apply( Math, colGroup );
32835         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32836         
32837         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32838          
32839         // position the brick
32840         var position = {
32841             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32842             y: this.currentSize.y + minimumY + this.padHeight
32843         };
32844         
32845         Roo.log(position);
32846         // apply setHeight to necessary columns
32847         var setHeight = minimumY + sz.height + this.padHeight;
32848         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32849         
32850         var setSpan = this.cols + 1 - colGroup.length;
32851         for ( var i = 0; i < setSpan; i++ ) {
32852           this.colYs[ shortColIndex + i ] = setHeight ;
32853         }
32854       
32855         return position;
32856     },
32857     
32858     /**
32859      * @param {Number} colSpan - number of columns the element spans
32860      * @returns {Array} colGroup
32861      */
32862     _getColGroup : function( colSpan )
32863     {
32864         if ( colSpan < 2 ) {
32865           // if brick spans only one column, use all the column Ys
32866           return this.colYs;
32867         }
32868       
32869         var colGroup = [];
32870         // how many different places could this brick fit horizontally
32871         var groupCount = this.cols + 1 - colSpan;
32872         // for each group potential horizontal position
32873         for ( var i = 0; i < groupCount; i++ ) {
32874           // make an array of colY values for that one group
32875           var groupColYs = this.colYs.slice( i, i + colSpan );
32876           // and get the max value of the array
32877           colGroup[i] = Math.max.apply( Math, groupColYs );
32878         }
32879         return colGroup;
32880     },
32881     /*
32882     _manageStamp : function( stamp )
32883     {
32884         var stampSize =  stamp.getSize();
32885         var offset = stamp.getBox();
32886         // get the columns that this stamp affects
32887         var firstX = this.isOriginLeft ? offset.x : offset.right;
32888         var lastX = firstX + stampSize.width;
32889         var firstCol = Math.floor( firstX / this.columnWidth );
32890         firstCol = Math.max( 0, firstCol );
32891         
32892         var lastCol = Math.floor( lastX / this.columnWidth );
32893         // lastCol should not go over if multiple of columnWidth #425
32894         lastCol -= lastX % this.columnWidth ? 0 : 1;
32895         lastCol = Math.min( this.cols - 1, lastCol );
32896         
32897         // set colYs to bottom of the stamp
32898         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32899             stampSize.height;
32900             
32901         for ( var i = firstCol; i <= lastCol; i++ ) {
32902           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32903         }
32904     },
32905     */
32906     
32907     _getContainerSize : function()
32908     {
32909         this.maxY = Math.max.apply( Math, this.colYs );
32910         var size = {
32911             height: this.maxY
32912         };
32913       
32914         if ( this.isFitWidth ) {
32915             size.width = this._getContainerFitWidth();
32916         }
32917       
32918         return size;
32919     },
32920     
32921     _getContainerFitWidth : function()
32922     {
32923         var unusedCols = 0;
32924         // count unused columns
32925         var i = this.cols;
32926         while ( --i ) {
32927           if ( this.colYs[i] !== 0 ) {
32928             break;
32929           }
32930           unusedCols++;
32931         }
32932         // fit container to columns that have been used
32933         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32934     },
32935     
32936     needsResizeLayout : function()
32937     {
32938         var previousWidth = this.containerWidth;
32939         this.getContainerWidth();
32940         return previousWidth !== this.containerWidth;
32941     }
32942  
32943 });
32944
32945  
32946
32947  /*
32948  * - LGPL
32949  *
32950  * element
32951  * 
32952  */
32953
32954 /**
32955  * @class Roo.bootstrap.MasonryBrick
32956  * @extends Roo.bootstrap.Component
32957  * Bootstrap MasonryBrick class
32958  * 
32959  * @constructor
32960  * Create a new MasonryBrick
32961  * @param {Object} config The config object
32962  */
32963
32964 Roo.bootstrap.MasonryBrick = function(config){
32965     
32966     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32967     
32968     Roo.bootstrap.MasonryBrick.register(this);
32969     
32970     this.addEvents({
32971         // raw events
32972         /**
32973          * @event click
32974          * When a MasonryBrick is clcik
32975          * @param {Roo.bootstrap.MasonryBrick} this
32976          * @param {Roo.EventObject} e
32977          */
32978         "click" : true
32979     });
32980 };
32981
32982 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32983     
32984     /**
32985      * @cfg {String} title
32986      */   
32987     title : '',
32988     /**
32989      * @cfg {String} html
32990      */   
32991     html : '',
32992     /**
32993      * @cfg {String} bgimage
32994      */   
32995     bgimage : '',
32996     /**
32997      * @cfg {String} videourl
32998      */   
32999     videourl : '',
33000     /**
33001      * @cfg {String} cls
33002      */   
33003     cls : '',
33004     /**
33005      * @cfg {String} href
33006      */   
33007     href : '',
33008     /**
33009      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
33010      */   
33011     size : 'xs',
33012     
33013     /**
33014      * @cfg {String} placetitle (center|bottom)
33015      */   
33016     placetitle : '',
33017     
33018     /**
33019      * @cfg {Boolean} isFitContainer defalut true
33020      */   
33021     isFitContainer : true, 
33022     
33023     /**
33024      * @cfg {Boolean} preventDefault defalut false
33025      */   
33026     preventDefault : false, 
33027     
33028     /**
33029      * @cfg {Boolean} inverse defalut false
33030      */   
33031     maskInverse : false, 
33032     
33033     getAutoCreate : function()
33034     {
33035         if(!this.isFitContainer){
33036             return this.getSplitAutoCreate();
33037         }
33038         
33039         var cls = 'masonry-brick masonry-brick-full';
33040         
33041         if(this.href.length){
33042             cls += ' masonry-brick-link';
33043         }
33044         
33045         if(this.bgimage.length){
33046             cls += ' masonry-brick-image';
33047         }
33048         
33049         if(this.maskInverse){
33050             cls += ' mask-inverse';
33051         }
33052         
33053         if(!this.html.length && !this.maskInverse && !this.videourl.length){
33054             cls += ' enable-mask';
33055         }
33056         
33057         if(this.size){
33058             cls += ' masonry-' + this.size + '-brick';
33059         }
33060         
33061         if(this.placetitle.length){
33062             
33063             switch (this.placetitle) {
33064                 case 'center' :
33065                     cls += ' masonry-center-title';
33066                     break;
33067                 case 'bottom' :
33068                     cls += ' masonry-bottom-title';
33069                     break;
33070                 default:
33071                     break;
33072             }
33073             
33074         } else {
33075             if(!this.html.length && !this.bgimage.length){
33076                 cls += ' masonry-center-title';
33077             }
33078
33079             if(!this.html.length && this.bgimage.length){
33080                 cls += ' masonry-bottom-title';
33081             }
33082         }
33083         
33084         if(this.cls){
33085             cls += ' ' + this.cls;
33086         }
33087         
33088         var cfg = {
33089             tag: (this.href.length) ? 'a' : 'div',
33090             cls: cls,
33091             cn: [
33092                 {
33093                     tag: 'div',
33094                     cls: 'masonry-brick-mask'
33095                 },
33096                 {
33097                     tag: 'div',
33098                     cls: 'masonry-brick-paragraph',
33099                     cn: []
33100                 }
33101             ]
33102         };
33103         
33104         if(this.href.length){
33105             cfg.href = this.href;
33106         }
33107         
33108         var cn = cfg.cn[1].cn;
33109         
33110         if(this.title.length){
33111             cn.push({
33112                 tag: 'h4',
33113                 cls: 'masonry-brick-title',
33114                 html: this.title
33115             });
33116         }
33117         
33118         if(this.html.length){
33119             cn.push({
33120                 tag: 'p',
33121                 cls: 'masonry-brick-text',
33122                 html: this.html
33123             });
33124         }
33125         
33126         if (!this.title.length && !this.html.length) {
33127             cfg.cn[1].cls += ' hide';
33128         }
33129         
33130         if(this.bgimage.length){
33131             cfg.cn.push({
33132                 tag: 'img',
33133                 cls: 'masonry-brick-image-view',
33134                 src: this.bgimage
33135             });
33136         }
33137         
33138         if(this.videourl.length){
33139             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33140             // youtube support only?
33141             cfg.cn.push({
33142                 tag: 'iframe',
33143                 cls: 'masonry-brick-image-view',
33144                 src: vurl,
33145                 frameborder : 0,
33146                 allowfullscreen : true
33147             });
33148         }
33149         
33150         return cfg;
33151         
33152     },
33153     
33154     getSplitAutoCreate : function()
33155     {
33156         var cls = 'masonry-brick masonry-brick-split';
33157         
33158         if(this.href.length){
33159             cls += ' masonry-brick-link';
33160         }
33161         
33162         if(this.bgimage.length){
33163             cls += ' masonry-brick-image';
33164         }
33165         
33166         if(this.size){
33167             cls += ' masonry-' + this.size + '-brick';
33168         }
33169         
33170         switch (this.placetitle) {
33171             case 'center' :
33172                 cls += ' masonry-center-title';
33173                 break;
33174             case 'bottom' :
33175                 cls += ' masonry-bottom-title';
33176                 break;
33177             default:
33178                 if(!this.bgimage.length){
33179                     cls += ' masonry-center-title';
33180                 }
33181
33182                 if(this.bgimage.length){
33183                     cls += ' masonry-bottom-title';
33184                 }
33185                 break;
33186         }
33187         
33188         if(this.cls){
33189             cls += ' ' + this.cls;
33190         }
33191         
33192         var cfg = {
33193             tag: (this.href.length) ? 'a' : 'div',
33194             cls: cls,
33195             cn: [
33196                 {
33197                     tag: 'div',
33198                     cls: 'masonry-brick-split-head',
33199                     cn: [
33200                         {
33201                             tag: 'div',
33202                             cls: 'masonry-brick-paragraph',
33203                             cn: []
33204                         }
33205                     ]
33206                 },
33207                 {
33208                     tag: 'div',
33209                     cls: 'masonry-brick-split-body',
33210                     cn: []
33211                 }
33212             ]
33213         };
33214         
33215         if(this.href.length){
33216             cfg.href = this.href;
33217         }
33218         
33219         if(this.title.length){
33220             cfg.cn[0].cn[0].cn.push({
33221                 tag: 'h4',
33222                 cls: 'masonry-brick-title',
33223                 html: this.title
33224             });
33225         }
33226         
33227         if(this.html.length){
33228             cfg.cn[1].cn.push({
33229                 tag: 'p',
33230                 cls: 'masonry-brick-text',
33231                 html: this.html
33232             });
33233         }
33234
33235         if(this.bgimage.length){
33236             cfg.cn[0].cn.push({
33237                 tag: 'img',
33238                 cls: 'masonry-brick-image-view',
33239                 src: this.bgimage
33240             });
33241         }
33242         
33243         if(this.videourl.length){
33244             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33245             // youtube support only?
33246             cfg.cn[0].cn.cn.push({
33247                 tag: 'iframe',
33248                 cls: 'masonry-brick-image-view',
33249                 src: vurl,
33250                 frameborder : 0,
33251                 allowfullscreen : true
33252             });
33253         }
33254         
33255         return cfg;
33256     },
33257     
33258     initEvents: function() 
33259     {
33260         switch (this.size) {
33261             case 'xs' :
33262                 this.x = 1;
33263                 this.y = 1;
33264                 break;
33265             case 'sm' :
33266                 this.x = 2;
33267                 this.y = 2;
33268                 break;
33269             case 'md' :
33270             case 'md-left' :
33271             case 'md-right' :
33272                 this.x = 3;
33273                 this.y = 3;
33274                 break;
33275             case 'tall' :
33276                 this.x = 2;
33277                 this.y = 3;
33278                 break;
33279             case 'wide' :
33280                 this.x = 3;
33281                 this.y = 2;
33282                 break;
33283             case 'wide-thin' :
33284                 this.x = 3;
33285                 this.y = 1;
33286                 break;
33287                         
33288             default :
33289                 break;
33290         }
33291         
33292         if(Roo.isTouch){
33293             this.el.on('touchstart', this.onTouchStart, this);
33294             this.el.on('touchmove', this.onTouchMove, this);
33295             this.el.on('touchend', this.onTouchEnd, this);
33296             this.el.on('contextmenu', this.onContextMenu, this);
33297         } else {
33298             this.el.on('mouseenter'  ,this.enter, this);
33299             this.el.on('mouseleave', this.leave, this);
33300             this.el.on('click', this.onClick, this);
33301         }
33302         
33303         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33304             this.parent().bricks.push(this);   
33305         }
33306         
33307     },
33308     
33309     onClick: function(e, el)
33310     {
33311         var time = this.endTimer - this.startTimer;
33312         // Roo.log(e.preventDefault());
33313         if(Roo.isTouch){
33314             if(time > 1000){
33315                 e.preventDefault();
33316                 return;
33317             }
33318         }
33319         
33320         if(!this.preventDefault){
33321             return;
33322         }
33323         
33324         e.preventDefault();
33325         
33326         if (this.activeClass != '') {
33327             this.selectBrick();
33328         }
33329         
33330         this.fireEvent('click', this, e);
33331     },
33332     
33333     enter: function(e, el)
33334     {
33335         e.preventDefault();
33336         
33337         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33338             return;
33339         }
33340         
33341         if(this.bgimage.length && this.html.length){
33342             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33343         }
33344     },
33345     
33346     leave: 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, true);
33356         }
33357     },
33358     
33359     onTouchStart: function(e, el)
33360     {
33361 //        e.preventDefault();
33362         
33363         this.touchmoved = false;
33364         
33365         if(!this.isFitContainer){
33366             return;
33367         }
33368         
33369         if(!this.bgimage.length || !this.html.length){
33370             return;
33371         }
33372         
33373         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33374         
33375         this.timer = new Date().getTime();
33376         
33377     },
33378     
33379     onTouchMove: function(e, el)
33380     {
33381         this.touchmoved = true;
33382     },
33383     
33384     onContextMenu : function(e,el)
33385     {
33386         e.preventDefault();
33387         e.stopPropagation();
33388         return false;
33389     },
33390     
33391     onTouchEnd: function(e, el)
33392     {
33393 //        e.preventDefault();
33394         
33395         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33396         
33397             this.leave(e,el);
33398             
33399             return;
33400         }
33401         
33402         if(!this.bgimage.length || !this.html.length){
33403             
33404             if(this.href.length){
33405                 window.location.href = this.href;
33406             }
33407             
33408             return;
33409         }
33410         
33411         if(!this.isFitContainer){
33412             return;
33413         }
33414         
33415         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33416         
33417         window.location.href = this.href;
33418     },
33419     
33420     //selection on single brick only
33421     selectBrick : function() {
33422         
33423         if (!this.parentId) {
33424             return;
33425         }
33426         
33427         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33428         var index = m.selectedBrick.indexOf(this.id);
33429         
33430         if ( index > -1) {
33431             m.selectedBrick.splice(index,1);
33432             this.el.removeClass(this.activeClass);
33433             return;
33434         }
33435         
33436         for(var i = 0; i < m.selectedBrick.length; i++) {
33437             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33438             b.el.removeClass(b.activeClass);
33439         }
33440         
33441         m.selectedBrick = [];
33442         
33443         m.selectedBrick.push(this.id);
33444         this.el.addClass(this.activeClass);
33445         return;
33446     },
33447     
33448     isSelected : function(){
33449         return this.el.hasClass(this.activeClass);
33450         
33451     }
33452 });
33453
33454 Roo.apply(Roo.bootstrap.MasonryBrick, {
33455     
33456     //groups: {},
33457     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33458      /**
33459     * register a Masonry Brick
33460     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33461     */
33462     
33463     register : function(brick)
33464     {
33465         //this.groups[brick.id] = brick;
33466         this.groups.add(brick.id, brick);
33467     },
33468     /**
33469     * fetch a  masonry brick based on the masonry brick ID
33470     * @param {string} the masonry brick to add
33471     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33472     */
33473     
33474     get: function(brick_id) 
33475     {
33476         // if (typeof(this.groups[brick_id]) == 'undefined') {
33477         //     return false;
33478         // }
33479         // return this.groups[brick_id] ;
33480         
33481         if(this.groups.key(brick_id)) {
33482             return this.groups.key(brick_id);
33483         }
33484         
33485         return false;
33486     }
33487     
33488     
33489     
33490 });
33491
33492  /*
33493  * - LGPL
33494  *
33495  * element
33496  * 
33497  */
33498
33499 /**
33500  * @class Roo.bootstrap.Brick
33501  * @extends Roo.bootstrap.Component
33502  * Bootstrap Brick class
33503  * 
33504  * @constructor
33505  * Create a new Brick
33506  * @param {Object} config The config object
33507  */
33508
33509 Roo.bootstrap.Brick = function(config){
33510     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33511     
33512     this.addEvents({
33513         // raw events
33514         /**
33515          * @event click
33516          * When a Brick is click
33517          * @param {Roo.bootstrap.Brick} this
33518          * @param {Roo.EventObject} e
33519          */
33520         "click" : true
33521     });
33522 };
33523
33524 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33525     
33526     /**
33527      * @cfg {String} title
33528      */   
33529     title : '',
33530     /**
33531      * @cfg {String} html
33532      */   
33533     html : '',
33534     /**
33535      * @cfg {String} bgimage
33536      */   
33537     bgimage : '',
33538     /**
33539      * @cfg {String} cls
33540      */   
33541     cls : '',
33542     /**
33543      * @cfg {String} href
33544      */   
33545     href : '',
33546     /**
33547      * @cfg {String} video
33548      */   
33549     video : '',
33550     /**
33551      * @cfg {Boolean} square
33552      */   
33553     square : true,
33554     
33555     getAutoCreate : function()
33556     {
33557         var cls = 'roo-brick';
33558         
33559         if(this.href.length){
33560             cls += ' roo-brick-link';
33561         }
33562         
33563         if(this.bgimage.length){
33564             cls += ' roo-brick-image';
33565         }
33566         
33567         if(!this.html.length && !this.bgimage.length){
33568             cls += ' roo-brick-center-title';
33569         }
33570         
33571         if(!this.html.length && this.bgimage.length){
33572             cls += ' roo-brick-bottom-title';
33573         }
33574         
33575         if(this.cls){
33576             cls += ' ' + this.cls;
33577         }
33578         
33579         var cfg = {
33580             tag: (this.href.length) ? 'a' : 'div',
33581             cls: cls,
33582             cn: [
33583                 {
33584                     tag: 'div',
33585                     cls: 'roo-brick-paragraph',
33586                     cn: []
33587                 }
33588             ]
33589         };
33590         
33591         if(this.href.length){
33592             cfg.href = this.href;
33593         }
33594         
33595         var cn = cfg.cn[0].cn;
33596         
33597         if(this.title.length){
33598             cn.push({
33599                 tag: 'h4',
33600                 cls: 'roo-brick-title',
33601                 html: this.title
33602             });
33603         }
33604         
33605         if(this.html.length){
33606             cn.push({
33607                 tag: 'p',
33608                 cls: 'roo-brick-text',
33609                 html: this.html
33610             });
33611         } else {
33612             cn.cls += ' hide';
33613         }
33614         
33615         if(this.bgimage.length){
33616             cfg.cn.push({
33617                 tag: 'img',
33618                 cls: 'roo-brick-image-view',
33619                 src: this.bgimage
33620             });
33621         }
33622         
33623         return cfg;
33624     },
33625     
33626     initEvents: function() 
33627     {
33628         if(this.title.length || this.html.length){
33629             this.el.on('mouseenter'  ,this.enter, this);
33630             this.el.on('mouseleave', this.leave, this);
33631         }
33632         
33633         Roo.EventManager.onWindowResize(this.resize, this); 
33634         
33635         if(this.bgimage.length){
33636             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33637             this.imageEl.on('load', this.onImageLoad, this);
33638             return;
33639         }
33640         
33641         this.resize();
33642     },
33643     
33644     onImageLoad : function()
33645     {
33646         this.resize();
33647     },
33648     
33649     resize : function()
33650     {
33651         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33652         
33653         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33654         
33655         if(this.bgimage.length){
33656             var image = this.el.select('.roo-brick-image-view', true).first();
33657             
33658             image.setWidth(paragraph.getWidth());
33659             
33660             if(this.square){
33661                 image.setHeight(paragraph.getWidth());
33662             }
33663             
33664             this.el.setHeight(image.getHeight());
33665             paragraph.setHeight(image.getHeight());
33666             
33667         }
33668         
33669     },
33670     
33671     enter: function(e, el)
33672     {
33673         e.preventDefault();
33674         
33675         if(this.bgimage.length){
33676             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33677             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33678         }
33679     },
33680     
33681     leave: function(e, el)
33682     {
33683         e.preventDefault();
33684         
33685         if(this.bgimage.length){
33686             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33687             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33688         }
33689     }
33690     
33691 });
33692
33693  
33694
33695  /*
33696  * - LGPL
33697  *
33698  * Number field 
33699  */
33700
33701 /**
33702  * @class Roo.bootstrap.NumberField
33703  * @extends Roo.bootstrap.Input
33704  * Bootstrap NumberField class
33705  * 
33706  * 
33707  * 
33708  * 
33709  * @constructor
33710  * Create a new NumberField
33711  * @param {Object} config The config object
33712  */
33713
33714 Roo.bootstrap.NumberField = function(config){
33715     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33716 };
33717
33718 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33719     
33720     /**
33721      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33722      */
33723     allowDecimals : true,
33724     /**
33725      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33726      */
33727     decimalSeparator : ".",
33728     /**
33729      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33730      */
33731     decimalPrecision : 2,
33732     /**
33733      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33734      */
33735     allowNegative : true,
33736     
33737     /**
33738      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33739      */
33740     allowZero: true,
33741     /**
33742      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33743      */
33744     minValue : Number.NEGATIVE_INFINITY,
33745     /**
33746      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33747      */
33748     maxValue : Number.MAX_VALUE,
33749     /**
33750      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33751      */
33752     minText : "The minimum value for this field is {0}",
33753     /**
33754      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33755      */
33756     maxText : "The maximum value for this field is {0}",
33757     /**
33758      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33759      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33760      */
33761     nanText : "{0} is not a valid number",
33762     /**
33763      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33764      */
33765     thousandsDelimiter : false,
33766     /**
33767      * @cfg {String} valueAlign alignment of value
33768      */
33769     valueAlign : "left",
33770
33771     getAutoCreate : function()
33772     {
33773         var hiddenInput = {
33774             tag: 'input',
33775             type: 'hidden',
33776             id: Roo.id(),
33777             cls: 'hidden-number-input'
33778         };
33779         
33780         if (this.name) {
33781             hiddenInput.name = this.name;
33782         }
33783         
33784         this.name = '';
33785         
33786         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33787         
33788         this.name = hiddenInput.name;
33789         
33790         if(cfg.cn.length > 0) {
33791             cfg.cn.push(hiddenInput);
33792         }
33793         
33794         return cfg;
33795     },
33796
33797     // private
33798     initEvents : function()
33799     {   
33800         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33801         
33802         var allowed = "0123456789";
33803         
33804         if(this.allowDecimals){
33805             allowed += this.decimalSeparator;
33806         }
33807         
33808         if(this.allowNegative){
33809             allowed += "-";
33810         }
33811         
33812         if(this.thousandsDelimiter) {
33813             allowed += ",";
33814         }
33815         
33816         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33817         
33818         var keyPress = function(e){
33819             
33820             var k = e.getKey();
33821             
33822             var c = e.getCharCode();
33823             
33824             if(
33825                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33826                     allowed.indexOf(String.fromCharCode(c)) === -1
33827             ){
33828                 e.stopEvent();
33829                 return;
33830             }
33831             
33832             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33833                 return;
33834             }
33835             
33836             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33837                 e.stopEvent();
33838             }
33839         };
33840         
33841         this.el.on("keypress", keyPress, this);
33842     },
33843     
33844     validateValue : function(value)
33845     {
33846         
33847         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33848             return false;
33849         }
33850         
33851         var num = this.parseValue(value);
33852         
33853         if(isNaN(num)){
33854             this.markInvalid(String.format(this.nanText, value));
33855             return false;
33856         }
33857         
33858         if(num < this.minValue){
33859             this.markInvalid(String.format(this.minText, this.minValue));
33860             return false;
33861         }
33862         
33863         if(num > this.maxValue){
33864             this.markInvalid(String.format(this.maxText, this.maxValue));
33865             return false;
33866         }
33867         
33868         return true;
33869     },
33870
33871     getValue : function()
33872     {
33873         var v = this.hiddenEl().getValue();
33874         
33875         return this.fixPrecision(this.parseValue(v));
33876     },
33877
33878     parseValue : function(value)
33879     {
33880         if(this.thousandsDelimiter) {
33881             value += "";
33882             r = new RegExp(",", "g");
33883             value = value.replace(r, "");
33884         }
33885         
33886         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33887         return isNaN(value) ? '' : value;
33888     },
33889
33890     fixPrecision : function(value)
33891     {
33892         if(this.thousandsDelimiter) {
33893             value += "";
33894             r = new RegExp(",", "g");
33895             value = value.replace(r, "");
33896         }
33897         
33898         var nan = isNaN(value);
33899         
33900         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33901             return nan ? '' : value;
33902         }
33903         return parseFloat(value).toFixed(this.decimalPrecision);
33904     },
33905
33906     setValue : function(v)
33907     {
33908         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33909         
33910         this.value = v;
33911         
33912         if(this.rendered){
33913             
33914             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33915             
33916             this.inputEl().dom.value = (v == '') ? '' :
33917                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33918             
33919             if(!this.allowZero && v === '0') {
33920                 this.hiddenEl().dom.value = '';
33921                 this.inputEl().dom.value = '';
33922             }
33923             
33924             this.validate();
33925         }
33926     },
33927
33928     decimalPrecisionFcn : function(v)
33929     {
33930         return Math.floor(v);
33931     },
33932
33933     beforeBlur : function()
33934     {
33935         var v = this.parseValue(this.getRawValue());
33936         
33937         if(v || v === 0 || v === ''){
33938             this.setValue(v);
33939         }
33940     },
33941     
33942     hiddenEl : function()
33943     {
33944         return this.el.select('input.hidden-number-input',true).first();
33945     }
33946     
33947 });
33948
33949  
33950
33951 /*
33952 * Licence: LGPL
33953 */
33954
33955 /**
33956  * @class Roo.bootstrap.DocumentSlider
33957  * @extends Roo.bootstrap.Component
33958  * Bootstrap DocumentSlider class
33959  * 
33960  * @constructor
33961  * Create a new DocumentViewer
33962  * @param {Object} config The config object
33963  */
33964
33965 Roo.bootstrap.DocumentSlider = function(config){
33966     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33967     
33968     this.files = [];
33969     
33970     this.addEvents({
33971         /**
33972          * @event initial
33973          * Fire after initEvent
33974          * @param {Roo.bootstrap.DocumentSlider} this
33975          */
33976         "initial" : true,
33977         /**
33978          * @event update
33979          * Fire after update
33980          * @param {Roo.bootstrap.DocumentSlider} this
33981          */
33982         "update" : true,
33983         /**
33984          * @event click
33985          * Fire after click
33986          * @param {Roo.bootstrap.DocumentSlider} this
33987          */
33988         "click" : true
33989     });
33990 };
33991
33992 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33993     
33994     files : false,
33995     
33996     indicator : 0,
33997     
33998     getAutoCreate : function()
33999     {
34000         var cfg = {
34001             tag : 'div',
34002             cls : 'roo-document-slider',
34003             cn : [
34004                 {
34005                     tag : 'div',
34006                     cls : 'roo-document-slider-header',
34007                     cn : [
34008                         {
34009                             tag : 'div',
34010                             cls : 'roo-document-slider-header-title'
34011                         }
34012                     ]
34013                 },
34014                 {
34015                     tag : 'div',
34016                     cls : 'roo-document-slider-body',
34017                     cn : [
34018                         {
34019                             tag : 'div',
34020                             cls : 'roo-document-slider-prev',
34021                             cn : [
34022                                 {
34023                                     tag : 'i',
34024                                     cls : 'fa fa-chevron-left'
34025                                 }
34026                             ]
34027                         },
34028                         {
34029                             tag : 'div',
34030                             cls : 'roo-document-slider-thumb',
34031                             cn : [
34032                                 {
34033                                     tag : 'img',
34034                                     cls : 'roo-document-slider-image'
34035                                 }
34036                             ]
34037                         },
34038                         {
34039                             tag : 'div',
34040                             cls : 'roo-document-slider-next',
34041                             cn : [
34042                                 {
34043                                     tag : 'i',
34044                                     cls : 'fa fa-chevron-right'
34045                                 }
34046                             ]
34047                         }
34048                     ]
34049                 }
34050             ]
34051         };
34052         
34053         return cfg;
34054     },
34055     
34056     initEvents : function()
34057     {
34058         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
34059         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
34060         
34061         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
34062         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
34063         
34064         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
34065         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
34066         
34067         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
34068         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
34069         
34070         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
34071         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
34072         
34073         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
34074         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34075         
34076         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
34077         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34078         
34079         this.thumbEl.on('click', this.onClick, this);
34080         
34081         this.prevIndicator.on('click', this.prev, this);
34082         
34083         this.nextIndicator.on('click', this.next, this);
34084         
34085     },
34086     
34087     initial : function()
34088     {
34089         if(this.files.length){
34090             this.indicator = 1;
34091             this.update()
34092         }
34093         
34094         this.fireEvent('initial', this);
34095     },
34096     
34097     update : function()
34098     {
34099         this.imageEl.attr('src', this.files[this.indicator - 1]);
34100         
34101         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
34102         
34103         this.prevIndicator.show();
34104         
34105         if(this.indicator == 1){
34106             this.prevIndicator.hide();
34107         }
34108         
34109         this.nextIndicator.show();
34110         
34111         if(this.indicator == this.files.length){
34112             this.nextIndicator.hide();
34113         }
34114         
34115         this.thumbEl.scrollTo('top');
34116         
34117         this.fireEvent('update', this);
34118     },
34119     
34120     onClick : function(e)
34121     {
34122         e.preventDefault();
34123         
34124         this.fireEvent('click', this);
34125     },
34126     
34127     prev : function(e)
34128     {
34129         e.preventDefault();
34130         
34131         this.indicator = Math.max(1, this.indicator - 1);
34132         
34133         this.update();
34134     },
34135     
34136     next : function(e)
34137     {
34138         e.preventDefault();
34139         
34140         this.indicator = Math.min(this.files.length, this.indicator + 1);
34141         
34142         this.update();
34143     }
34144 });
34145 /*
34146  * - LGPL
34147  *
34148  * RadioSet
34149  *
34150  *
34151  */
34152
34153 /**
34154  * @class Roo.bootstrap.RadioSet
34155  * @extends Roo.bootstrap.Input
34156  * Bootstrap RadioSet class
34157  * @cfg {String} indicatorpos (left|right) default left
34158  * @cfg {Boolean} inline (true|false) inline the element (default true)
34159  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
34160  * @constructor
34161  * Create a new RadioSet
34162  * @param {Object} config The config object
34163  */
34164
34165 Roo.bootstrap.RadioSet = function(config){
34166     
34167     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
34168     
34169     this.radioes = [];
34170     
34171     Roo.bootstrap.RadioSet.register(this);
34172     
34173     this.addEvents({
34174         /**
34175         * @event check
34176         * Fires when the element is checked or unchecked.
34177         * @param {Roo.bootstrap.RadioSet} this This radio
34178         * @param {Roo.bootstrap.Radio} item The checked item
34179         */
34180        check : true,
34181        /**
34182         * @event click
34183         * Fires when the element is click.
34184         * @param {Roo.bootstrap.RadioSet} this This radio set
34185         * @param {Roo.bootstrap.Radio} item The checked item
34186         * @param {Roo.EventObject} e The event object
34187         */
34188        click : true
34189     });
34190     
34191 };
34192
34193 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
34194
34195     radioes : false,
34196     
34197     inline : true,
34198     
34199     weight : '',
34200     
34201     indicatorpos : 'left',
34202     
34203     getAutoCreate : function()
34204     {
34205         var label = {
34206             tag : 'label',
34207             cls : 'roo-radio-set-label',
34208             cn : [
34209                 {
34210                     tag : 'span',
34211                     html : this.fieldLabel
34212                 }
34213             ]
34214         };
34215         if (Roo.bootstrap.version == 3) {
34216             
34217             
34218             if(this.indicatorpos == 'left'){
34219                 label.cn.unshift({
34220                     tag : 'i',
34221                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34222                     tooltip : 'This field is required'
34223                 });
34224             } else {
34225                 label.cn.push({
34226                     tag : 'i',
34227                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34228                     tooltip : 'This field is required'
34229                 });
34230             }
34231         }
34232         var items = {
34233             tag : 'div',
34234             cls : 'roo-radio-set-items'
34235         };
34236         
34237         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34238         
34239         if (align === 'left' && this.fieldLabel.length) {
34240             
34241             items = {
34242                 cls : "roo-radio-set-right", 
34243                 cn: [
34244                     items
34245                 ]
34246             };
34247             
34248             if(this.labelWidth > 12){
34249                 label.style = "width: " + this.labelWidth + 'px';
34250             }
34251             
34252             if(this.labelWidth < 13 && this.labelmd == 0){
34253                 this.labelmd = this.labelWidth;
34254             }
34255             
34256             if(this.labellg > 0){
34257                 label.cls += ' col-lg-' + this.labellg;
34258                 items.cls += ' col-lg-' + (12 - this.labellg);
34259             }
34260             
34261             if(this.labelmd > 0){
34262                 label.cls += ' col-md-' + this.labelmd;
34263                 items.cls += ' col-md-' + (12 - this.labelmd);
34264             }
34265             
34266             if(this.labelsm > 0){
34267                 label.cls += ' col-sm-' + this.labelsm;
34268                 items.cls += ' col-sm-' + (12 - this.labelsm);
34269             }
34270             
34271             if(this.labelxs > 0){
34272                 label.cls += ' col-xs-' + this.labelxs;
34273                 items.cls += ' col-xs-' + (12 - this.labelxs);
34274             }
34275         }
34276         
34277         var cfg = {
34278             tag : 'div',
34279             cls : 'roo-radio-set',
34280             cn : [
34281                 {
34282                     tag : 'input',
34283                     cls : 'roo-radio-set-input',
34284                     type : 'hidden',
34285                     name : this.name,
34286                     value : this.value ? this.value :  ''
34287                 },
34288                 label,
34289                 items
34290             ]
34291         };
34292         
34293         if(this.weight.length){
34294             cfg.cls += ' roo-radio-' + this.weight;
34295         }
34296         
34297         if(this.inline) {
34298             cfg.cls += ' roo-radio-set-inline';
34299         }
34300         
34301         var settings=this;
34302         ['xs','sm','md','lg'].map(function(size){
34303             if (settings[size]) {
34304                 cfg.cls += ' col-' + size + '-' + settings[size];
34305             }
34306         });
34307         
34308         return cfg;
34309         
34310     },
34311
34312     initEvents : function()
34313     {
34314         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34315         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34316         
34317         if(!this.fieldLabel.length){
34318             this.labelEl.hide();
34319         }
34320         
34321         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34322         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34323         
34324         this.indicator = this.indicatorEl();
34325         
34326         if(this.indicator){
34327             this.indicator.addClass('invisible');
34328         }
34329         
34330         this.originalValue = this.getValue();
34331         
34332     },
34333     
34334     inputEl: function ()
34335     {
34336         return this.el.select('.roo-radio-set-input', true).first();
34337     },
34338     
34339     getChildContainer : function()
34340     {
34341         return this.itemsEl;
34342     },
34343     
34344     register : function(item)
34345     {
34346         this.radioes.push(item);
34347         
34348     },
34349     
34350     validate : function()
34351     {   
34352         if(this.getVisibilityEl().hasClass('hidden')){
34353             return true;
34354         }
34355         
34356         var valid = false;
34357         
34358         Roo.each(this.radioes, function(i){
34359             if(!i.checked){
34360                 return;
34361             }
34362             
34363             valid = true;
34364             return false;
34365         });
34366         
34367         if(this.allowBlank) {
34368             return true;
34369         }
34370         
34371         if(this.disabled || valid){
34372             this.markValid();
34373             return true;
34374         }
34375         
34376         this.markInvalid();
34377         return false;
34378         
34379     },
34380     
34381     markValid : function()
34382     {
34383         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34384             this.indicatorEl().removeClass('visible');
34385             this.indicatorEl().addClass('invisible');
34386         }
34387         
34388         
34389         if (Roo.bootstrap.version == 3) {
34390             this.el.removeClass([this.invalidClass, this.validClass]);
34391             this.el.addClass(this.validClass);
34392         } else {
34393             this.el.removeClass(['is-invalid','is-valid']);
34394             this.el.addClass(['is-valid']);
34395         }
34396         this.fireEvent('valid', this);
34397     },
34398     
34399     markInvalid : function(msg)
34400     {
34401         if(this.allowBlank || this.disabled){
34402             return;
34403         }
34404         
34405         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34406             this.indicatorEl().removeClass('invisible');
34407             this.indicatorEl().addClass('visible');
34408         }
34409         if (Roo.bootstrap.version == 3) {
34410             this.el.removeClass([this.invalidClass, this.validClass]);
34411             this.el.addClass(this.invalidClass);
34412         } else {
34413             this.el.removeClass(['is-invalid','is-valid']);
34414             this.el.addClass(['is-invalid']);
34415         }
34416         
34417         this.fireEvent('invalid', this, msg);
34418         
34419     },
34420     
34421     setValue : function(v, suppressEvent)
34422     {   
34423         if(this.value === v){
34424             return;
34425         }
34426         
34427         this.value = v;
34428         
34429         if(this.rendered){
34430             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34431         }
34432         
34433         Roo.each(this.radioes, function(i){
34434             i.checked = false;
34435             i.el.removeClass('checked');
34436         });
34437         
34438         Roo.each(this.radioes, function(i){
34439             
34440             if(i.value === v || i.value.toString() === v.toString()){
34441                 i.checked = true;
34442                 i.el.addClass('checked');
34443                 
34444                 if(suppressEvent !== true){
34445                     this.fireEvent('check', this, i);
34446                 }
34447                 
34448                 return false;
34449             }
34450             
34451         }, this);
34452         
34453         this.validate();
34454     },
34455     
34456     clearInvalid : function(){
34457         
34458         if(!this.el || this.preventMark){
34459             return;
34460         }
34461         
34462         this.el.removeClass([this.invalidClass]);
34463         
34464         this.fireEvent('valid', this);
34465     }
34466     
34467 });
34468
34469 Roo.apply(Roo.bootstrap.RadioSet, {
34470     
34471     groups: {},
34472     
34473     register : function(set)
34474     {
34475         this.groups[set.name] = set;
34476     },
34477     
34478     get: function(name) 
34479     {
34480         if (typeof(this.groups[name]) == 'undefined') {
34481             return false;
34482         }
34483         
34484         return this.groups[name] ;
34485     }
34486     
34487 });
34488 /*
34489  * Based on:
34490  * Ext JS Library 1.1.1
34491  * Copyright(c) 2006-2007, Ext JS, LLC.
34492  *
34493  * Originally Released Under LGPL - original licence link has changed is not relivant.
34494  *
34495  * Fork - LGPL
34496  * <script type="text/javascript">
34497  */
34498
34499
34500 /**
34501  * @class Roo.bootstrap.SplitBar
34502  * @extends Roo.util.Observable
34503  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34504  * <br><br>
34505  * Usage:
34506  * <pre><code>
34507 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34508                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34509 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34510 split.minSize = 100;
34511 split.maxSize = 600;
34512 split.animate = true;
34513 split.on('moved', splitterMoved);
34514 </code></pre>
34515  * @constructor
34516  * Create a new SplitBar
34517  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34518  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34519  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34520  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34521                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34522                         position of the SplitBar).
34523  */
34524 Roo.bootstrap.SplitBar = function(cfg){
34525     
34526     /** @private */
34527     
34528     //{
34529     //  dragElement : elm
34530     //  resizingElement: el,
34531         // optional..
34532     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34533     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34534         // existingProxy ???
34535     //}
34536     
34537     this.el = Roo.get(cfg.dragElement, true);
34538     this.el.dom.unselectable = "on";
34539     /** @private */
34540     this.resizingEl = Roo.get(cfg.resizingElement, true);
34541
34542     /**
34543      * @private
34544      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34545      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34546      * @type Number
34547      */
34548     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34549     
34550     /**
34551      * The minimum size of the resizing element. (Defaults to 0)
34552      * @type Number
34553      */
34554     this.minSize = 0;
34555     
34556     /**
34557      * The maximum size of the resizing element. (Defaults to 2000)
34558      * @type Number
34559      */
34560     this.maxSize = 2000;
34561     
34562     /**
34563      * Whether to animate the transition to the new size
34564      * @type Boolean
34565      */
34566     this.animate = false;
34567     
34568     /**
34569      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34570      * @type Boolean
34571      */
34572     this.useShim = false;
34573     
34574     /** @private */
34575     this.shim = null;
34576     
34577     if(!cfg.existingProxy){
34578         /** @private */
34579         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34580     }else{
34581         this.proxy = Roo.get(cfg.existingProxy).dom;
34582     }
34583     /** @private */
34584     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34585     
34586     /** @private */
34587     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34588     
34589     /** @private */
34590     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34591     
34592     /** @private */
34593     this.dragSpecs = {};
34594     
34595     /**
34596      * @private The adapter to use to positon and resize elements
34597      */
34598     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34599     this.adapter.init(this);
34600     
34601     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34602         /** @private */
34603         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34604         this.el.addClass("roo-splitbar-h");
34605     }else{
34606         /** @private */
34607         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34608         this.el.addClass("roo-splitbar-v");
34609     }
34610     
34611     this.addEvents({
34612         /**
34613          * @event resize
34614          * Fires when the splitter is moved (alias for {@link #event-moved})
34615          * @param {Roo.bootstrap.SplitBar} this
34616          * @param {Number} newSize the new width or height
34617          */
34618         "resize" : true,
34619         /**
34620          * @event moved
34621          * Fires when the splitter is moved
34622          * @param {Roo.bootstrap.SplitBar} this
34623          * @param {Number} newSize the new width or height
34624          */
34625         "moved" : true,
34626         /**
34627          * @event beforeresize
34628          * Fires before the splitter is dragged
34629          * @param {Roo.bootstrap.SplitBar} this
34630          */
34631         "beforeresize" : true,
34632
34633         "beforeapply" : true
34634     });
34635
34636     Roo.util.Observable.call(this);
34637 };
34638
34639 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34640     onStartProxyDrag : function(x, y){
34641         this.fireEvent("beforeresize", this);
34642         if(!this.overlay){
34643             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34644             o.unselectable();
34645             o.enableDisplayMode("block");
34646             // all splitbars share the same overlay
34647             Roo.bootstrap.SplitBar.prototype.overlay = o;
34648         }
34649         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34650         this.overlay.show();
34651         Roo.get(this.proxy).setDisplayed("block");
34652         var size = this.adapter.getElementSize(this);
34653         this.activeMinSize = this.getMinimumSize();;
34654         this.activeMaxSize = this.getMaximumSize();;
34655         var c1 = size - this.activeMinSize;
34656         var c2 = Math.max(this.activeMaxSize - size, 0);
34657         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34658             this.dd.resetConstraints();
34659             this.dd.setXConstraint(
34660                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34661                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34662             );
34663             this.dd.setYConstraint(0, 0);
34664         }else{
34665             this.dd.resetConstraints();
34666             this.dd.setXConstraint(0, 0);
34667             this.dd.setYConstraint(
34668                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34669                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34670             );
34671          }
34672         this.dragSpecs.startSize = size;
34673         this.dragSpecs.startPoint = [x, y];
34674         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34675     },
34676     
34677     /** 
34678      * @private Called after the drag operation by the DDProxy
34679      */
34680     onEndProxyDrag : function(e){
34681         Roo.get(this.proxy).setDisplayed(false);
34682         var endPoint = Roo.lib.Event.getXY(e);
34683         if(this.overlay){
34684             this.overlay.hide();
34685         }
34686         var newSize;
34687         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34688             newSize = this.dragSpecs.startSize + 
34689                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34690                     endPoint[0] - this.dragSpecs.startPoint[0] :
34691                     this.dragSpecs.startPoint[0] - endPoint[0]
34692                 );
34693         }else{
34694             newSize = this.dragSpecs.startSize + 
34695                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34696                     endPoint[1] - this.dragSpecs.startPoint[1] :
34697                     this.dragSpecs.startPoint[1] - endPoint[1]
34698                 );
34699         }
34700         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34701         if(newSize != this.dragSpecs.startSize){
34702             if(this.fireEvent('beforeapply', this, newSize) !== false){
34703                 this.adapter.setElementSize(this, newSize);
34704                 this.fireEvent("moved", this, newSize);
34705                 this.fireEvent("resize", this, newSize);
34706             }
34707         }
34708     },
34709     
34710     /**
34711      * Get the adapter this SplitBar uses
34712      * @return The adapter object
34713      */
34714     getAdapter : function(){
34715         return this.adapter;
34716     },
34717     
34718     /**
34719      * Set the adapter this SplitBar uses
34720      * @param {Object} adapter A SplitBar adapter object
34721      */
34722     setAdapter : function(adapter){
34723         this.adapter = adapter;
34724         this.adapter.init(this);
34725     },
34726     
34727     /**
34728      * Gets the minimum size for the resizing element
34729      * @return {Number} The minimum size
34730      */
34731     getMinimumSize : function(){
34732         return this.minSize;
34733     },
34734     
34735     /**
34736      * Sets the minimum size for the resizing element
34737      * @param {Number} minSize The minimum size
34738      */
34739     setMinimumSize : function(minSize){
34740         this.minSize = minSize;
34741     },
34742     
34743     /**
34744      * Gets the maximum size for the resizing element
34745      * @return {Number} The maximum size
34746      */
34747     getMaximumSize : function(){
34748         return this.maxSize;
34749     },
34750     
34751     /**
34752      * Sets the maximum size for the resizing element
34753      * @param {Number} maxSize The maximum size
34754      */
34755     setMaximumSize : function(maxSize){
34756         this.maxSize = maxSize;
34757     },
34758     
34759     /**
34760      * Sets the initialize size for the resizing element
34761      * @param {Number} size The initial size
34762      */
34763     setCurrentSize : function(size){
34764         var oldAnimate = this.animate;
34765         this.animate = false;
34766         this.adapter.setElementSize(this, size);
34767         this.animate = oldAnimate;
34768     },
34769     
34770     /**
34771      * Destroy this splitbar. 
34772      * @param {Boolean} removeEl True to remove the element
34773      */
34774     destroy : function(removeEl){
34775         if(this.shim){
34776             this.shim.remove();
34777         }
34778         this.dd.unreg();
34779         this.proxy.parentNode.removeChild(this.proxy);
34780         if(removeEl){
34781             this.el.remove();
34782         }
34783     }
34784 });
34785
34786 /**
34787  * @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.
34788  */
34789 Roo.bootstrap.SplitBar.createProxy = function(dir){
34790     var proxy = new Roo.Element(document.createElement("div"));
34791     proxy.unselectable();
34792     var cls = 'roo-splitbar-proxy';
34793     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34794     document.body.appendChild(proxy.dom);
34795     return proxy.dom;
34796 };
34797
34798 /** 
34799  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34800  * Default Adapter. It assumes the splitter and resizing element are not positioned
34801  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34802  */
34803 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34804 };
34805
34806 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34807     // do nothing for now
34808     init : function(s){
34809     
34810     },
34811     /**
34812      * Called before drag operations to get the current size of the resizing element. 
34813      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34814      */
34815      getElementSize : function(s){
34816         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34817             return s.resizingEl.getWidth();
34818         }else{
34819             return s.resizingEl.getHeight();
34820         }
34821     },
34822     
34823     /**
34824      * Called after drag operations to set the size of the resizing element.
34825      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34826      * @param {Number} newSize The new size to set
34827      * @param {Function} onComplete A function to be invoked when resizing is complete
34828      */
34829     setElementSize : function(s, newSize, onComplete){
34830         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34831             if(!s.animate){
34832                 s.resizingEl.setWidth(newSize);
34833                 if(onComplete){
34834                     onComplete(s, newSize);
34835                 }
34836             }else{
34837                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34838             }
34839         }else{
34840             
34841             if(!s.animate){
34842                 s.resizingEl.setHeight(newSize);
34843                 if(onComplete){
34844                     onComplete(s, newSize);
34845                 }
34846             }else{
34847                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34848             }
34849         }
34850     }
34851 };
34852
34853 /** 
34854  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34855  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34856  * Adapter that  moves the splitter element to align with the resized sizing element. 
34857  * Used with an absolute positioned SplitBar.
34858  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34859  * document.body, make sure you assign an id to the body element.
34860  */
34861 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34862     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34863     this.container = Roo.get(container);
34864 };
34865
34866 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34867     init : function(s){
34868         this.basic.init(s);
34869     },
34870     
34871     getElementSize : function(s){
34872         return this.basic.getElementSize(s);
34873     },
34874     
34875     setElementSize : function(s, newSize, onComplete){
34876         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34877     },
34878     
34879     moveSplitter : function(s){
34880         var yes = Roo.bootstrap.SplitBar;
34881         switch(s.placement){
34882             case yes.LEFT:
34883                 s.el.setX(s.resizingEl.getRight());
34884                 break;
34885             case yes.RIGHT:
34886                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34887                 break;
34888             case yes.TOP:
34889                 s.el.setY(s.resizingEl.getBottom());
34890                 break;
34891             case yes.BOTTOM:
34892                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34893                 break;
34894         }
34895     }
34896 };
34897
34898 /**
34899  * Orientation constant - Create a vertical SplitBar
34900  * @static
34901  * @type Number
34902  */
34903 Roo.bootstrap.SplitBar.VERTICAL = 1;
34904
34905 /**
34906  * Orientation constant - Create a horizontal SplitBar
34907  * @static
34908  * @type Number
34909  */
34910 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34911
34912 /**
34913  * Placement constant - The resizing element is to the left of the splitter element
34914  * @static
34915  * @type Number
34916  */
34917 Roo.bootstrap.SplitBar.LEFT = 1;
34918
34919 /**
34920  * Placement constant - The resizing element is to the right of the splitter element
34921  * @static
34922  * @type Number
34923  */
34924 Roo.bootstrap.SplitBar.RIGHT = 2;
34925
34926 /**
34927  * Placement constant - The resizing element is positioned above the splitter element
34928  * @static
34929  * @type Number
34930  */
34931 Roo.bootstrap.SplitBar.TOP = 3;
34932
34933 /**
34934  * Placement constant - The resizing element is positioned under splitter element
34935  * @static
34936  * @type Number
34937  */
34938 Roo.bootstrap.SplitBar.BOTTOM = 4;
34939 Roo.namespace("Roo.bootstrap.layout");/*
34940  * Based on:
34941  * Ext JS Library 1.1.1
34942  * Copyright(c) 2006-2007, Ext JS, LLC.
34943  *
34944  * Originally Released Under LGPL - original licence link has changed is not relivant.
34945  *
34946  * Fork - LGPL
34947  * <script type="text/javascript">
34948  */
34949
34950 /**
34951  * @class Roo.bootstrap.layout.Manager
34952  * @extends Roo.bootstrap.Component
34953  * Base class for layout managers.
34954  */
34955 Roo.bootstrap.layout.Manager = function(config)
34956 {
34957     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34958
34959
34960
34961
34962
34963     /** false to disable window resize monitoring @type Boolean */
34964     this.monitorWindowResize = true;
34965     this.regions = {};
34966     this.addEvents({
34967         /**
34968          * @event layout
34969          * Fires when a layout is performed.
34970          * @param {Roo.LayoutManager} this
34971          */
34972         "layout" : true,
34973         /**
34974          * @event regionresized
34975          * Fires when the user resizes a region.
34976          * @param {Roo.LayoutRegion} region The resized region
34977          * @param {Number} newSize The new size (width for east/west, height for north/south)
34978          */
34979         "regionresized" : true,
34980         /**
34981          * @event regioncollapsed
34982          * Fires when a region is collapsed.
34983          * @param {Roo.LayoutRegion} region The collapsed region
34984          */
34985         "regioncollapsed" : true,
34986         /**
34987          * @event regionexpanded
34988          * Fires when a region is expanded.
34989          * @param {Roo.LayoutRegion} region The expanded region
34990          */
34991         "regionexpanded" : true
34992     });
34993     this.updating = false;
34994
34995     if (config.el) {
34996         this.el = Roo.get(config.el);
34997         this.initEvents();
34998     }
34999
35000 };
35001
35002 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
35003
35004
35005     regions : null,
35006
35007     monitorWindowResize : true,
35008
35009
35010     updating : false,
35011
35012
35013     onRender : function(ct, position)
35014     {
35015         if(!this.el){
35016             this.el = Roo.get(ct);
35017             this.initEvents();
35018         }
35019         //this.fireEvent('render',this);
35020     },
35021
35022
35023     initEvents: function()
35024     {
35025
35026
35027         // ie scrollbar fix
35028         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
35029             document.body.scroll = "no";
35030         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
35031             this.el.position('relative');
35032         }
35033         this.id = this.el.id;
35034         this.el.addClass("roo-layout-container");
35035         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
35036         if(this.el.dom != document.body ) {
35037             this.el.on('resize', this.layout,this);
35038             this.el.on('show', this.layout,this);
35039         }
35040
35041     },
35042
35043     /**
35044      * Returns true if this layout is currently being updated
35045      * @return {Boolean}
35046      */
35047     isUpdating : function(){
35048         return this.updating;
35049     },
35050
35051     /**
35052      * Suspend the LayoutManager from doing auto-layouts while
35053      * making multiple add or remove calls
35054      */
35055     beginUpdate : function(){
35056         this.updating = true;
35057     },
35058
35059     /**
35060      * Restore auto-layouts and optionally disable the manager from performing a layout
35061      * @param {Boolean} noLayout true to disable a layout update
35062      */
35063     endUpdate : function(noLayout){
35064         this.updating = false;
35065         if(!noLayout){
35066             this.layout();
35067         }
35068     },
35069
35070     layout: function(){
35071         // abstract...
35072     },
35073
35074     onRegionResized : function(region, newSize){
35075         this.fireEvent("regionresized", region, newSize);
35076         this.layout();
35077     },
35078
35079     onRegionCollapsed : function(region){
35080         this.fireEvent("regioncollapsed", region);
35081     },
35082
35083     onRegionExpanded : function(region){
35084         this.fireEvent("regionexpanded", region);
35085     },
35086
35087     /**
35088      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
35089      * performs box-model adjustments.
35090      * @return {Object} The size as an object {width: (the width), height: (the height)}
35091      */
35092     getViewSize : function()
35093     {
35094         var size;
35095         if(this.el.dom != document.body){
35096             size = this.el.getSize();
35097         }else{
35098             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
35099         }
35100         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
35101         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35102         return size;
35103     },
35104
35105     /**
35106      * Returns the Element this layout is bound to.
35107      * @return {Roo.Element}
35108      */
35109     getEl : function(){
35110         return this.el;
35111     },
35112
35113     /**
35114      * Returns the specified region.
35115      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
35116      * @return {Roo.LayoutRegion}
35117      */
35118     getRegion : function(target){
35119         return this.regions[target.toLowerCase()];
35120     },
35121
35122     onWindowResize : function(){
35123         if(this.monitorWindowResize){
35124             this.layout();
35125         }
35126     }
35127 });
35128 /*
35129  * Based on:
35130  * Ext JS Library 1.1.1
35131  * Copyright(c) 2006-2007, Ext JS, LLC.
35132  *
35133  * Originally Released Under LGPL - original licence link has changed is not relivant.
35134  *
35135  * Fork - LGPL
35136  * <script type="text/javascript">
35137  */
35138 /**
35139  * @class Roo.bootstrap.layout.Border
35140  * @extends Roo.bootstrap.layout.Manager
35141  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
35142  * please see: examples/bootstrap/nested.html<br><br>
35143  
35144 <b>The container the layout is rendered into can be either the body element or any other element.
35145 If it is not the body element, the container needs to either be an absolute positioned element,
35146 or you will need to add "position:relative" to the css of the container.  You will also need to specify
35147 the container size if it is not the body element.</b>
35148
35149 * @constructor
35150 * Create a new Border
35151 * @param {Object} config Configuration options
35152  */
35153 Roo.bootstrap.layout.Border = function(config){
35154     config = config || {};
35155     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
35156     
35157     
35158     
35159     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35160         if(config[region]){
35161             config[region].region = region;
35162             this.addRegion(config[region]);
35163         }
35164     },this);
35165     
35166 };
35167
35168 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
35169
35170 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
35171     
35172     parent : false, // this might point to a 'nest' or a ???
35173     
35174     /**
35175      * Creates and adds a new region if it doesn't already exist.
35176      * @param {String} target The target region key (north, south, east, west or center).
35177      * @param {Object} config The regions config object
35178      * @return {BorderLayoutRegion} The new region
35179      */
35180     addRegion : function(config)
35181     {
35182         if(!this.regions[config.region]){
35183             var r = this.factory(config);
35184             this.bindRegion(r);
35185         }
35186         return this.regions[config.region];
35187     },
35188
35189     // private (kinda)
35190     bindRegion : function(r){
35191         this.regions[r.config.region] = r;
35192         
35193         r.on("visibilitychange",    this.layout, this);
35194         r.on("paneladded",          this.layout, this);
35195         r.on("panelremoved",        this.layout, this);
35196         r.on("invalidated",         this.layout, this);
35197         r.on("resized",             this.onRegionResized, this);
35198         r.on("collapsed",           this.onRegionCollapsed, this);
35199         r.on("expanded",            this.onRegionExpanded, this);
35200     },
35201
35202     /**
35203      * Performs a layout update.
35204      */
35205     layout : function()
35206     {
35207         if(this.updating) {
35208             return;
35209         }
35210         
35211         // render all the rebions if they have not been done alreayd?
35212         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35213             if(this.regions[region] && !this.regions[region].bodyEl){
35214                 this.regions[region].onRender(this.el)
35215             }
35216         },this);
35217         
35218         var size = this.getViewSize();
35219         var w = size.width;
35220         var h = size.height;
35221         var centerW = w;
35222         var centerH = h;
35223         var centerY = 0;
35224         var centerX = 0;
35225         //var x = 0, y = 0;
35226
35227         var rs = this.regions;
35228         var north = rs["north"];
35229         var south = rs["south"]; 
35230         var west = rs["west"];
35231         var east = rs["east"];
35232         var center = rs["center"];
35233         //if(this.hideOnLayout){ // not supported anymore
35234             //c.el.setStyle("display", "none");
35235         //}
35236         if(north && north.isVisible()){
35237             var b = north.getBox();
35238             var m = north.getMargins();
35239             b.width = w - (m.left+m.right);
35240             b.x = m.left;
35241             b.y = m.top;
35242             centerY = b.height + b.y + m.bottom;
35243             centerH -= centerY;
35244             north.updateBox(this.safeBox(b));
35245         }
35246         if(south && south.isVisible()){
35247             var b = south.getBox();
35248             var m = south.getMargins();
35249             b.width = w - (m.left+m.right);
35250             b.x = m.left;
35251             var totalHeight = (b.height + m.top + m.bottom);
35252             b.y = h - totalHeight + m.top;
35253             centerH -= totalHeight;
35254             south.updateBox(this.safeBox(b));
35255         }
35256         if(west && west.isVisible()){
35257             var b = west.getBox();
35258             var m = west.getMargins();
35259             b.height = centerH - (m.top+m.bottom);
35260             b.x = m.left;
35261             b.y = centerY + m.top;
35262             var totalWidth = (b.width + m.left + m.right);
35263             centerX += totalWidth;
35264             centerW -= totalWidth;
35265             west.updateBox(this.safeBox(b));
35266         }
35267         if(east && east.isVisible()){
35268             var b = east.getBox();
35269             var m = east.getMargins();
35270             b.height = centerH - (m.top+m.bottom);
35271             var totalWidth = (b.width + m.left + m.right);
35272             b.x = w - totalWidth + m.left;
35273             b.y = centerY + m.top;
35274             centerW -= totalWidth;
35275             east.updateBox(this.safeBox(b));
35276         }
35277         if(center){
35278             var m = center.getMargins();
35279             var centerBox = {
35280                 x: centerX + m.left,
35281                 y: centerY + m.top,
35282                 width: centerW - (m.left+m.right),
35283                 height: centerH - (m.top+m.bottom)
35284             };
35285             //if(this.hideOnLayout){
35286                 //center.el.setStyle("display", "block");
35287             //}
35288             center.updateBox(this.safeBox(centerBox));
35289         }
35290         this.el.repaint();
35291         this.fireEvent("layout", this);
35292     },
35293
35294     // private
35295     safeBox : function(box){
35296         box.width = Math.max(0, box.width);
35297         box.height = Math.max(0, box.height);
35298         return box;
35299     },
35300
35301     /**
35302      * Adds a ContentPanel (or subclass) to this layout.
35303      * @param {String} target The target region key (north, south, east, west or center).
35304      * @param {Roo.ContentPanel} panel The panel to add
35305      * @return {Roo.ContentPanel} The added panel
35306      */
35307     add : function(target, panel){
35308          
35309         target = target.toLowerCase();
35310         return this.regions[target].add(panel);
35311     },
35312
35313     /**
35314      * Remove a ContentPanel (or subclass) to this layout.
35315      * @param {String} target The target region key (north, south, east, west or center).
35316      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35317      * @return {Roo.ContentPanel} The removed panel
35318      */
35319     remove : function(target, panel){
35320         target = target.toLowerCase();
35321         return this.regions[target].remove(panel);
35322     },
35323
35324     /**
35325      * Searches all regions for a panel with the specified id
35326      * @param {String} panelId
35327      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35328      */
35329     findPanel : function(panelId){
35330         var rs = this.regions;
35331         for(var target in rs){
35332             if(typeof rs[target] != "function"){
35333                 var p = rs[target].getPanel(panelId);
35334                 if(p){
35335                     return p;
35336                 }
35337             }
35338         }
35339         return null;
35340     },
35341
35342     /**
35343      * Searches all regions for a panel with the specified id and activates (shows) it.
35344      * @param {String/ContentPanel} panelId The panels id or the panel itself
35345      * @return {Roo.ContentPanel} The shown panel or null
35346      */
35347     showPanel : function(panelId) {
35348       var rs = this.regions;
35349       for(var target in rs){
35350          var r = rs[target];
35351          if(typeof r != "function"){
35352             if(r.hasPanel(panelId)){
35353                return r.showPanel(panelId);
35354             }
35355          }
35356       }
35357       return null;
35358    },
35359
35360    /**
35361      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35362      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35363      */
35364    /*
35365     restoreState : function(provider){
35366         if(!provider){
35367             provider = Roo.state.Manager;
35368         }
35369         var sm = new Roo.LayoutStateManager();
35370         sm.init(this, provider);
35371     },
35372 */
35373  
35374  
35375     /**
35376      * Adds a xtype elements to the layout.
35377      * <pre><code>
35378
35379 layout.addxtype({
35380        xtype : 'ContentPanel',
35381        region: 'west',
35382        items: [ .... ]
35383    }
35384 );
35385
35386 layout.addxtype({
35387         xtype : 'NestedLayoutPanel',
35388         region: 'west',
35389         layout: {
35390            center: { },
35391            west: { }   
35392         },
35393         items : [ ... list of content panels or nested layout panels.. ]
35394    }
35395 );
35396 </code></pre>
35397      * @param {Object} cfg Xtype definition of item to add.
35398      */
35399     addxtype : function(cfg)
35400     {
35401         // basically accepts a pannel...
35402         // can accept a layout region..!?!?
35403         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35404         
35405         
35406         // theory?  children can only be panels??
35407         
35408         //if (!cfg.xtype.match(/Panel$/)) {
35409         //    return false;
35410         //}
35411         var ret = false;
35412         
35413         if (typeof(cfg.region) == 'undefined') {
35414             Roo.log("Failed to add Panel, region was not set");
35415             Roo.log(cfg);
35416             return false;
35417         }
35418         var region = cfg.region;
35419         delete cfg.region;
35420         
35421           
35422         var xitems = [];
35423         if (cfg.items) {
35424             xitems = cfg.items;
35425             delete cfg.items;
35426         }
35427         var nb = false;
35428         
35429         if ( region == 'center') {
35430             Roo.log("Center: " + cfg.title);
35431         }
35432         
35433         
35434         switch(cfg.xtype) 
35435         {
35436             case 'Content':  // ContentPanel (el, cfg)
35437             case 'Scroll':  // ContentPanel (el, cfg)
35438             case 'View': 
35439                 cfg.autoCreate = cfg.autoCreate || true;
35440                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35441                 //} else {
35442                 //    var el = this.el.createChild();
35443                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35444                 //}
35445                 
35446                 this.add(region, ret);
35447                 break;
35448             
35449             /*
35450             case 'TreePanel': // our new panel!
35451                 cfg.el = this.el.createChild();
35452                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35453                 this.add(region, ret);
35454                 break;
35455             */
35456             
35457             case 'Nest': 
35458                 // create a new Layout (which is  a Border Layout...
35459                 
35460                 var clayout = cfg.layout;
35461                 clayout.el  = this.el.createChild();
35462                 clayout.items   = clayout.items  || [];
35463                 
35464                 delete cfg.layout;
35465                 
35466                 // replace this exitems with the clayout ones..
35467                 xitems = clayout.items;
35468                  
35469                 // force background off if it's in center...
35470                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35471                     cfg.background = false;
35472                 }
35473                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35474                 
35475                 
35476                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35477                 //console.log('adding nested layout panel '  + cfg.toSource());
35478                 this.add(region, ret);
35479                 nb = {}; /// find first...
35480                 break;
35481             
35482             case 'Grid':
35483                 
35484                 // needs grid and region
35485                 
35486                 //var el = this.getRegion(region).el.createChild();
35487                 /*
35488                  *var el = this.el.createChild();
35489                 // create the grid first...
35490                 cfg.grid.container = el;
35491                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35492                 */
35493                 
35494                 if (region == 'center' && this.active ) {
35495                     cfg.background = false;
35496                 }
35497                 
35498                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35499                 
35500                 this.add(region, ret);
35501                 /*
35502                 if (cfg.background) {
35503                     // render grid on panel activation (if panel background)
35504                     ret.on('activate', function(gp) {
35505                         if (!gp.grid.rendered) {
35506                     //        gp.grid.render(el);
35507                         }
35508                     });
35509                 } else {
35510                   //  cfg.grid.render(el);
35511                 }
35512                 */
35513                 break;
35514            
35515            
35516             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35517                 // it was the old xcomponent building that caused this before.
35518                 // espeically if border is the top element in the tree.
35519                 ret = this;
35520                 break; 
35521                 
35522                     
35523                 
35524                 
35525                 
35526             default:
35527                 /*
35528                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35529                     
35530                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35531                     this.add(region, ret);
35532                 } else {
35533                 */
35534                     Roo.log(cfg);
35535                     throw "Can not add '" + cfg.xtype + "' to Border";
35536                     return null;
35537              
35538                                 
35539              
35540         }
35541         this.beginUpdate();
35542         // add children..
35543         var region = '';
35544         var abn = {};
35545         Roo.each(xitems, function(i)  {
35546             region = nb && i.region ? i.region : false;
35547             
35548             var add = ret.addxtype(i);
35549            
35550             if (region) {
35551                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35552                 if (!i.background) {
35553                     abn[region] = nb[region] ;
35554                 }
35555             }
35556             
35557         });
35558         this.endUpdate();
35559
35560         // make the last non-background panel active..
35561         //if (nb) { Roo.log(abn); }
35562         if (nb) {
35563             
35564             for(var r in abn) {
35565                 region = this.getRegion(r);
35566                 if (region) {
35567                     // tried using nb[r], but it does not work..
35568                      
35569                     region.showPanel(abn[r]);
35570                    
35571                 }
35572             }
35573         }
35574         return ret;
35575         
35576     },
35577     
35578     
35579 // private
35580     factory : function(cfg)
35581     {
35582         
35583         var validRegions = Roo.bootstrap.layout.Border.regions;
35584
35585         var target = cfg.region;
35586         cfg.mgr = this;
35587         
35588         var r = Roo.bootstrap.layout;
35589         Roo.log(target);
35590         switch(target){
35591             case "north":
35592                 return new r.North(cfg);
35593             case "south":
35594                 return new r.South(cfg);
35595             case "east":
35596                 return new r.East(cfg);
35597             case "west":
35598                 return new r.West(cfg);
35599             case "center":
35600                 return new r.Center(cfg);
35601         }
35602         throw 'Layout region "'+target+'" not supported.';
35603     }
35604     
35605     
35606 });
35607  /*
35608  * Based on:
35609  * Ext JS Library 1.1.1
35610  * Copyright(c) 2006-2007, Ext JS, LLC.
35611  *
35612  * Originally Released Under LGPL - original licence link has changed is not relivant.
35613  *
35614  * Fork - LGPL
35615  * <script type="text/javascript">
35616  */
35617  
35618 /**
35619  * @class Roo.bootstrap.layout.Basic
35620  * @extends Roo.util.Observable
35621  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35622  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35623  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35624  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35625  * @cfg {string}   region  the region that it inhabits..
35626  * @cfg {bool}   skipConfig skip config?
35627  * 
35628
35629  */
35630 Roo.bootstrap.layout.Basic = function(config){
35631     
35632     this.mgr = config.mgr;
35633     
35634     this.position = config.region;
35635     
35636     var skipConfig = config.skipConfig;
35637     
35638     this.events = {
35639         /**
35640          * @scope Roo.BasicLayoutRegion
35641          */
35642         
35643         /**
35644          * @event beforeremove
35645          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35646          * @param {Roo.LayoutRegion} this
35647          * @param {Roo.ContentPanel} panel The panel
35648          * @param {Object} e The cancel event object
35649          */
35650         "beforeremove" : true,
35651         /**
35652          * @event invalidated
35653          * Fires when the layout for this region is changed.
35654          * @param {Roo.LayoutRegion} this
35655          */
35656         "invalidated" : true,
35657         /**
35658          * @event visibilitychange
35659          * Fires when this region is shown or hidden 
35660          * @param {Roo.LayoutRegion} this
35661          * @param {Boolean} visibility true or false
35662          */
35663         "visibilitychange" : true,
35664         /**
35665          * @event paneladded
35666          * Fires when a panel is added. 
35667          * @param {Roo.LayoutRegion} this
35668          * @param {Roo.ContentPanel} panel The panel
35669          */
35670         "paneladded" : true,
35671         /**
35672          * @event panelremoved
35673          * Fires when a panel is removed. 
35674          * @param {Roo.LayoutRegion} this
35675          * @param {Roo.ContentPanel} panel The panel
35676          */
35677         "panelremoved" : true,
35678         /**
35679          * @event beforecollapse
35680          * Fires when this region before collapse.
35681          * @param {Roo.LayoutRegion} this
35682          */
35683         "beforecollapse" : true,
35684         /**
35685          * @event collapsed
35686          * Fires when this region is collapsed.
35687          * @param {Roo.LayoutRegion} this
35688          */
35689         "collapsed" : true,
35690         /**
35691          * @event expanded
35692          * Fires when this region is expanded.
35693          * @param {Roo.LayoutRegion} this
35694          */
35695         "expanded" : true,
35696         /**
35697          * @event slideshow
35698          * Fires when this region is slid into view.
35699          * @param {Roo.LayoutRegion} this
35700          */
35701         "slideshow" : true,
35702         /**
35703          * @event slidehide
35704          * Fires when this region slides out of view. 
35705          * @param {Roo.LayoutRegion} this
35706          */
35707         "slidehide" : true,
35708         /**
35709          * @event panelactivated
35710          * Fires when a panel is activated. 
35711          * @param {Roo.LayoutRegion} this
35712          * @param {Roo.ContentPanel} panel The activated panel
35713          */
35714         "panelactivated" : true,
35715         /**
35716          * @event resized
35717          * Fires when the user resizes this region. 
35718          * @param {Roo.LayoutRegion} this
35719          * @param {Number} newSize The new size (width for east/west, height for north/south)
35720          */
35721         "resized" : true
35722     };
35723     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35724     this.panels = new Roo.util.MixedCollection();
35725     this.panels.getKey = this.getPanelId.createDelegate(this);
35726     this.box = null;
35727     this.activePanel = null;
35728     // ensure listeners are added...
35729     
35730     if (config.listeners || config.events) {
35731         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35732             listeners : config.listeners || {},
35733             events : config.events || {}
35734         });
35735     }
35736     
35737     if(skipConfig !== true){
35738         this.applyConfig(config);
35739     }
35740 };
35741
35742 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35743 {
35744     getPanelId : function(p){
35745         return p.getId();
35746     },
35747     
35748     applyConfig : function(config){
35749         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35750         this.config = config;
35751         
35752     },
35753     
35754     /**
35755      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35756      * the width, for horizontal (north, south) the height.
35757      * @param {Number} newSize The new width or height
35758      */
35759     resizeTo : function(newSize){
35760         var el = this.el ? this.el :
35761                  (this.activePanel ? this.activePanel.getEl() : null);
35762         if(el){
35763             switch(this.position){
35764                 case "east":
35765                 case "west":
35766                     el.setWidth(newSize);
35767                     this.fireEvent("resized", this, newSize);
35768                 break;
35769                 case "north":
35770                 case "south":
35771                     el.setHeight(newSize);
35772                     this.fireEvent("resized", this, newSize);
35773                 break;                
35774             }
35775         }
35776     },
35777     
35778     getBox : function(){
35779         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35780     },
35781     
35782     getMargins : function(){
35783         return this.margins;
35784     },
35785     
35786     updateBox : function(box){
35787         this.box = box;
35788         var el = this.activePanel.getEl();
35789         el.dom.style.left = box.x + "px";
35790         el.dom.style.top = box.y + "px";
35791         this.activePanel.setSize(box.width, box.height);
35792     },
35793     
35794     /**
35795      * Returns the container element for this region.
35796      * @return {Roo.Element}
35797      */
35798     getEl : function(){
35799         return this.activePanel;
35800     },
35801     
35802     /**
35803      * Returns true if this region is currently visible.
35804      * @return {Boolean}
35805      */
35806     isVisible : function(){
35807         return this.activePanel ? true : false;
35808     },
35809     
35810     setActivePanel : function(panel){
35811         panel = this.getPanel(panel);
35812         if(this.activePanel && this.activePanel != panel){
35813             this.activePanel.setActiveState(false);
35814             this.activePanel.getEl().setLeftTop(-10000,-10000);
35815         }
35816         this.activePanel = panel;
35817         panel.setActiveState(true);
35818         if(this.box){
35819             panel.setSize(this.box.width, this.box.height);
35820         }
35821         this.fireEvent("panelactivated", this, panel);
35822         this.fireEvent("invalidated");
35823     },
35824     
35825     /**
35826      * Show the specified panel.
35827      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35828      * @return {Roo.ContentPanel} The shown panel or null
35829      */
35830     showPanel : function(panel){
35831         panel = this.getPanel(panel);
35832         if(panel){
35833             this.setActivePanel(panel);
35834         }
35835         return panel;
35836     },
35837     
35838     /**
35839      * Get the active panel for this region.
35840      * @return {Roo.ContentPanel} The active panel or null
35841      */
35842     getActivePanel : function(){
35843         return this.activePanel;
35844     },
35845     
35846     /**
35847      * Add the passed ContentPanel(s)
35848      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35849      * @return {Roo.ContentPanel} The panel added (if only one was added)
35850      */
35851     add : function(panel){
35852         if(arguments.length > 1){
35853             for(var i = 0, len = arguments.length; i < len; i++) {
35854                 this.add(arguments[i]);
35855             }
35856             return null;
35857         }
35858         if(this.hasPanel(panel)){
35859             this.showPanel(panel);
35860             return panel;
35861         }
35862         var el = panel.getEl();
35863         if(el.dom.parentNode != this.mgr.el.dom){
35864             this.mgr.el.dom.appendChild(el.dom);
35865         }
35866         if(panel.setRegion){
35867             panel.setRegion(this);
35868         }
35869         this.panels.add(panel);
35870         el.setStyle("position", "absolute");
35871         if(!panel.background){
35872             this.setActivePanel(panel);
35873             if(this.config.initialSize && this.panels.getCount()==1){
35874                 this.resizeTo(this.config.initialSize);
35875             }
35876         }
35877         this.fireEvent("paneladded", this, panel);
35878         return panel;
35879     },
35880     
35881     /**
35882      * Returns true if the panel is in this region.
35883      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35884      * @return {Boolean}
35885      */
35886     hasPanel : function(panel){
35887         if(typeof panel == "object"){ // must be panel obj
35888             panel = panel.getId();
35889         }
35890         return this.getPanel(panel) ? true : false;
35891     },
35892     
35893     /**
35894      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35895      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35896      * @param {Boolean} preservePanel Overrides the config preservePanel option
35897      * @return {Roo.ContentPanel} The panel that was removed
35898      */
35899     remove : function(panel, preservePanel){
35900         panel = this.getPanel(panel);
35901         if(!panel){
35902             return null;
35903         }
35904         var e = {};
35905         this.fireEvent("beforeremove", this, panel, e);
35906         if(e.cancel === true){
35907             return null;
35908         }
35909         var panelId = panel.getId();
35910         this.panels.removeKey(panelId);
35911         return panel;
35912     },
35913     
35914     /**
35915      * Returns the panel specified or null if it's not in this region.
35916      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35917      * @return {Roo.ContentPanel}
35918      */
35919     getPanel : function(id){
35920         if(typeof id == "object"){ // must be panel obj
35921             return id;
35922         }
35923         return this.panels.get(id);
35924     },
35925     
35926     /**
35927      * Returns this regions position (north/south/east/west/center).
35928      * @return {String} 
35929      */
35930     getPosition: function(){
35931         return this.position;    
35932     }
35933 });/*
35934  * Based on:
35935  * Ext JS Library 1.1.1
35936  * Copyright(c) 2006-2007, Ext JS, LLC.
35937  *
35938  * Originally Released Under LGPL - original licence link has changed is not relivant.
35939  *
35940  * Fork - LGPL
35941  * <script type="text/javascript">
35942  */
35943  
35944 /**
35945  * @class Roo.bootstrap.layout.Region
35946  * @extends Roo.bootstrap.layout.Basic
35947  * This class represents a region in a layout manager.
35948  
35949  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35950  * @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})
35951  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35952  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35953  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35954  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35955  * @cfg {String}    title           The title for the region (overrides panel titles)
35956  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35957  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35958  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35959  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35960  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35961  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35962  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35963  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35964  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35965  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35966
35967  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35968  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35969  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35970  * @cfg {Number}    width           For East/West panels
35971  * @cfg {Number}    height          For North/South panels
35972  * @cfg {Boolean}   split           To show the splitter
35973  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35974  * 
35975  * @cfg {string}   cls             Extra CSS classes to add to region
35976  * 
35977  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35978  * @cfg {string}   region  the region that it inhabits..
35979  *
35980
35981  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35982  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35983
35984  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35985  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35986  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35987  */
35988 Roo.bootstrap.layout.Region = function(config)
35989 {
35990     this.applyConfig(config);
35991
35992     var mgr = config.mgr;
35993     var pos = config.region;
35994     config.skipConfig = true;
35995     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35996     
35997     if (mgr.el) {
35998         this.onRender(mgr.el);   
35999     }
36000      
36001     this.visible = true;
36002     this.collapsed = false;
36003     this.unrendered_panels = [];
36004 };
36005
36006 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
36007
36008     position: '', // set by wrapper (eg. north/south etc..)
36009     unrendered_panels : null,  // unrendered panels.
36010     
36011     tabPosition : false,
36012     
36013     mgr: false, // points to 'Border'
36014     
36015     
36016     createBody : function(){
36017         /** This region's body element 
36018         * @type Roo.Element */
36019         this.bodyEl = this.el.createChild({
36020                 tag: "div",
36021                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
36022         });
36023     },
36024
36025     onRender: function(ctr, pos)
36026     {
36027         var dh = Roo.DomHelper;
36028         /** This region's container element 
36029         * @type Roo.Element */
36030         this.el = dh.append(ctr.dom, {
36031                 tag: "div",
36032                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
36033             }, true);
36034         /** This region's title element 
36035         * @type Roo.Element */
36036     
36037         this.titleEl = dh.append(this.el.dom,  {
36038                 tag: "div",
36039                 unselectable: "on",
36040                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
36041                 children:[
36042                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
36043                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
36044                 ]
36045             }, true);
36046         
36047         this.titleEl.enableDisplayMode();
36048         /** This region's title text element 
36049         * @type HTMLElement */
36050         this.titleTextEl = this.titleEl.dom.firstChild;
36051         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
36052         /*
36053         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
36054         this.closeBtn.enableDisplayMode();
36055         this.closeBtn.on("click", this.closeClicked, this);
36056         this.closeBtn.hide();
36057     */
36058         this.createBody(this.config);
36059         if(this.config.hideWhenEmpty){
36060             this.hide();
36061             this.on("paneladded", this.validateVisibility, this);
36062             this.on("panelremoved", this.validateVisibility, this);
36063         }
36064         if(this.autoScroll){
36065             this.bodyEl.setStyle("overflow", "auto");
36066         }else{
36067             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
36068         }
36069         //if(c.titlebar !== false){
36070             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
36071                 this.titleEl.hide();
36072             }else{
36073                 this.titleEl.show();
36074                 if(this.config.title){
36075                     this.titleTextEl.innerHTML = this.config.title;
36076                 }
36077             }
36078         //}
36079         if(this.config.collapsed){
36080             this.collapse(true);
36081         }
36082         if(this.config.hidden){
36083             this.hide();
36084         }
36085         
36086         if (this.unrendered_panels && this.unrendered_panels.length) {
36087             for (var i =0;i< this.unrendered_panels.length; i++) {
36088                 this.add(this.unrendered_panels[i]);
36089             }
36090             this.unrendered_panels = null;
36091             
36092         }
36093         
36094     },
36095     
36096     applyConfig : function(c)
36097     {
36098         /*
36099          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
36100             var dh = Roo.DomHelper;
36101             if(c.titlebar !== false){
36102                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
36103                 this.collapseBtn.on("click", this.collapse, this);
36104                 this.collapseBtn.enableDisplayMode();
36105                 /*
36106                 if(c.showPin === true || this.showPin){
36107                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
36108                     this.stickBtn.enableDisplayMode();
36109                     this.stickBtn.on("click", this.expand, this);
36110                     this.stickBtn.hide();
36111                 }
36112                 
36113             }
36114             */
36115             /** This region's collapsed element
36116             * @type Roo.Element */
36117             /*
36118              *
36119             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
36120                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
36121             ]}, true);
36122             
36123             if(c.floatable !== false){
36124                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
36125                this.collapsedEl.on("click", this.collapseClick, this);
36126             }
36127
36128             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
36129                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
36130                    id: "message", unselectable: "on", style:{"float":"left"}});
36131                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
36132              }
36133             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
36134             this.expandBtn.on("click", this.expand, this);
36135             
36136         }
36137         
36138         if(this.collapseBtn){
36139             this.collapseBtn.setVisible(c.collapsible == true);
36140         }
36141         
36142         this.cmargins = c.cmargins || this.cmargins ||
36143                          (this.position == "west" || this.position == "east" ?
36144                              {top: 0, left: 2, right:2, bottom: 0} :
36145                              {top: 2, left: 0, right:0, bottom: 2});
36146         */
36147         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36148         
36149         
36150         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
36151         
36152         this.autoScroll = c.autoScroll || false;
36153         
36154         
36155        
36156         
36157         this.duration = c.duration || .30;
36158         this.slideDuration = c.slideDuration || .45;
36159         this.config = c;
36160        
36161     },
36162     /**
36163      * Returns true if this region is currently visible.
36164      * @return {Boolean}
36165      */
36166     isVisible : function(){
36167         return this.visible;
36168     },
36169
36170     /**
36171      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
36172      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
36173      */
36174     //setCollapsedTitle : function(title){
36175     //    title = title || "&#160;";
36176      //   if(this.collapsedTitleTextEl){
36177       //      this.collapsedTitleTextEl.innerHTML = title;
36178        // }
36179     //},
36180
36181     getBox : function(){
36182         var b;
36183       //  if(!this.collapsed){
36184             b = this.el.getBox(false, true);
36185        // }else{
36186           //  b = this.collapsedEl.getBox(false, true);
36187         //}
36188         return b;
36189     },
36190
36191     getMargins : function(){
36192         return this.margins;
36193         //return this.collapsed ? this.cmargins : this.margins;
36194     },
36195 /*
36196     highlight : function(){
36197         this.el.addClass("x-layout-panel-dragover");
36198     },
36199
36200     unhighlight : function(){
36201         this.el.removeClass("x-layout-panel-dragover");
36202     },
36203 */
36204     updateBox : function(box)
36205     {
36206         if (!this.bodyEl) {
36207             return; // not rendered yet..
36208         }
36209         
36210         this.box = box;
36211         if(!this.collapsed){
36212             this.el.dom.style.left = box.x + "px";
36213             this.el.dom.style.top = box.y + "px";
36214             this.updateBody(box.width, box.height);
36215         }else{
36216             this.collapsedEl.dom.style.left = box.x + "px";
36217             this.collapsedEl.dom.style.top = box.y + "px";
36218             this.collapsedEl.setSize(box.width, box.height);
36219         }
36220         if(this.tabs){
36221             this.tabs.autoSizeTabs();
36222         }
36223     },
36224
36225     updateBody : function(w, h)
36226     {
36227         if(w !== null){
36228             this.el.setWidth(w);
36229             w -= this.el.getBorderWidth("rl");
36230             if(this.config.adjustments){
36231                 w += this.config.adjustments[0];
36232             }
36233         }
36234         if(h !== null && h > 0){
36235             this.el.setHeight(h);
36236             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36237             h -= this.el.getBorderWidth("tb");
36238             if(this.config.adjustments){
36239                 h += this.config.adjustments[1];
36240             }
36241             this.bodyEl.setHeight(h);
36242             if(this.tabs){
36243                 h = this.tabs.syncHeight(h);
36244             }
36245         }
36246         if(this.panelSize){
36247             w = w !== null ? w : this.panelSize.width;
36248             h = h !== null ? h : this.panelSize.height;
36249         }
36250         if(this.activePanel){
36251             var el = this.activePanel.getEl();
36252             w = w !== null ? w : el.getWidth();
36253             h = h !== null ? h : el.getHeight();
36254             this.panelSize = {width: w, height: h};
36255             this.activePanel.setSize(w, h);
36256         }
36257         if(Roo.isIE && this.tabs){
36258             this.tabs.el.repaint();
36259         }
36260     },
36261
36262     /**
36263      * Returns the container element for this region.
36264      * @return {Roo.Element}
36265      */
36266     getEl : function(){
36267         return this.el;
36268     },
36269
36270     /**
36271      * Hides this region.
36272      */
36273     hide : function(){
36274         //if(!this.collapsed){
36275             this.el.dom.style.left = "-2000px";
36276             this.el.hide();
36277         //}else{
36278          //   this.collapsedEl.dom.style.left = "-2000px";
36279          //   this.collapsedEl.hide();
36280        // }
36281         this.visible = false;
36282         this.fireEvent("visibilitychange", this, false);
36283     },
36284
36285     /**
36286      * Shows this region if it was previously hidden.
36287      */
36288     show : function(){
36289         //if(!this.collapsed){
36290             this.el.show();
36291         //}else{
36292         //    this.collapsedEl.show();
36293        // }
36294         this.visible = true;
36295         this.fireEvent("visibilitychange", this, true);
36296     },
36297 /*
36298     closeClicked : function(){
36299         if(this.activePanel){
36300             this.remove(this.activePanel);
36301         }
36302     },
36303
36304     collapseClick : function(e){
36305         if(this.isSlid){
36306            e.stopPropagation();
36307            this.slideIn();
36308         }else{
36309            e.stopPropagation();
36310            this.slideOut();
36311         }
36312     },
36313 */
36314     /**
36315      * Collapses this region.
36316      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36317      */
36318     /*
36319     collapse : function(skipAnim, skipCheck = false){
36320         if(this.collapsed) {
36321             return;
36322         }
36323         
36324         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36325             
36326             this.collapsed = true;
36327             if(this.split){
36328                 this.split.el.hide();
36329             }
36330             if(this.config.animate && skipAnim !== true){
36331                 this.fireEvent("invalidated", this);
36332                 this.animateCollapse();
36333             }else{
36334                 this.el.setLocation(-20000,-20000);
36335                 this.el.hide();
36336                 this.collapsedEl.show();
36337                 this.fireEvent("collapsed", this);
36338                 this.fireEvent("invalidated", this);
36339             }
36340         }
36341         
36342     },
36343 */
36344     animateCollapse : function(){
36345         // overridden
36346     },
36347
36348     /**
36349      * Expands this region if it was previously collapsed.
36350      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36351      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36352      */
36353     /*
36354     expand : function(e, skipAnim){
36355         if(e) {
36356             e.stopPropagation();
36357         }
36358         if(!this.collapsed || this.el.hasActiveFx()) {
36359             return;
36360         }
36361         if(this.isSlid){
36362             this.afterSlideIn();
36363             skipAnim = true;
36364         }
36365         this.collapsed = false;
36366         if(this.config.animate && skipAnim !== true){
36367             this.animateExpand();
36368         }else{
36369             this.el.show();
36370             if(this.split){
36371                 this.split.el.show();
36372             }
36373             this.collapsedEl.setLocation(-2000,-2000);
36374             this.collapsedEl.hide();
36375             this.fireEvent("invalidated", this);
36376             this.fireEvent("expanded", this);
36377         }
36378     },
36379 */
36380     animateExpand : function(){
36381         // overridden
36382     },
36383
36384     initTabs : function()
36385     {
36386         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36387         
36388         var ts = new Roo.bootstrap.panel.Tabs({
36389             el: this.bodyEl.dom,
36390             region : this,
36391             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
36392             disableTooltips: this.config.disableTabTips,
36393             toolbar : this.config.toolbar
36394         });
36395         
36396         if(this.config.hideTabs){
36397             ts.stripWrap.setDisplayed(false);
36398         }
36399         this.tabs = ts;
36400         ts.resizeTabs = this.config.resizeTabs === true;
36401         ts.minTabWidth = this.config.minTabWidth || 40;
36402         ts.maxTabWidth = this.config.maxTabWidth || 250;
36403         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36404         ts.monitorResize = false;
36405         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36406         ts.bodyEl.addClass('roo-layout-tabs-body');
36407         this.panels.each(this.initPanelAsTab, this);
36408     },
36409
36410     initPanelAsTab : function(panel){
36411         var ti = this.tabs.addTab(
36412             panel.getEl().id,
36413             panel.getTitle(),
36414             null,
36415             this.config.closeOnTab && panel.isClosable(),
36416             panel.tpl
36417         );
36418         if(panel.tabTip !== undefined){
36419             ti.setTooltip(panel.tabTip);
36420         }
36421         ti.on("activate", function(){
36422               this.setActivePanel(panel);
36423         }, this);
36424         
36425         if(this.config.closeOnTab){
36426             ti.on("beforeclose", function(t, e){
36427                 e.cancel = true;
36428                 this.remove(panel);
36429             }, this);
36430         }
36431         
36432         panel.tabItem = ti;
36433         
36434         return ti;
36435     },
36436
36437     updatePanelTitle : function(panel, title)
36438     {
36439         if(this.activePanel == panel){
36440             this.updateTitle(title);
36441         }
36442         if(this.tabs){
36443             var ti = this.tabs.getTab(panel.getEl().id);
36444             ti.setText(title);
36445             if(panel.tabTip !== undefined){
36446                 ti.setTooltip(panel.tabTip);
36447             }
36448         }
36449     },
36450
36451     updateTitle : function(title){
36452         if(this.titleTextEl && !this.config.title){
36453             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36454         }
36455     },
36456
36457     setActivePanel : function(panel)
36458     {
36459         panel = this.getPanel(panel);
36460         if(this.activePanel && this.activePanel != panel){
36461             if(this.activePanel.setActiveState(false) === false){
36462                 return;
36463             }
36464         }
36465         this.activePanel = panel;
36466         panel.setActiveState(true);
36467         if(this.panelSize){
36468             panel.setSize(this.panelSize.width, this.panelSize.height);
36469         }
36470         if(this.closeBtn){
36471             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36472         }
36473         this.updateTitle(panel.getTitle());
36474         if(this.tabs){
36475             this.fireEvent("invalidated", this);
36476         }
36477         this.fireEvent("panelactivated", this, panel);
36478     },
36479
36480     /**
36481      * Shows the specified panel.
36482      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36483      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36484      */
36485     showPanel : function(panel)
36486     {
36487         panel = this.getPanel(panel);
36488         if(panel){
36489             if(this.tabs){
36490                 var tab = this.tabs.getTab(panel.getEl().id);
36491                 if(tab.isHidden()){
36492                     this.tabs.unhideTab(tab.id);
36493                 }
36494                 tab.activate();
36495             }else{
36496                 this.setActivePanel(panel);
36497             }
36498         }
36499         return panel;
36500     },
36501
36502     /**
36503      * Get the active panel for this region.
36504      * @return {Roo.ContentPanel} The active panel or null
36505      */
36506     getActivePanel : function(){
36507         return this.activePanel;
36508     },
36509
36510     validateVisibility : function(){
36511         if(this.panels.getCount() < 1){
36512             this.updateTitle("&#160;");
36513             this.closeBtn.hide();
36514             this.hide();
36515         }else{
36516             if(!this.isVisible()){
36517                 this.show();
36518             }
36519         }
36520     },
36521
36522     /**
36523      * Adds the passed ContentPanel(s) to this region.
36524      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36525      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36526      */
36527     add : function(panel)
36528     {
36529         if(arguments.length > 1){
36530             for(var i = 0, len = arguments.length; i < len; i++) {
36531                 this.add(arguments[i]);
36532             }
36533             return null;
36534         }
36535         
36536         // if we have not been rendered yet, then we can not really do much of this..
36537         if (!this.bodyEl) {
36538             this.unrendered_panels.push(panel);
36539             return panel;
36540         }
36541         
36542         
36543         
36544         
36545         if(this.hasPanel(panel)){
36546             this.showPanel(panel);
36547             return panel;
36548         }
36549         panel.setRegion(this);
36550         this.panels.add(panel);
36551        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36552             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36553             // and hide them... ???
36554             this.bodyEl.dom.appendChild(panel.getEl().dom);
36555             if(panel.background !== true){
36556                 this.setActivePanel(panel);
36557             }
36558             this.fireEvent("paneladded", this, panel);
36559             return panel;
36560         }
36561         */
36562         if(!this.tabs){
36563             this.initTabs();
36564         }else{
36565             this.initPanelAsTab(panel);
36566         }
36567         
36568         
36569         if(panel.background !== true){
36570             this.tabs.activate(panel.getEl().id);
36571         }
36572         this.fireEvent("paneladded", this, panel);
36573         return panel;
36574     },
36575
36576     /**
36577      * Hides the tab for the specified panel.
36578      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36579      */
36580     hidePanel : function(panel){
36581         if(this.tabs && (panel = this.getPanel(panel))){
36582             this.tabs.hideTab(panel.getEl().id);
36583         }
36584     },
36585
36586     /**
36587      * Unhides the tab for a previously hidden panel.
36588      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36589      */
36590     unhidePanel : function(panel){
36591         if(this.tabs && (panel = this.getPanel(panel))){
36592             this.tabs.unhideTab(panel.getEl().id);
36593         }
36594     },
36595
36596     clearPanels : function(){
36597         while(this.panels.getCount() > 0){
36598              this.remove(this.panels.first());
36599         }
36600     },
36601
36602     /**
36603      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36604      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36605      * @param {Boolean} preservePanel Overrides the config preservePanel option
36606      * @return {Roo.ContentPanel} The panel that was removed
36607      */
36608     remove : function(panel, preservePanel)
36609     {
36610         panel = this.getPanel(panel);
36611         if(!panel){
36612             return null;
36613         }
36614         var e = {};
36615         this.fireEvent("beforeremove", this, panel, e);
36616         if(e.cancel === true){
36617             return null;
36618         }
36619         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36620         var panelId = panel.getId();
36621         this.panels.removeKey(panelId);
36622         if(preservePanel){
36623             document.body.appendChild(panel.getEl().dom);
36624         }
36625         if(this.tabs){
36626             this.tabs.removeTab(panel.getEl().id);
36627         }else if (!preservePanel){
36628             this.bodyEl.dom.removeChild(panel.getEl().dom);
36629         }
36630         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36631             var p = this.panels.first();
36632             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36633             tempEl.appendChild(p.getEl().dom);
36634             this.bodyEl.update("");
36635             this.bodyEl.dom.appendChild(p.getEl().dom);
36636             tempEl = null;
36637             this.updateTitle(p.getTitle());
36638             this.tabs = null;
36639             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36640             this.setActivePanel(p);
36641         }
36642         panel.setRegion(null);
36643         if(this.activePanel == panel){
36644             this.activePanel = null;
36645         }
36646         if(this.config.autoDestroy !== false && preservePanel !== true){
36647             try{panel.destroy();}catch(e){}
36648         }
36649         this.fireEvent("panelremoved", this, panel);
36650         return panel;
36651     },
36652
36653     /**
36654      * Returns the TabPanel component used by this region
36655      * @return {Roo.TabPanel}
36656      */
36657     getTabs : function(){
36658         return this.tabs;
36659     },
36660
36661     createTool : function(parentEl, className){
36662         var btn = Roo.DomHelper.append(parentEl, {
36663             tag: "div",
36664             cls: "x-layout-tools-button",
36665             children: [ {
36666                 tag: "div",
36667                 cls: "roo-layout-tools-button-inner " + className,
36668                 html: "&#160;"
36669             }]
36670         }, true);
36671         btn.addClassOnOver("roo-layout-tools-button-over");
36672         return btn;
36673     }
36674 });/*
36675  * Based on:
36676  * Ext JS Library 1.1.1
36677  * Copyright(c) 2006-2007, Ext JS, LLC.
36678  *
36679  * Originally Released Under LGPL - original licence link has changed is not relivant.
36680  *
36681  * Fork - LGPL
36682  * <script type="text/javascript">
36683  */
36684  
36685
36686
36687 /**
36688  * @class Roo.SplitLayoutRegion
36689  * @extends Roo.LayoutRegion
36690  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36691  */
36692 Roo.bootstrap.layout.Split = function(config){
36693     this.cursor = config.cursor;
36694     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36695 };
36696
36697 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36698 {
36699     splitTip : "Drag to resize.",
36700     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36701     useSplitTips : false,
36702
36703     applyConfig : function(config){
36704         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36705     },
36706     
36707     onRender : function(ctr,pos) {
36708         
36709         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36710         if(!this.config.split){
36711             return;
36712         }
36713         if(!this.split){
36714             
36715             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36716                             tag: "div",
36717                             id: this.el.id + "-split",
36718                             cls: "roo-layout-split roo-layout-split-"+this.position,
36719                             html: "&#160;"
36720             });
36721             /** The SplitBar for this region 
36722             * @type Roo.SplitBar */
36723             // does not exist yet...
36724             Roo.log([this.position, this.orientation]);
36725             
36726             this.split = new Roo.bootstrap.SplitBar({
36727                 dragElement : splitEl,
36728                 resizingElement: this.el,
36729                 orientation : this.orientation
36730             });
36731             
36732             this.split.on("moved", this.onSplitMove, this);
36733             this.split.useShim = this.config.useShim === true;
36734             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36735             if(this.useSplitTips){
36736                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36737             }
36738             //if(config.collapsible){
36739             //    this.split.el.on("dblclick", this.collapse,  this);
36740             //}
36741         }
36742         if(typeof this.config.minSize != "undefined"){
36743             this.split.minSize = this.config.minSize;
36744         }
36745         if(typeof this.config.maxSize != "undefined"){
36746             this.split.maxSize = this.config.maxSize;
36747         }
36748         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36749             this.hideSplitter();
36750         }
36751         
36752     },
36753
36754     getHMaxSize : function(){
36755          var cmax = this.config.maxSize || 10000;
36756          var center = this.mgr.getRegion("center");
36757          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36758     },
36759
36760     getVMaxSize : function(){
36761          var cmax = this.config.maxSize || 10000;
36762          var center = this.mgr.getRegion("center");
36763          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36764     },
36765
36766     onSplitMove : function(split, newSize){
36767         this.fireEvent("resized", this, newSize);
36768     },
36769     
36770     /** 
36771      * Returns the {@link Roo.SplitBar} for this region.
36772      * @return {Roo.SplitBar}
36773      */
36774     getSplitBar : function(){
36775         return this.split;
36776     },
36777     
36778     hide : function(){
36779         this.hideSplitter();
36780         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36781     },
36782
36783     hideSplitter : function(){
36784         if(this.split){
36785             this.split.el.setLocation(-2000,-2000);
36786             this.split.el.hide();
36787         }
36788     },
36789
36790     show : function(){
36791         if(this.split){
36792             this.split.el.show();
36793         }
36794         Roo.bootstrap.layout.Split.superclass.show.call(this);
36795     },
36796     
36797     beforeSlide: function(){
36798         if(Roo.isGecko){// firefox overflow auto bug workaround
36799             this.bodyEl.clip();
36800             if(this.tabs) {
36801                 this.tabs.bodyEl.clip();
36802             }
36803             if(this.activePanel){
36804                 this.activePanel.getEl().clip();
36805                 
36806                 if(this.activePanel.beforeSlide){
36807                     this.activePanel.beforeSlide();
36808                 }
36809             }
36810         }
36811     },
36812     
36813     afterSlide : function(){
36814         if(Roo.isGecko){// firefox overflow auto bug workaround
36815             this.bodyEl.unclip();
36816             if(this.tabs) {
36817                 this.tabs.bodyEl.unclip();
36818             }
36819             if(this.activePanel){
36820                 this.activePanel.getEl().unclip();
36821                 if(this.activePanel.afterSlide){
36822                     this.activePanel.afterSlide();
36823                 }
36824             }
36825         }
36826     },
36827
36828     initAutoHide : function(){
36829         if(this.autoHide !== false){
36830             if(!this.autoHideHd){
36831                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36832                 this.autoHideHd = {
36833                     "mouseout": function(e){
36834                         if(!e.within(this.el, true)){
36835                             st.delay(500);
36836                         }
36837                     },
36838                     "mouseover" : function(e){
36839                         st.cancel();
36840                     },
36841                     scope : this
36842                 };
36843             }
36844             this.el.on(this.autoHideHd);
36845         }
36846     },
36847
36848     clearAutoHide : function(){
36849         if(this.autoHide !== false){
36850             this.el.un("mouseout", this.autoHideHd.mouseout);
36851             this.el.un("mouseover", this.autoHideHd.mouseover);
36852         }
36853     },
36854
36855     clearMonitor : function(){
36856         Roo.get(document).un("click", this.slideInIf, this);
36857     },
36858
36859     // these names are backwards but not changed for compat
36860     slideOut : function(){
36861         if(this.isSlid || this.el.hasActiveFx()){
36862             return;
36863         }
36864         this.isSlid = true;
36865         if(this.collapseBtn){
36866             this.collapseBtn.hide();
36867         }
36868         this.closeBtnState = this.closeBtn.getStyle('display');
36869         this.closeBtn.hide();
36870         if(this.stickBtn){
36871             this.stickBtn.show();
36872         }
36873         this.el.show();
36874         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36875         this.beforeSlide();
36876         this.el.setStyle("z-index", 10001);
36877         this.el.slideIn(this.getSlideAnchor(), {
36878             callback: function(){
36879                 this.afterSlide();
36880                 this.initAutoHide();
36881                 Roo.get(document).on("click", this.slideInIf, this);
36882                 this.fireEvent("slideshow", this);
36883             },
36884             scope: this,
36885             block: true
36886         });
36887     },
36888
36889     afterSlideIn : function(){
36890         this.clearAutoHide();
36891         this.isSlid = false;
36892         this.clearMonitor();
36893         this.el.setStyle("z-index", "");
36894         if(this.collapseBtn){
36895             this.collapseBtn.show();
36896         }
36897         this.closeBtn.setStyle('display', this.closeBtnState);
36898         if(this.stickBtn){
36899             this.stickBtn.hide();
36900         }
36901         this.fireEvent("slidehide", this);
36902     },
36903
36904     slideIn : function(cb){
36905         if(!this.isSlid || this.el.hasActiveFx()){
36906             Roo.callback(cb);
36907             return;
36908         }
36909         this.isSlid = false;
36910         this.beforeSlide();
36911         this.el.slideOut(this.getSlideAnchor(), {
36912             callback: function(){
36913                 this.el.setLeftTop(-10000, -10000);
36914                 this.afterSlide();
36915                 this.afterSlideIn();
36916                 Roo.callback(cb);
36917             },
36918             scope: this,
36919             block: true
36920         });
36921     },
36922     
36923     slideInIf : function(e){
36924         if(!e.within(this.el)){
36925             this.slideIn();
36926         }
36927     },
36928
36929     animateCollapse : function(){
36930         this.beforeSlide();
36931         this.el.setStyle("z-index", 20000);
36932         var anchor = this.getSlideAnchor();
36933         this.el.slideOut(anchor, {
36934             callback : function(){
36935                 this.el.setStyle("z-index", "");
36936                 this.collapsedEl.slideIn(anchor, {duration:.3});
36937                 this.afterSlide();
36938                 this.el.setLocation(-10000,-10000);
36939                 this.el.hide();
36940                 this.fireEvent("collapsed", this);
36941             },
36942             scope: this,
36943             block: true
36944         });
36945     },
36946
36947     animateExpand : function(){
36948         this.beforeSlide();
36949         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36950         this.el.setStyle("z-index", 20000);
36951         this.collapsedEl.hide({
36952             duration:.1
36953         });
36954         this.el.slideIn(this.getSlideAnchor(), {
36955             callback : function(){
36956                 this.el.setStyle("z-index", "");
36957                 this.afterSlide();
36958                 if(this.split){
36959                     this.split.el.show();
36960                 }
36961                 this.fireEvent("invalidated", this);
36962                 this.fireEvent("expanded", this);
36963             },
36964             scope: this,
36965             block: true
36966         });
36967     },
36968
36969     anchors : {
36970         "west" : "left",
36971         "east" : "right",
36972         "north" : "top",
36973         "south" : "bottom"
36974     },
36975
36976     sanchors : {
36977         "west" : "l",
36978         "east" : "r",
36979         "north" : "t",
36980         "south" : "b"
36981     },
36982
36983     canchors : {
36984         "west" : "tl-tr",
36985         "east" : "tr-tl",
36986         "north" : "tl-bl",
36987         "south" : "bl-tl"
36988     },
36989
36990     getAnchor : function(){
36991         return this.anchors[this.position];
36992     },
36993
36994     getCollapseAnchor : function(){
36995         return this.canchors[this.position];
36996     },
36997
36998     getSlideAnchor : function(){
36999         return this.sanchors[this.position];
37000     },
37001
37002     getAlignAdj : function(){
37003         var cm = this.cmargins;
37004         switch(this.position){
37005             case "west":
37006                 return [0, 0];
37007             break;
37008             case "east":
37009                 return [0, 0];
37010             break;
37011             case "north":
37012                 return [0, 0];
37013             break;
37014             case "south":
37015                 return [0, 0];
37016             break;
37017         }
37018     },
37019
37020     getExpandAdj : function(){
37021         var c = this.collapsedEl, cm = this.cmargins;
37022         switch(this.position){
37023             case "west":
37024                 return [-(cm.right+c.getWidth()+cm.left), 0];
37025             break;
37026             case "east":
37027                 return [cm.right+c.getWidth()+cm.left, 0];
37028             break;
37029             case "north":
37030                 return [0, -(cm.top+cm.bottom+c.getHeight())];
37031             break;
37032             case "south":
37033                 return [0, cm.top+cm.bottom+c.getHeight()];
37034             break;
37035         }
37036     }
37037 });/*
37038  * Based on:
37039  * Ext JS Library 1.1.1
37040  * Copyright(c) 2006-2007, Ext JS, LLC.
37041  *
37042  * Originally Released Under LGPL - original licence link has changed is not relivant.
37043  *
37044  * Fork - LGPL
37045  * <script type="text/javascript">
37046  */
37047 /*
37048  * These classes are private internal classes
37049  */
37050 Roo.bootstrap.layout.Center = function(config){
37051     config.region = "center";
37052     Roo.bootstrap.layout.Region.call(this, config);
37053     this.visible = true;
37054     this.minWidth = config.minWidth || 20;
37055     this.minHeight = config.minHeight || 20;
37056 };
37057
37058 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
37059     hide : function(){
37060         // center panel can't be hidden
37061     },
37062     
37063     show : function(){
37064         // center panel can't be hidden
37065     },
37066     
37067     getMinWidth: function(){
37068         return this.minWidth;
37069     },
37070     
37071     getMinHeight: function(){
37072         return this.minHeight;
37073     }
37074 });
37075
37076
37077
37078
37079  
37080
37081
37082
37083
37084
37085
37086 Roo.bootstrap.layout.North = function(config)
37087 {
37088     config.region = 'north';
37089     config.cursor = 'n-resize';
37090     
37091     Roo.bootstrap.layout.Split.call(this, config);
37092     
37093     
37094     if(this.split){
37095         this.split.placement = Roo.bootstrap.SplitBar.TOP;
37096         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37097         this.split.el.addClass("roo-layout-split-v");
37098     }
37099     var size = config.initialSize || config.height;
37100     if(typeof size != "undefined"){
37101         this.el.setHeight(size);
37102     }
37103 };
37104 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
37105 {
37106     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37107     
37108     
37109     
37110     getBox : function(){
37111         if(this.collapsed){
37112             return this.collapsedEl.getBox();
37113         }
37114         var box = this.el.getBox();
37115         if(this.split){
37116             box.height += this.split.el.getHeight();
37117         }
37118         return box;
37119     },
37120     
37121     updateBox : function(box){
37122         if(this.split && !this.collapsed){
37123             box.height -= this.split.el.getHeight();
37124             this.split.el.setLeft(box.x);
37125             this.split.el.setTop(box.y+box.height);
37126             this.split.el.setWidth(box.width);
37127         }
37128         if(this.collapsed){
37129             this.updateBody(box.width, null);
37130         }
37131         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37132     }
37133 });
37134
37135
37136
37137
37138
37139 Roo.bootstrap.layout.South = function(config){
37140     config.region = 'south';
37141     config.cursor = 's-resize';
37142     Roo.bootstrap.layout.Split.call(this, config);
37143     if(this.split){
37144         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
37145         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37146         this.split.el.addClass("roo-layout-split-v");
37147     }
37148     var size = config.initialSize || config.height;
37149     if(typeof size != "undefined"){
37150         this.el.setHeight(size);
37151     }
37152 };
37153
37154 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
37155     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37156     getBox : function(){
37157         if(this.collapsed){
37158             return this.collapsedEl.getBox();
37159         }
37160         var box = this.el.getBox();
37161         if(this.split){
37162             var sh = this.split.el.getHeight();
37163             box.height += sh;
37164             box.y -= sh;
37165         }
37166         return box;
37167     },
37168     
37169     updateBox : function(box){
37170         if(this.split && !this.collapsed){
37171             var sh = this.split.el.getHeight();
37172             box.height -= sh;
37173             box.y += sh;
37174             this.split.el.setLeft(box.x);
37175             this.split.el.setTop(box.y-sh);
37176             this.split.el.setWidth(box.width);
37177         }
37178         if(this.collapsed){
37179             this.updateBody(box.width, null);
37180         }
37181         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37182     }
37183 });
37184
37185 Roo.bootstrap.layout.East = function(config){
37186     config.region = "east";
37187     config.cursor = "e-resize";
37188     Roo.bootstrap.layout.Split.call(this, config);
37189     if(this.split){
37190         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
37191         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37192         this.split.el.addClass("roo-layout-split-h");
37193     }
37194     var size = config.initialSize || config.width;
37195     if(typeof size != "undefined"){
37196         this.el.setWidth(size);
37197     }
37198 };
37199 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
37200     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37201     getBox : function(){
37202         if(this.collapsed){
37203             return this.collapsedEl.getBox();
37204         }
37205         var box = this.el.getBox();
37206         if(this.split){
37207             var sw = this.split.el.getWidth();
37208             box.width += sw;
37209             box.x -= sw;
37210         }
37211         return box;
37212     },
37213
37214     updateBox : function(box){
37215         if(this.split && !this.collapsed){
37216             var sw = this.split.el.getWidth();
37217             box.width -= sw;
37218             this.split.el.setLeft(box.x);
37219             this.split.el.setTop(box.y);
37220             this.split.el.setHeight(box.height);
37221             box.x += sw;
37222         }
37223         if(this.collapsed){
37224             this.updateBody(null, box.height);
37225         }
37226         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37227     }
37228 });
37229
37230 Roo.bootstrap.layout.West = function(config){
37231     config.region = "west";
37232     config.cursor = "w-resize";
37233     
37234     Roo.bootstrap.layout.Split.call(this, config);
37235     if(this.split){
37236         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37237         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37238         this.split.el.addClass("roo-layout-split-h");
37239     }
37240     
37241 };
37242 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37243     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37244     
37245     onRender: function(ctr, pos)
37246     {
37247         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37248         var size = this.config.initialSize || this.config.width;
37249         if(typeof size != "undefined"){
37250             this.el.setWidth(size);
37251         }
37252     },
37253     
37254     getBox : function(){
37255         if(this.collapsed){
37256             return this.collapsedEl.getBox();
37257         }
37258         var box = this.el.getBox();
37259         if(this.split){
37260             box.width += this.split.el.getWidth();
37261         }
37262         return box;
37263     },
37264     
37265     updateBox : function(box){
37266         if(this.split && !this.collapsed){
37267             var sw = this.split.el.getWidth();
37268             box.width -= sw;
37269             this.split.el.setLeft(box.x+box.width);
37270             this.split.el.setTop(box.y);
37271             this.split.el.setHeight(box.height);
37272         }
37273         if(this.collapsed){
37274             this.updateBody(null, box.height);
37275         }
37276         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37277     }
37278 });Roo.namespace("Roo.bootstrap.panel");/*
37279  * Based on:
37280  * Ext JS Library 1.1.1
37281  * Copyright(c) 2006-2007, Ext JS, LLC.
37282  *
37283  * Originally Released Under LGPL - original licence link has changed is not relivant.
37284  *
37285  * Fork - LGPL
37286  * <script type="text/javascript">
37287  */
37288 /**
37289  * @class Roo.ContentPanel
37290  * @extends Roo.util.Observable
37291  * A basic ContentPanel element.
37292  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
37293  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
37294  * @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
37295  * @cfg {Boolean}   closable      True if the panel can be closed/removed
37296  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
37297  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37298  * @cfg {Toolbar}   toolbar       A toolbar for this panel
37299  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
37300  * @cfg {String} title          The title for this panel
37301  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37302  * @cfg {String} url            Calls {@link #setUrl} with this value
37303  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37304  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37305  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37306  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37307  * @cfg {Boolean} badges render the badges
37308
37309  * @constructor
37310  * Create a new ContentPanel.
37311  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37312  * @param {String/Object} config A string to set only the title or a config object
37313  * @param {String} content (optional) Set the HTML content for this panel
37314  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37315  */
37316 Roo.bootstrap.panel.Content = function( config){
37317     
37318     this.tpl = config.tpl || false;
37319     
37320     var el = config.el;
37321     var content = config.content;
37322
37323     if(config.autoCreate){ // xtype is available if this is called from factory
37324         el = Roo.id();
37325     }
37326     this.el = Roo.get(el);
37327     if(!this.el && config && config.autoCreate){
37328         if(typeof config.autoCreate == "object"){
37329             if(!config.autoCreate.id){
37330                 config.autoCreate.id = config.id||el;
37331             }
37332             this.el = Roo.DomHelper.append(document.body,
37333                         config.autoCreate, true);
37334         }else{
37335             var elcfg =  {   tag: "div",
37336                             cls: "roo-layout-inactive-content",
37337                             id: config.id||el
37338                             };
37339             if (config.html) {
37340                 elcfg.html = config.html;
37341                 
37342             }
37343                         
37344             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37345         }
37346     } 
37347     this.closable = false;
37348     this.loaded = false;
37349     this.active = false;
37350    
37351       
37352     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37353         
37354         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37355         
37356         this.wrapEl = this.el; //this.el.wrap();
37357         var ti = [];
37358         if (config.toolbar.items) {
37359             ti = config.toolbar.items ;
37360             delete config.toolbar.items ;
37361         }
37362         
37363         var nitems = [];
37364         this.toolbar.render(this.wrapEl, 'before');
37365         for(var i =0;i < ti.length;i++) {
37366           //  Roo.log(['add child', items[i]]);
37367             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37368         }
37369         this.toolbar.items = nitems;
37370         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37371         delete config.toolbar;
37372         
37373     }
37374     /*
37375     // xtype created footer. - not sure if will work as we normally have to render first..
37376     if (this.footer && !this.footer.el && this.footer.xtype) {
37377         if (!this.wrapEl) {
37378             this.wrapEl = this.el.wrap();
37379         }
37380     
37381         this.footer.container = this.wrapEl.createChild();
37382          
37383         this.footer = Roo.factory(this.footer, Roo);
37384         
37385     }
37386     */
37387     
37388      if(typeof config == "string"){
37389         this.title = config;
37390     }else{
37391         Roo.apply(this, config);
37392     }
37393     
37394     if(this.resizeEl){
37395         this.resizeEl = Roo.get(this.resizeEl, true);
37396     }else{
37397         this.resizeEl = this.el;
37398     }
37399     // handle view.xtype
37400     
37401  
37402     
37403     
37404     this.addEvents({
37405         /**
37406          * @event activate
37407          * Fires when this panel is activated. 
37408          * @param {Roo.ContentPanel} this
37409          */
37410         "activate" : true,
37411         /**
37412          * @event deactivate
37413          * Fires when this panel is activated. 
37414          * @param {Roo.ContentPanel} this
37415          */
37416         "deactivate" : true,
37417
37418         /**
37419          * @event resize
37420          * Fires when this panel is resized if fitToFrame is true.
37421          * @param {Roo.ContentPanel} this
37422          * @param {Number} width The width after any component adjustments
37423          * @param {Number} height The height after any component adjustments
37424          */
37425         "resize" : true,
37426         
37427          /**
37428          * @event render
37429          * Fires when this tab is created
37430          * @param {Roo.ContentPanel} this
37431          */
37432         "render" : true
37433         
37434         
37435         
37436     });
37437     
37438
37439     
37440     
37441     if(this.autoScroll){
37442         this.resizeEl.setStyle("overflow", "auto");
37443     } else {
37444         // fix randome scrolling
37445         //this.el.on('scroll', function() {
37446         //    Roo.log('fix random scolling');
37447         //    this.scrollTo('top',0); 
37448         //});
37449     }
37450     content = content || this.content;
37451     if(content){
37452         this.setContent(content);
37453     }
37454     if(config && config.url){
37455         this.setUrl(this.url, this.params, this.loadOnce);
37456     }
37457     
37458     
37459     
37460     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37461     
37462     if (this.view && typeof(this.view.xtype) != 'undefined') {
37463         this.view.el = this.el.appendChild(document.createElement("div"));
37464         this.view = Roo.factory(this.view); 
37465         this.view.render  &&  this.view.render(false, '');  
37466     }
37467     
37468     
37469     this.fireEvent('render', this);
37470 };
37471
37472 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37473     
37474     tabTip : '',
37475     
37476     setRegion : function(region){
37477         this.region = region;
37478         this.setActiveClass(region && !this.background);
37479     },
37480     
37481     
37482     setActiveClass: function(state)
37483     {
37484         if(state){
37485            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37486            this.el.setStyle('position','relative');
37487         }else{
37488            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37489            this.el.setStyle('position', 'absolute');
37490         } 
37491     },
37492     
37493     /**
37494      * Returns the toolbar for this Panel if one was configured. 
37495      * @return {Roo.Toolbar} 
37496      */
37497     getToolbar : function(){
37498         return this.toolbar;
37499     },
37500     
37501     setActiveState : function(active)
37502     {
37503         this.active = active;
37504         this.setActiveClass(active);
37505         if(!active){
37506             if(this.fireEvent("deactivate", this) === false){
37507                 return false;
37508             }
37509             return true;
37510         }
37511         this.fireEvent("activate", this);
37512         return true;
37513     },
37514     /**
37515      * Updates this panel's element
37516      * @param {String} content The new content
37517      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37518     */
37519     setContent : function(content, loadScripts){
37520         this.el.update(content, loadScripts);
37521     },
37522
37523     ignoreResize : function(w, h){
37524         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37525             return true;
37526         }else{
37527             this.lastSize = {width: w, height: h};
37528             return false;
37529         }
37530     },
37531     /**
37532      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37533      * @return {Roo.UpdateManager} The UpdateManager
37534      */
37535     getUpdateManager : function(){
37536         return this.el.getUpdateManager();
37537     },
37538      /**
37539      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37540      * @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:
37541 <pre><code>
37542 panel.load({
37543     url: "your-url.php",
37544     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37545     callback: yourFunction,
37546     scope: yourObject, //(optional scope)
37547     discardUrl: false,
37548     nocache: false,
37549     text: "Loading...",
37550     timeout: 30,
37551     scripts: false
37552 });
37553 </code></pre>
37554      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37555      * 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.
37556      * @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}
37557      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37558      * @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.
37559      * @return {Roo.ContentPanel} this
37560      */
37561     load : function(){
37562         var um = this.el.getUpdateManager();
37563         um.update.apply(um, arguments);
37564         return this;
37565     },
37566
37567
37568     /**
37569      * 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.
37570      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37571      * @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)
37572      * @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)
37573      * @return {Roo.UpdateManager} The UpdateManager
37574      */
37575     setUrl : function(url, params, loadOnce){
37576         if(this.refreshDelegate){
37577             this.removeListener("activate", this.refreshDelegate);
37578         }
37579         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37580         this.on("activate", this.refreshDelegate);
37581         return this.el.getUpdateManager();
37582     },
37583     
37584     _handleRefresh : function(url, params, loadOnce){
37585         if(!loadOnce || !this.loaded){
37586             var updater = this.el.getUpdateManager();
37587             updater.update(url, params, this._setLoaded.createDelegate(this));
37588         }
37589     },
37590     
37591     _setLoaded : function(){
37592         this.loaded = true;
37593     }, 
37594     
37595     /**
37596      * Returns this panel's id
37597      * @return {String} 
37598      */
37599     getId : function(){
37600         return this.el.id;
37601     },
37602     
37603     /** 
37604      * Returns this panel's element - used by regiosn to add.
37605      * @return {Roo.Element} 
37606      */
37607     getEl : function(){
37608         return this.wrapEl || this.el;
37609     },
37610     
37611    
37612     
37613     adjustForComponents : function(width, height)
37614     {
37615         //Roo.log('adjustForComponents ');
37616         if(this.resizeEl != this.el){
37617             width -= this.el.getFrameWidth('lr');
37618             height -= this.el.getFrameWidth('tb');
37619         }
37620         if(this.toolbar){
37621             var te = this.toolbar.getEl();
37622             te.setWidth(width);
37623             height -= te.getHeight();
37624         }
37625         if(this.footer){
37626             var te = this.footer.getEl();
37627             te.setWidth(width);
37628             height -= te.getHeight();
37629         }
37630         
37631         
37632         if(this.adjustments){
37633             width += this.adjustments[0];
37634             height += this.adjustments[1];
37635         }
37636         return {"width": width, "height": height};
37637     },
37638     
37639     setSize : function(width, height){
37640         if(this.fitToFrame && !this.ignoreResize(width, height)){
37641             if(this.fitContainer && this.resizeEl != this.el){
37642                 this.el.setSize(width, height);
37643             }
37644             var size = this.adjustForComponents(width, height);
37645             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37646             this.fireEvent('resize', this, size.width, size.height);
37647         }
37648     },
37649     
37650     /**
37651      * Returns this panel's title
37652      * @return {String} 
37653      */
37654     getTitle : function(){
37655         
37656         if (typeof(this.title) != 'object') {
37657             return this.title;
37658         }
37659         
37660         var t = '';
37661         for (var k in this.title) {
37662             if (!this.title.hasOwnProperty(k)) {
37663                 continue;
37664             }
37665             
37666             if (k.indexOf('-') >= 0) {
37667                 var s = k.split('-');
37668                 for (var i = 0; i<s.length; i++) {
37669                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37670                 }
37671             } else {
37672                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37673             }
37674         }
37675         return t;
37676     },
37677     
37678     /**
37679      * Set this panel's title
37680      * @param {String} title
37681      */
37682     setTitle : function(title){
37683         this.title = title;
37684         if(this.region){
37685             this.region.updatePanelTitle(this, title);
37686         }
37687     },
37688     
37689     /**
37690      * Returns true is this panel was configured to be closable
37691      * @return {Boolean} 
37692      */
37693     isClosable : function(){
37694         return this.closable;
37695     },
37696     
37697     beforeSlide : function(){
37698         this.el.clip();
37699         this.resizeEl.clip();
37700     },
37701     
37702     afterSlide : function(){
37703         this.el.unclip();
37704         this.resizeEl.unclip();
37705     },
37706     
37707     /**
37708      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37709      *   Will fail silently if the {@link #setUrl} method has not been called.
37710      *   This does not activate the panel, just updates its content.
37711      */
37712     refresh : function(){
37713         if(this.refreshDelegate){
37714            this.loaded = false;
37715            this.refreshDelegate();
37716         }
37717     },
37718     
37719     /**
37720      * Destroys this panel
37721      */
37722     destroy : function(){
37723         this.el.removeAllListeners();
37724         var tempEl = document.createElement("span");
37725         tempEl.appendChild(this.el.dom);
37726         tempEl.innerHTML = "";
37727         this.el.remove();
37728         this.el = null;
37729     },
37730     
37731     /**
37732      * form - if the content panel contains a form - this is a reference to it.
37733      * @type {Roo.form.Form}
37734      */
37735     form : false,
37736     /**
37737      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37738      *    This contains a reference to it.
37739      * @type {Roo.View}
37740      */
37741     view : false,
37742     
37743       /**
37744      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37745      * <pre><code>
37746
37747 layout.addxtype({
37748        xtype : 'Form',
37749        items: [ .... ]
37750    }
37751 );
37752
37753 </code></pre>
37754      * @param {Object} cfg Xtype definition of item to add.
37755      */
37756     
37757     
37758     getChildContainer: function () {
37759         return this.getEl();
37760     }
37761     
37762     
37763     /*
37764         var  ret = new Roo.factory(cfg);
37765         return ret;
37766         
37767         
37768         // add form..
37769         if (cfg.xtype.match(/^Form$/)) {
37770             
37771             var el;
37772             //if (this.footer) {
37773             //    el = this.footer.container.insertSibling(false, 'before');
37774             //} else {
37775                 el = this.el.createChild();
37776             //}
37777
37778             this.form = new  Roo.form.Form(cfg);
37779             
37780             
37781             if ( this.form.allItems.length) {
37782                 this.form.render(el.dom);
37783             }
37784             return this.form;
37785         }
37786         // should only have one of theses..
37787         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37788             // views.. should not be just added - used named prop 'view''
37789             
37790             cfg.el = this.el.appendChild(document.createElement("div"));
37791             // factory?
37792             
37793             var ret = new Roo.factory(cfg);
37794              
37795              ret.render && ret.render(false, ''); // render blank..
37796             this.view = ret;
37797             return ret;
37798         }
37799         return false;
37800     }
37801     \*/
37802 });
37803  
37804 /**
37805  * @class Roo.bootstrap.panel.Grid
37806  * @extends Roo.bootstrap.panel.Content
37807  * @constructor
37808  * Create a new GridPanel.
37809  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37810  * @param {Object} config A the config object
37811   
37812  */
37813
37814
37815
37816 Roo.bootstrap.panel.Grid = function(config)
37817 {
37818     
37819       
37820     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37821         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37822
37823     config.el = this.wrapper;
37824     //this.el = this.wrapper;
37825     
37826       if (config.container) {
37827         // ctor'ed from a Border/panel.grid
37828         
37829         
37830         this.wrapper.setStyle("overflow", "hidden");
37831         this.wrapper.addClass('roo-grid-container');
37832
37833     }
37834     
37835     
37836     if(config.toolbar){
37837         var tool_el = this.wrapper.createChild();    
37838         this.toolbar = Roo.factory(config.toolbar);
37839         var ti = [];
37840         if (config.toolbar.items) {
37841             ti = config.toolbar.items ;
37842             delete config.toolbar.items ;
37843         }
37844         
37845         var nitems = [];
37846         this.toolbar.render(tool_el);
37847         for(var i =0;i < ti.length;i++) {
37848           //  Roo.log(['add child', items[i]]);
37849             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37850         }
37851         this.toolbar.items = nitems;
37852         
37853         delete config.toolbar;
37854     }
37855     
37856     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37857     config.grid.scrollBody = true;;
37858     config.grid.monitorWindowResize = false; // turn off autosizing
37859     config.grid.autoHeight = false;
37860     config.grid.autoWidth = false;
37861     
37862     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37863     
37864     if (config.background) {
37865         // render grid on panel activation (if panel background)
37866         this.on('activate', function(gp) {
37867             if (!gp.grid.rendered) {
37868                 gp.grid.render(this.wrapper);
37869                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37870             }
37871         });
37872             
37873     } else {
37874         this.grid.render(this.wrapper);
37875         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37876
37877     }
37878     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37879     // ??? needed ??? config.el = this.wrapper;
37880     
37881     
37882     
37883   
37884     // xtype created footer. - not sure if will work as we normally have to render first..
37885     if (this.footer && !this.footer.el && this.footer.xtype) {
37886         
37887         var ctr = this.grid.getView().getFooterPanel(true);
37888         this.footer.dataSource = this.grid.dataSource;
37889         this.footer = Roo.factory(this.footer, Roo);
37890         this.footer.render(ctr);
37891         
37892     }
37893     
37894     
37895     
37896     
37897      
37898 };
37899
37900 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37901     getId : function(){
37902         return this.grid.id;
37903     },
37904     
37905     /**
37906      * Returns the grid for this panel
37907      * @return {Roo.bootstrap.Table} 
37908      */
37909     getGrid : function(){
37910         return this.grid;    
37911     },
37912     
37913     setSize : function(width, height){
37914         if(!this.ignoreResize(width, height)){
37915             var grid = this.grid;
37916             var size = this.adjustForComponents(width, height);
37917             var gridel = grid.getGridEl();
37918             gridel.setSize(size.width, size.height);
37919             /*
37920             var thd = grid.getGridEl().select('thead',true).first();
37921             var tbd = grid.getGridEl().select('tbody', true).first();
37922             if (tbd) {
37923                 tbd.setSize(width, height - thd.getHeight());
37924             }
37925             */
37926             grid.autoSize();
37927         }
37928     },
37929      
37930     
37931     
37932     beforeSlide : function(){
37933         this.grid.getView().scroller.clip();
37934     },
37935     
37936     afterSlide : function(){
37937         this.grid.getView().scroller.unclip();
37938     },
37939     
37940     destroy : function(){
37941         this.grid.destroy();
37942         delete this.grid;
37943         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37944     }
37945 });
37946
37947 /**
37948  * @class Roo.bootstrap.panel.Nest
37949  * @extends Roo.bootstrap.panel.Content
37950  * @constructor
37951  * Create a new Panel, that can contain a layout.Border.
37952  * 
37953  * 
37954  * @param {Roo.BorderLayout} layout The layout for this panel
37955  * @param {String/Object} config A string to set only the title or a config object
37956  */
37957 Roo.bootstrap.panel.Nest = function(config)
37958 {
37959     // construct with only one argument..
37960     /* FIXME - implement nicer consturctors
37961     if (layout.layout) {
37962         config = layout;
37963         layout = config.layout;
37964         delete config.layout;
37965     }
37966     if (layout.xtype && !layout.getEl) {
37967         // then layout needs constructing..
37968         layout = Roo.factory(layout, Roo);
37969     }
37970     */
37971     
37972     config.el =  config.layout.getEl();
37973     
37974     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37975     
37976     config.layout.monitorWindowResize = false; // turn off autosizing
37977     this.layout = config.layout;
37978     this.layout.getEl().addClass("roo-layout-nested-layout");
37979     this.layout.parent = this;
37980     
37981     
37982     
37983     
37984 };
37985
37986 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37987
37988     setSize : function(width, height){
37989         if(!this.ignoreResize(width, height)){
37990             var size = this.adjustForComponents(width, height);
37991             var el = this.layout.getEl();
37992             if (size.height < 1) {
37993                 el.setWidth(size.width);   
37994             } else {
37995                 el.setSize(size.width, size.height);
37996             }
37997             var touch = el.dom.offsetWidth;
37998             this.layout.layout();
37999             // ie requires a double layout on the first pass
38000             if(Roo.isIE && !this.initialized){
38001                 this.initialized = true;
38002                 this.layout.layout();
38003             }
38004         }
38005     },
38006     
38007     // activate all subpanels if not currently active..
38008     
38009     setActiveState : function(active){
38010         this.active = active;
38011         this.setActiveClass(active);
38012         
38013         if(!active){
38014             this.fireEvent("deactivate", this);
38015             return;
38016         }
38017         
38018         this.fireEvent("activate", this);
38019         // not sure if this should happen before or after..
38020         if (!this.layout) {
38021             return; // should not happen..
38022         }
38023         var reg = false;
38024         for (var r in this.layout.regions) {
38025             reg = this.layout.getRegion(r);
38026             if (reg.getActivePanel()) {
38027                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
38028                 reg.setActivePanel(reg.getActivePanel());
38029                 continue;
38030             }
38031             if (!reg.panels.length) {
38032                 continue;
38033             }
38034             reg.showPanel(reg.getPanel(0));
38035         }
38036         
38037         
38038         
38039         
38040     },
38041     
38042     /**
38043      * Returns the nested BorderLayout for this panel
38044      * @return {Roo.BorderLayout} 
38045      */
38046     getLayout : function(){
38047         return this.layout;
38048     },
38049     
38050      /**
38051      * Adds a xtype elements to the layout of the nested panel
38052      * <pre><code>
38053
38054 panel.addxtype({
38055        xtype : 'ContentPanel',
38056        region: 'west',
38057        items: [ .... ]
38058    }
38059 );
38060
38061 panel.addxtype({
38062         xtype : 'NestedLayoutPanel',
38063         region: 'west',
38064         layout: {
38065            center: { },
38066            west: { }   
38067         },
38068         items : [ ... list of content panels or nested layout panels.. ]
38069    }
38070 );
38071 </code></pre>
38072      * @param {Object} cfg Xtype definition of item to add.
38073      */
38074     addxtype : function(cfg) {
38075         return this.layout.addxtype(cfg);
38076     
38077     }
38078 });/*
38079  * Based on:
38080  * Ext JS Library 1.1.1
38081  * Copyright(c) 2006-2007, Ext JS, LLC.
38082  *
38083  * Originally Released Under LGPL - original licence link has changed is not relivant.
38084  *
38085  * Fork - LGPL
38086  * <script type="text/javascript">
38087  */
38088 /**
38089  * @class Roo.TabPanel
38090  * @extends Roo.util.Observable
38091  * A lightweight tab container.
38092  * <br><br>
38093  * Usage:
38094  * <pre><code>
38095 // basic tabs 1, built from existing content
38096 var tabs = new Roo.TabPanel("tabs1");
38097 tabs.addTab("script", "View Script");
38098 tabs.addTab("markup", "View Markup");
38099 tabs.activate("script");
38100
38101 // more advanced tabs, built from javascript
38102 var jtabs = new Roo.TabPanel("jtabs");
38103 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
38104
38105 // set up the UpdateManager
38106 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
38107 var updater = tab2.getUpdateManager();
38108 updater.setDefaultUrl("ajax1.htm");
38109 tab2.on('activate', updater.refresh, updater, true);
38110
38111 // Use setUrl for Ajax loading
38112 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
38113 tab3.setUrl("ajax2.htm", null, true);
38114
38115 // Disabled tab
38116 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
38117 tab4.disable();
38118
38119 jtabs.activate("jtabs-1");
38120  * </code></pre>
38121  * @constructor
38122  * Create a new TabPanel.
38123  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
38124  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
38125  */
38126 Roo.bootstrap.panel.Tabs = function(config){
38127     /**
38128     * The container element for this TabPanel.
38129     * @type Roo.Element
38130     */
38131     this.el = Roo.get(config.el);
38132     delete config.el;
38133     if(config){
38134         if(typeof config == "boolean"){
38135             this.tabPosition = config ? "bottom" : "top";
38136         }else{
38137             Roo.apply(this, config);
38138         }
38139     }
38140     
38141     if(this.tabPosition == "bottom"){
38142         // if tabs are at the bottom = create the body first.
38143         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38144         this.el.addClass("roo-tabs-bottom");
38145     }
38146     // next create the tabs holders
38147     
38148     if (this.tabPosition == "west"){
38149         
38150         var reg = this.region; // fake it..
38151         while (reg) {
38152             if (!reg.mgr.parent) {
38153                 break;
38154             }
38155             reg = reg.mgr.parent.region;
38156         }
38157         Roo.log("got nest?");
38158         Roo.log(reg);
38159         if (reg.mgr.getRegion('west')) {
38160             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
38161             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
38162             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38163             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38164             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38165         
38166             
38167         }
38168         
38169         
38170     } else {
38171      
38172         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
38173         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38174         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38175         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38176     }
38177     
38178     
38179     if(Roo.isIE){
38180         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
38181     }
38182     
38183     // finally - if tabs are at the top, then create the body last..
38184     if(this.tabPosition != "bottom"){
38185         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
38186          * @type Roo.Element
38187          */
38188         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38189         this.el.addClass("roo-tabs-top");
38190     }
38191     this.items = [];
38192
38193     this.bodyEl.setStyle("position", "relative");
38194
38195     this.active = null;
38196     this.activateDelegate = this.activate.createDelegate(this);
38197
38198     this.addEvents({
38199         /**
38200          * @event tabchange
38201          * Fires when the active tab changes
38202          * @param {Roo.TabPanel} this
38203          * @param {Roo.TabPanelItem} activePanel The new active tab
38204          */
38205         "tabchange": true,
38206         /**
38207          * @event beforetabchange
38208          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
38209          * @param {Roo.TabPanel} this
38210          * @param {Object} e Set cancel to true on this object to cancel the tab change
38211          * @param {Roo.TabPanelItem} tab The tab being changed to
38212          */
38213         "beforetabchange" : true
38214     });
38215
38216     Roo.EventManager.onWindowResize(this.onResize, this);
38217     this.cpad = this.el.getPadding("lr");
38218     this.hiddenCount = 0;
38219
38220
38221     // toolbar on the tabbar support...
38222     if (this.toolbar) {
38223         alert("no toolbar support yet");
38224         this.toolbar  = false;
38225         /*
38226         var tcfg = this.toolbar;
38227         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
38228         this.toolbar = new Roo.Toolbar(tcfg);
38229         if (Roo.isSafari) {
38230             var tbl = tcfg.container.child('table', true);
38231             tbl.setAttribute('width', '100%');
38232         }
38233         */
38234         
38235     }
38236    
38237
38238
38239     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
38240 };
38241
38242 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
38243     /*
38244      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
38245      */
38246     tabPosition : "top",
38247     /*
38248      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
38249      */
38250     currentTabWidth : 0,
38251     /*
38252      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
38253      */
38254     minTabWidth : 40,
38255     /*
38256      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
38257      */
38258     maxTabWidth : 250,
38259     /*
38260      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
38261      */
38262     preferredTabWidth : 175,
38263     /*
38264      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
38265      */
38266     resizeTabs : false,
38267     /*
38268      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38269      */
38270     monitorResize : true,
38271     /*
38272      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
38273      */
38274     toolbar : false,  // set by caller..
38275     
38276     region : false, /// set by caller
38277     
38278     disableTooltips : true, // not used yet...
38279
38280     /**
38281      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38282      * @param {String} id The id of the div to use <b>or create</b>
38283      * @param {String} text The text for the tab
38284      * @param {String} content (optional) Content to put in the TabPanelItem body
38285      * @param {Boolean} closable (optional) True to create a close icon on the tab
38286      * @return {Roo.TabPanelItem} The created TabPanelItem
38287      */
38288     addTab : function(id, text, content, closable, tpl)
38289     {
38290         var item = new Roo.bootstrap.panel.TabItem({
38291             panel: this,
38292             id : id,
38293             text : text,
38294             closable : closable,
38295             tpl : tpl
38296         });
38297         this.addTabItem(item);
38298         if(content){
38299             item.setContent(content);
38300         }
38301         return item;
38302     },
38303
38304     /**
38305      * Returns the {@link Roo.TabPanelItem} with the specified id/index
38306      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38307      * @return {Roo.TabPanelItem}
38308      */
38309     getTab : function(id){
38310         return this.items[id];
38311     },
38312
38313     /**
38314      * Hides the {@link Roo.TabPanelItem} with the specified id/index
38315      * @param {String/Number} id The id or index of the TabPanelItem to hide.
38316      */
38317     hideTab : function(id){
38318         var t = this.items[id];
38319         if(!t.isHidden()){
38320            t.setHidden(true);
38321            this.hiddenCount++;
38322            this.autoSizeTabs();
38323         }
38324     },
38325
38326     /**
38327      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38328      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38329      */
38330     unhideTab : function(id){
38331         var t = this.items[id];
38332         if(t.isHidden()){
38333            t.setHidden(false);
38334            this.hiddenCount--;
38335            this.autoSizeTabs();
38336         }
38337     },
38338
38339     /**
38340      * Adds an existing {@link Roo.TabPanelItem}.
38341      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38342      */
38343     addTabItem : function(item)
38344     {
38345         this.items[item.id] = item;
38346         this.items.push(item);
38347         this.autoSizeTabs();
38348       //  if(this.resizeTabs){
38349     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38350   //         this.autoSizeTabs();
38351 //        }else{
38352 //            item.autoSize();
38353        // }
38354     },
38355
38356     /**
38357      * Removes a {@link Roo.TabPanelItem}.
38358      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38359      */
38360     removeTab : function(id){
38361         var items = this.items;
38362         var tab = items[id];
38363         if(!tab) { return; }
38364         var index = items.indexOf(tab);
38365         if(this.active == tab && items.length > 1){
38366             var newTab = this.getNextAvailable(index);
38367             if(newTab) {
38368                 newTab.activate();
38369             }
38370         }
38371         this.stripEl.dom.removeChild(tab.pnode.dom);
38372         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38373             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38374         }
38375         items.splice(index, 1);
38376         delete this.items[tab.id];
38377         tab.fireEvent("close", tab);
38378         tab.purgeListeners();
38379         this.autoSizeTabs();
38380     },
38381
38382     getNextAvailable : function(start){
38383         var items = this.items;
38384         var index = start;
38385         // look for a next tab that will slide over to
38386         // replace the one being removed
38387         while(index < items.length){
38388             var item = items[++index];
38389             if(item && !item.isHidden()){
38390                 return item;
38391             }
38392         }
38393         // if one isn't found select the previous tab (on the left)
38394         index = start;
38395         while(index >= 0){
38396             var item = items[--index];
38397             if(item && !item.isHidden()){
38398                 return item;
38399             }
38400         }
38401         return null;
38402     },
38403
38404     /**
38405      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38406      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38407      */
38408     disableTab : function(id){
38409         var tab = this.items[id];
38410         if(tab && this.active != tab){
38411             tab.disable();
38412         }
38413     },
38414
38415     /**
38416      * Enables a {@link Roo.TabPanelItem} that is disabled.
38417      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38418      */
38419     enableTab : function(id){
38420         var tab = this.items[id];
38421         tab.enable();
38422     },
38423
38424     /**
38425      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38426      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38427      * @return {Roo.TabPanelItem} The TabPanelItem.
38428      */
38429     activate : function(id)
38430     {
38431         //Roo.log('activite:'  + id);
38432         
38433         var tab = this.items[id];
38434         if(!tab){
38435             return null;
38436         }
38437         if(tab == this.active || tab.disabled){
38438             return tab;
38439         }
38440         var e = {};
38441         this.fireEvent("beforetabchange", this, e, tab);
38442         if(e.cancel !== true && !tab.disabled){
38443             if(this.active){
38444                 this.active.hide();
38445             }
38446             this.active = this.items[id];
38447             this.active.show();
38448             this.fireEvent("tabchange", this, this.active);
38449         }
38450         return tab;
38451     },
38452
38453     /**
38454      * Gets the active {@link Roo.TabPanelItem}.
38455      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38456      */
38457     getActiveTab : function(){
38458         return this.active;
38459     },
38460
38461     /**
38462      * Updates the tab body element to fit the height of the container element
38463      * for overflow scrolling
38464      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38465      */
38466     syncHeight : function(targetHeight){
38467         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38468         var bm = this.bodyEl.getMargins();
38469         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38470         this.bodyEl.setHeight(newHeight);
38471         return newHeight;
38472     },
38473
38474     onResize : function(){
38475         if(this.monitorResize){
38476             this.autoSizeTabs();
38477         }
38478     },
38479
38480     /**
38481      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38482      */
38483     beginUpdate : function(){
38484         this.updating = true;
38485     },
38486
38487     /**
38488      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38489      */
38490     endUpdate : function(){
38491         this.updating = false;
38492         this.autoSizeTabs();
38493     },
38494
38495     /**
38496      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38497      */
38498     autoSizeTabs : function()
38499     {
38500         var count = this.items.length;
38501         var vcount = count - this.hiddenCount;
38502         
38503         if (vcount < 2) {
38504             this.stripEl.hide();
38505         } else {
38506             this.stripEl.show();
38507         }
38508         
38509         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38510             return;
38511         }
38512         
38513         
38514         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38515         var availWidth = Math.floor(w / vcount);
38516         var b = this.stripBody;
38517         if(b.getWidth() > w){
38518             var tabs = this.items;
38519             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38520             if(availWidth < this.minTabWidth){
38521                 /*if(!this.sleft){    // incomplete scrolling code
38522                     this.createScrollButtons();
38523                 }
38524                 this.showScroll();
38525                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38526             }
38527         }else{
38528             if(this.currentTabWidth < this.preferredTabWidth){
38529                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38530             }
38531         }
38532     },
38533
38534     /**
38535      * Returns the number of tabs in this TabPanel.
38536      * @return {Number}
38537      */
38538      getCount : function(){
38539          return this.items.length;
38540      },
38541
38542     /**
38543      * Resizes all the tabs to the passed width
38544      * @param {Number} The new width
38545      */
38546     setTabWidth : function(width){
38547         this.currentTabWidth = width;
38548         for(var i = 0, len = this.items.length; i < len; i++) {
38549                 if(!this.items[i].isHidden()) {
38550                 this.items[i].setWidth(width);
38551             }
38552         }
38553     },
38554
38555     /**
38556      * Destroys this TabPanel
38557      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38558      */
38559     destroy : function(removeEl){
38560         Roo.EventManager.removeResizeListener(this.onResize, this);
38561         for(var i = 0, len = this.items.length; i < len; i++){
38562             this.items[i].purgeListeners();
38563         }
38564         if(removeEl === true){
38565             this.el.update("");
38566             this.el.remove();
38567         }
38568     },
38569     
38570     createStrip : function(container)
38571     {
38572         var strip = document.createElement("nav");
38573         strip.className = Roo.bootstrap.version == 4 ?
38574             "navbar-light bg-light" : 
38575             "navbar navbar-default"; //"x-tabs-wrap";
38576         container.appendChild(strip);
38577         return strip;
38578     },
38579     
38580     createStripList : function(strip)
38581     {
38582         // div wrapper for retard IE
38583         // returns the "tr" element.
38584         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38585         //'<div class="x-tabs-strip-wrap">'+
38586           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38587           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38588         return strip.firstChild; //.firstChild.firstChild.firstChild;
38589     },
38590     createBody : function(container)
38591     {
38592         var body = document.createElement("div");
38593         Roo.id(body, "tab-body");
38594         //Roo.fly(body).addClass("x-tabs-body");
38595         Roo.fly(body).addClass("tab-content");
38596         container.appendChild(body);
38597         return body;
38598     },
38599     createItemBody :function(bodyEl, id){
38600         var body = Roo.getDom(id);
38601         if(!body){
38602             body = document.createElement("div");
38603             body.id = id;
38604         }
38605         //Roo.fly(body).addClass("x-tabs-item-body");
38606         Roo.fly(body).addClass("tab-pane");
38607          bodyEl.insertBefore(body, bodyEl.firstChild);
38608         return body;
38609     },
38610     /** @private */
38611     createStripElements :  function(stripEl, text, closable, tpl)
38612     {
38613         var td = document.createElement("li"); // was td..
38614         td.className = 'nav-item';
38615         
38616         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38617         
38618         
38619         stripEl.appendChild(td);
38620         /*if(closable){
38621             td.className = "x-tabs-closable";
38622             if(!this.closeTpl){
38623                 this.closeTpl = new Roo.Template(
38624                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38625                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38626                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38627                 );
38628             }
38629             var el = this.closeTpl.overwrite(td, {"text": text});
38630             var close = el.getElementsByTagName("div")[0];
38631             var inner = el.getElementsByTagName("em")[0];
38632             return {"el": el, "close": close, "inner": inner};
38633         } else {
38634         */
38635         // not sure what this is..
38636 //            if(!this.tabTpl){
38637                 //this.tabTpl = new Roo.Template(
38638                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38639                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38640                 //);
38641 //                this.tabTpl = new Roo.Template(
38642 //                   '<a href="#">' +
38643 //                   '<span unselectable="on"' +
38644 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38645 //                            ' >{text}</span></a>'
38646 //                );
38647 //                
38648 //            }
38649
38650
38651             var template = tpl || this.tabTpl || false;
38652             
38653             if(!template){
38654                 template =  new Roo.Template(
38655                         Roo.bootstrap.version == 4 ? 
38656                             (
38657                                 '<a class="nav-link" href="#" unselectable="on"' +
38658                                      (this.disableTooltips ? '' : ' title="{text}"') +
38659                                      ' >{text}</a>'
38660                             ) : (
38661                                 '<a class="nav-link" href="#">' +
38662                                 '<span unselectable="on"' +
38663                                          (this.disableTooltips ? '' : ' title="{text}"') +
38664                                     ' >{text}</span></a>'
38665                             )
38666                 );
38667             }
38668             
38669             switch (typeof(template)) {
38670                 case 'object' :
38671                     break;
38672                 case 'string' :
38673                     template = new Roo.Template(template);
38674                     break;
38675                 default :
38676                     break;
38677             }
38678             
38679             var el = template.overwrite(td, {"text": text});
38680             
38681             var inner = el.getElementsByTagName("span")[0];
38682             
38683             return {"el": el, "inner": inner};
38684             
38685     }
38686         
38687     
38688 });
38689
38690 /**
38691  * @class Roo.TabPanelItem
38692  * @extends Roo.util.Observable
38693  * Represents an individual item (tab plus body) in a TabPanel.
38694  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38695  * @param {String} id The id of this TabPanelItem
38696  * @param {String} text The text for the tab of this TabPanelItem
38697  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38698  */
38699 Roo.bootstrap.panel.TabItem = function(config){
38700     /**
38701      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38702      * @type Roo.TabPanel
38703      */
38704     this.tabPanel = config.panel;
38705     /**
38706      * The id for this TabPanelItem
38707      * @type String
38708      */
38709     this.id = config.id;
38710     /** @private */
38711     this.disabled = false;
38712     /** @private */
38713     this.text = config.text;
38714     /** @private */
38715     this.loaded = false;
38716     this.closable = config.closable;
38717
38718     /**
38719      * The body element for this TabPanelItem.
38720      * @type Roo.Element
38721      */
38722     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38723     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38724     this.bodyEl.setStyle("display", "block");
38725     this.bodyEl.setStyle("zoom", "1");
38726     //this.hideAction();
38727
38728     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38729     /** @private */
38730     this.el = Roo.get(els.el);
38731     this.inner = Roo.get(els.inner, true);
38732      this.textEl = Roo.bootstrap.version == 4 ?
38733         this.el : Roo.get(this.el.dom.firstChild, true);
38734
38735     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
38736     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
38737
38738     
38739 //    this.el.on("mousedown", this.onTabMouseDown, this);
38740     this.el.on("click", this.onTabClick, this);
38741     /** @private */
38742     if(config.closable){
38743         var c = Roo.get(els.close, true);
38744         c.dom.title = this.closeText;
38745         c.addClassOnOver("close-over");
38746         c.on("click", this.closeClick, this);
38747      }
38748
38749     this.addEvents({
38750          /**
38751          * @event activate
38752          * Fires when this tab becomes the active tab.
38753          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38754          * @param {Roo.TabPanelItem} this
38755          */
38756         "activate": true,
38757         /**
38758          * @event beforeclose
38759          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38760          * @param {Roo.TabPanelItem} this
38761          * @param {Object} e Set cancel to true on this object to cancel the close.
38762          */
38763         "beforeclose": true,
38764         /**
38765          * @event close
38766          * Fires when this tab is closed.
38767          * @param {Roo.TabPanelItem} this
38768          */
38769          "close": true,
38770         /**
38771          * @event deactivate
38772          * Fires when this tab is no longer the active tab.
38773          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38774          * @param {Roo.TabPanelItem} this
38775          */
38776          "deactivate" : true
38777     });
38778     this.hidden = false;
38779
38780     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38781 };
38782
38783 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38784            {
38785     purgeListeners : function(){
38786        Roo.util.Observable.prototype.purgeListeners.call(this);
38787        this.el.removeAllListeners();
38788     },
38789     /**
38790      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38791      */
38792     show : function(){
38793         this.status_node.addClass("active");
38794         this.showAction();
38795         if(Roo.isOpera){
38796             this.tabPanel.stripWrap.repaint();
38797         }
38798         this.fireEvent("activate", this.tabPanel, this);
38799     },
38800
38801     /**
38802      * Returns true if this tab is the active tab.
38803      * @return {Boolean}
38804      */
38805     isActive : function(){
38806         return this.tabPanel.getActiveTab() == this;
38807     },
38808
38809     /**
38810      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38811      */
38812     hide : function(){
38813         this.status_node.removeClass("active");
38814         this.hideAction();
38815         this.fireEvent("deactivate", this.tabPanel, this);
38816     },
38817
38818     hideAction : function(){
38819         this.bodyEl.hide();
38820         this.bodyEl.setStyle("position", "absolute");
38821         this.bodyEl.setLeft("-20000px");
38822         this.bodyEl.setTop("-20000px");
38823     },
38824
38825     showAction : function(){
38826         this.bodyEl.setStyle("position", "relative");
38827         this.bodyEl.setTop("");
38828         this.bodyEl.setLeft("");
38829         this.bodyEl.show();
38830     },
38831
38832     /**
38833      * Set the tooltip for the tab.
38834      * @param {String} tooltip The tab's tooltip
38835      */
38836     setTooltip : function(text){
38837         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38838             this.textEl.dom.qtip = text;
38839             this.textEl.dom.removeAttribute('title');
38840         }else{
38841             this.textEl.dom.title = text;
38842         }
38843     },
38844
38845     onTabClick : function(e){
38846         e.preventDefault();
38847         this.tabPanel.activate(this.id);
38848     },
38849
38850     onTabMouseDown : function(e){
38851         e.preventDefault();
38852         this.tabPanel.activate(this.id);
38853     },
38854 /*
38855     getWidth : function(){
38856         return this.inner.getWidth();
38857     },
38858
38859     setWidth : function(width){
38860         var iwidth = width - this.linode.getPadding("lr");
38861         this.inner.setWidth(iwidth);
38862         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38863         this.linode.setWidth(width);
38864     },
38865 */
38866     /**
38867      * Show or hide the tab
38868      * @param {Boolean} hidden True to hide or false to show.
38869      */
38870     setHidden : function(hidden){
38871         this.hidden = hidden;
38872         this.linode.setStyle("display", hidden ? "none" : "");
38873     },
38874
38875     /**
38876      * Returns true if this tab is "hidden"
38877      * @return {Boolean}
38878      */
38879     isHidden : function(){
38880         return this.hidden;
38881     },
38882
38883     /**
38884      * Returns the text for this tab
38885      * @return {String}
38886      */
38887     getText : function(){
38888         return this.text;
38889     },
38890     /*
38891     autoSize : function(){
38892         //this.el.beginMeasure();
38893         this.textEl.setWidth(1);
38894         /*
38895          *  #2804 [new] Tabs in Roojs
38896          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38897          */
38898         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38899         //this.el.endMeasure();
38900     //},
38901
38902     /**
38903      * Sets the text for the tab (Note: this also sets the tooltip text)
38904      * @param {String} text The tab's text and tooltip
38905      */
38906     setText : function(text){
38907         this.text = text;
38908         this.textEl.update(text);
38909         this.setTooltip(text);
38910         //if(!this.tabPanel.resizeTabs){
38911         //    this.autoSize();
38912         //}
38913     },
38914     /**
38915      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38916      */
38917     activate : function(){
38918         this.tabPanel.activate(this.id);
38919     },
38920
38921     /**
38922      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38923      */
38924     disable : function(){
38925         if(this.tabPanel.active != this){
38926             this.disabled = true;
38927             this.status_node.addClass("disabled");
38928         }
38929     },
38930
38931     /**
38932      * Enables this TabPanelItem if it was previously disabled.
38933      */
38934     enable : function(){
38935         this.disabled = false;
38936         this.status_node.removeClass("disabled");
38937     },
38938
38939     /**
38940      * Sets the content for this TabPanelItem.
38941      * @param {String} content The content
38942      * @param {Boolean} loadScripts true to look for and load scripts
38943      */
38944     setContent : function(content, loadScripts){
38945         this.bodyEl.update(content, loadScripts);
38946     },
38947
38948     /**
38949      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38950      * @return {Roo.UpdateManager} The UpdateManager
38951      */
38952     getUpdateManager : function(){
38953         return this.bodyEl.getUpdateManager();
38954     },
38955
38956     /**
38957      * Set a URL to be used to load the content for this TabPanelItem.
38958      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38959      * @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)
38960      * @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)
38961      * @return {Roo.UpdateManager} The UpdateManager
38962      */
38963     setUrl : function(url, params, loadOnce){
38964         if(this.refreshDelegate){
38965             this.un('activate', this.refreshDelegate);
38966         }
38967         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38968         this.on("activate", this.refreshDelegate);
38969         return this.bodyEl.getUpdateManager();
38970     },
38971
38972     /** @private */
38973     _handleRefresh : function(url, params, loadOnce){
38974         if(!loadOnce || !this.loaded){
38975             var updater = this.bodyEl.getUpdateManager();
38976             updater.update(url, params, this._setLoaded.createDelegate(this));
38977         }
38978     },
38979
38980     /**
38981      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38982      *   Will fail silently if the setUrl method has not been called.
38983      *   This does not activate the panel, just updates its content.
38984      */
38985     refresh : function(){
38986         if(this.refreshDelegate){
38987            this.loaded = false;
38988            this.refreshDelegate();
38989         }
38990     },
38991
38992     /** @private */
38993     _setLoaded : function(){
38994         this.loaded = true;
38995     },
38996
38997     /** @private */
38998     closeClick : function(e){
38999         var o = {};
39000         e.stopEvent();
39001         this.fireEvent("beforeclose", this, o);
39002         if(o.cancel !== true){
39003             this.tabPanel.removeTab(this.id);
39004         }
39005     },
39006     /**
39007      * The text displayed in the tooltip for the close icon.
39008      * @type String
39009      */
39010     closeText : "Close this tab"
39011 });
39012 /**
39013 *    This script refer to:
39014 *    Title: International Telephone Input
39015 *    Author: Jack O'Connor
39016 *    Code version:  v12.1.12
39017 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39018 **/
39019
39020 Roo.bootstrap.PhoneInputData = function() {
39021     var d = [
39022       [
39023         "Afghanistan (‫افغانستان‬‎)",
39024         "af",
39025         "93"
39026       ],
39027       [
39028         "Albania (Shqipëri)",
39029         "al",
39030         "355"
39031       ],
39032       [
39033         "Algeria (‫الجزائر‬‎)",
39034         "dz",
39035         "213"
39036       ],
39037       [
39038         "American Samoa",
39039         "as",
39040         "1684"
39041       ],
39042       [
39043         "Andorra",
39044         "ad",
39045         "376"
39046       ],
39047       [
39048         "Angola",
39049         "ao",
39050         "244"
39051       ],
39052       [
39053         "Anguilla",
39054         "ai",
39055         "1264"
39056       ],
39057       [
39058         "Antigua and Barbuda",
39059         "ag",
39060         "1268"
39061       ],
39062       [
39063         "Argentina",
39064         "ar",
39065         "54"
39066       ],
39067       [
39068         "Armenia (Հայաստան)",
39069         "am",
39070         "374"
39071       ],
39072       [
39073         "Aruba",
39074         "aw",
39075         "297"
39076       ],
39077       [
39078         "Australia",
39079         "au",
39080         "61",
39081         0
39082       ],
39083       [
39084         "Austria (Österreich)",
39085         "at",
39086         "43"
39087       ],
39088       [
39089         "Azerbaijan (Azərbaycan)",
39090         "az",
39091         "994"
39092       ],
39093       [
39094         "Bahamas",
39095         "bs",
39096         "1242"
39097       ],
39098       [
39099         "Bahrain (‫البحرين‬‎)",
39100         "bh",
39101         "973"
39102       ],
39103       [
39104         "Bangladesh (বাংলাদেশ)",
39105         "bd",
39106         "880"
39107       ],
39108       [
39109         "Barbados",
39110         "bb",
39111         "1246"
39112       ],
39113       [
39114         "Belarus (Беларусь)",
39115         "by",
39116         "375"
39117       ],
39118       [
39119         "Belgium (België)",
39120         "be",
39121         "32"
39122       ],
39123       [
39124         "Belize",
39125         "bz",
39126         "501"
39127       ],
39128       [
39129         "Benin (Bénin)",
39130         "bj",
39131         "229"
39132       ],
39133       [
39134         "Bermuda",
39135         "bm",
39136         "1441"
39137       ],
39138       [
39139         "Bhutan (འབྲུག)",
39140         "bt",
39141         "975"
39142       ],
39143       [
39144         "Bolivia",
39145         "bo",
39146         "591"
39147       ],
39148       [
39149         "Bosnia and Herzegovina (Босна и Херцеговина)",
39150         "ba",
39151         "387"
39152       ],
39153       [
39154         "Botswana",
39155         "bw",
39156         "267"
39157       ],
39158       [
39159         "Brazil (Brasil)",
39160         "br",
39161         "55"
39162       ],
39163       [
39164         "British Indian Ocean Territory",
39165         "io",
39166         "246"
39167       ],
39168       [
39169         "British Virgin Islands",
39170         "vg",
39171         "1284"
39172       ],
39173       [
39174         "Brunei",
39175         "bn",
39176         "673"
39177       ],
39178       [
39179         "Bulgaria (България)",
39180         "bg",
39181         "359"
39182       ],
39183       [
39184         "Burkina Faso",
39185         "bf",
39186         "226"
39187       ],
39188       [
39189         "Burundi (Uburundi)",
39190         "bi",
39191         "257"
39192       ],
39193       [
39194         "Cambodia (កម្ពុជា)",
39195         "kh",
39196         "855"
39197       ],
39198       [
39199         "Cameroon (Cameroun)",
39200         "cm",
39201         "237"
39202       ],
39203       [
39204         "Canada",
39205         "ca",
39206         "1",
39207         1,
39208         ["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"]
39209       ],
39210       [
39211         "Cape Verde (Kabu Verdi)",
39212         "cv",
39213         "238"
39214       ],
39215       [
39216         "Caribbean Netherlands",
39217         "bq",
39218         "599",
39219         1
39220       ],
39221       [
39222         "Cayman Islands",
39223         "ky",
39224         "1345"
39225       ],
39226       [
39227         "Central African Republic (République centrafricaine)",
39228         "cf",
39229         "236"
39230       ],
39231       [
39232         "Chad (Tchad)",
39233         "td",
39234         "235"
39235       ],
39236       [
39237         "Chile",
39238         "cl",
39239         "56"
39240       ],
39241       [
39242         "China (中国)",
39243         "cn",
39244         "86"
39245       ],
39246       [
39247         "Christmas Island",
39248         "cx",
39249         "61",
39250         2
39251       ],
39252       [
39253         "Cocos (Keeling) Islands",
39254         "cc",
39255         "61",
39256         1
39257       ],
39258       [
39259         "Colombia",
39260         "co",
39261         "57"
39262       ],
39263       [
39264         "Comoros (‫جزر القمر‬‎)",
39265         "km",
39266         "269"
39267       ],
39268       [
39269         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
39270         "cd",
39271         "243"
39272       ],
39273       [
39274         "Congo (Republic) (Congo-Brazzaville)",
39275         "cg",
39276         "242"
39277       ],
39278       [
39279         "Cook Islands",
39280         "ck",
39281         "682"
39282       ],
39283       [
39284         "Costa Rica",
39285         "cr",
39286         "506"
39287       ],
39288       [
39289         "Côte d’Ivoire",
39290         "ci",
39291         "225"
39292       ],
39293       [
39294         "Croatia (Hrvatska)",
39295         "hr",
39296         "385"
39297       ],
39298       [
39299         "Cuba",
39300         "cu",
39301         "53"
39302       ],
39303       [
39304         "Curaçao",
39305         "cw",
39306         "599",
39307         0
39308       ],
39309       [
39310         "Cyprus (Κύπρος)",
39311         "cy",
39312         "357"
39313       ],
39314       [
39315         "Czech Republic (Česká republika)",
39316         "cz",
39317         "420"
39318       ],
39319       [
39320         "Denmark (Danmark)",
39321         "dk",
39322         "45"
39323       ],
39324       [
39325         "Djibouti",
39326         "dj",
39327         "253"
39328       ],
39329       [
39330         "Dominica",
39331         "dm",
39332         "1767"
39333       ],
39334       [
39335         "Dominican Republic (República Dominicana)",
39336         "do",
39337         "1",
39338         2,
39339         ["809", "829", "849"]
39340       ],
39341       [
39342         "Ecuador",
39343         "ec",
39344         "593"
39345       ],
39346       [
39347         "Egypt (‫مصر‬‎)",
39348         "eg",
39349         "20"
39350       ],
39351       [
39352         "El Salvador",
39353         "sv",
39354         "503"
39355       ],
39356       [
39357         "Equatorial Guinea (Guinea Ecuatorial)",
39358         "gq",
39359         "240"
39360       ],
39361       [
39362         "Eritrea",
39363         "er",
39364         "291"
39365       ],
39366       [
39367         "Estonia (Eesti)",
39368         "ee",
39369         "372"
39370       ],
39371       [
39372         "Ethiopia",
39373         "et",
39374         "251"
39375       ],
39376       [
39377         "Falkland Islands (Islas Malvinas)",
39378         "fk",
39379         "500"
39380       ],
39381       [
39382         "Faroe Islands (Føroyar)",
39383         "fo",
39384         "298"
39385       ],
39386       [
39387         "Fiji",
39388         "fj",
39389         "679"
39390       ],
39391       [
39392         "Finland (Suomi)",
39393         "fi",
39394         "358",
39395         0
39396       ],
39397       [
39398         "France",
39399         "fr",
39400         "33"
39401       ],
39402       [
39403         "French Guiana (Guyane française)",
39404         "gf",
39405         "594"
39406       ],
39407       [
39408         "French Polynesia (Polynésie française)",
39409         "pf",
39410         "689"
39411       ],
39412       [
39413         "Gabon",
39414         "ga",
39415         "241"
39416       ],
39417       [
39418         "Gambia",
39419         "gm",
39420         "220"
39421       ],
39422       [
39423         "Georgia (საქართველო)",
39424         "ge",
39425         "995"
39426       ],
39427       [
39428         "Germany (Deutschland)",
39429         "de",
39430         "49"
39431       ],
39432       [
39433         "Ghana (Gaana)",
39434         "gh",
39435         "233"
39436       ],
39437       [
39438         "Gibraltar",
39439         "gi",
39440         "350"
39441       ],
39442       [
39443         "Greece (Ελλάδα)",
39444         "gr",
39445         "30"
39446       ],
39447       [
39448         "Greenland (Kalaallit Nunaat)",
39449         "gl",
39450         "299"
39451       ],
39452       [
39453         "Grenada",
39454         "gd",
39455         "1473"
39456       ],
39457       [
39458         "Guadeloupe",
39459         "gp",
39460         "590",
39461         0
39462       ],
39463       [
39464         "Guam",
39465         "gu",
39466         "1671"
39467       ],
39468       [
39469         "Guatemala",
39470         "gt",
39471         "502"
39472       ],
39473       [
39474         "Guernsey",
39475         "gg",
39476         "44",
39477         1
39478       ],
39479       [
39480         "Guinea (Guinée)",
39481         "gn",
39482         "224"
39483       ],
39484       [
39485         "Guinea-Bissau (Guiné Bissau)",
39486         "gw",
39487         "245"
39488       ],
39489       [
39490         "Guyana",
39491         "gy",
39492         "592"
39493       ],
39494       [
39495         "Haiti",
39496         "ht",
39497         "509"
39498       ],
39499       [
39500         "Honduras",
39501         "hn",
39502         "504"
39503       ],
39504       [
39505         "Hong Kong (香港)",
39506         "hk",
39507         "852"
39508       ],
39509       [
39510         "Hungary (Magyarország)",
39511         "hu",
39512         "36"
39513       ],
39514       [
39515         "Iceland (Ísland)",
39516         "is",
39517         "354"
39518       ],
39519       [
39520         "India (भारत)",
39521         "in",
39522         "91"
39523       ],
39524       [
39525         "Indonesia",
39526         "id",
39527         "62"
39528       ],
39529       [
39530         "Iran (‫ایران‬‎)",
39531         "ir",
39532         "98"
39533       ],
39534       [
39535         "Iraq (‫العراق‬‎)",
39536         "iq",
39537         "964"
39538       ],
39539       [
39540         "Ireland",
39541         "ie",
39542         "353"
39543       ],
39544       [
39545         "Isle of Man",
39546         "im",
39547         "44",
39548         2
39549       ],
39550       [
39551         "Israel (‫ישראל‬‎)",
39552         "il",
39553         "972"
39554       ],
39555       [
39556         "Italy (Italia)",
39557         "it",
39558         "39",
39559         0
39560       ],
39561       [
39562         "Jamaica",
39563         "jm",
39564         "1876"
39565       ],
39566       [
39567         "Japan (日本)",
39568         "jp",
39569         "81"
39570       ],
39571       [
39572         "Jersey",
39573         "je",
39574         "44",
39575         3
39576       ],
39577       [
39578         "Jordan (‫الأردن‬‎)",
39579         "jo",
39580         "962"
39581       ],
39582       [
39583         "Kazakhstan (Казахстан)",
39584         "kz",
39585         "7",
39586         1
39587       ],
39588       [
39589         "Kenya",
39590         "ke",
39591         "254"
39592       ],
39593       [
39594         "Kiribati",
39595         "ki",
39596         "686"
39597       ],
39598       [
39599         "Kosovo",
39600         "xk",
39601         "383"
39602       ],
39603       [
39604         "Kuwait (‫الكويت‬‎)",
39605         "kw",
39606         "965"
39607       ],
39608       [
39609         "Kyrgyzstan (Кыргызстан)",
39610         "kg",
39611         "996"
39612       ],
39613       [
39614         "Laos (ລາວ)",
39615         "la",
39616         "856"
39617       ],
39618       [
39619         "Latvia (Latvija)",
39620         "lv",
39621         "371"
39622       ],
39623       [
39624         "Lebanon (‫لبنان‬‎)",
39625         "lb",
39626         "961"
39627       ],
39628       [
39629         "Lesotho",
39630         "ls",
39631         "266"
39632       ],
39633       [
39634         "Liberia",
39635         "lr",
39636         "231"
39637       ],
39638       [
39639         "Libya (‫ليبيا‬‎)",
39640         "ly",
39641         "218"
39642       ],
39643       [
39644         "Liechtenstein",
39645         "li",
39646         "423"
39647       ],
39648       [
39649         "Lithuania (Lietuva)",
39650         "lt",
39651         "370"
39652       ],
39653       [
39654         "Luxembourg",
39655         "lu",
39656         "352"
39657       ],
39658       [
39659         "Macau (澳門)",
39660         "mo",
39661         "853"
39662       ],
39663       [
39664         "Macedonia (FYROM) (Македонија)",
39665         "mk",
39666         "389"
39667       ],
39668       [
39669         "Madagascar (Madagasikara)",
39670         "mg",
39671         "261"
39672       ],
39673       [
39674         "Malawi",
39675         "mw",
39676         "265"
39677       ],
39678       [
39679         "Malaysia",
39680         "my",
39681         "60"
39682       ],
39683       [
39684         "Maldives",
39685         "mv",
39686         "960"
39687       ],
39688       [
39689         "Mali",
39690         "ml",
39691         "223"
39692       ],
39693       [
39694         "Malta",
39695         "mt",
39696         "356"
39697       ],
39698       [
39699         "Marshall Islands",
39700         "mh",
39701         "692"
39702       ],
39703       [
39704         "Martinique",
39705         "mq",
39706         "596"
39707       ],
39708       [
39709         "Mauritania (‫موريتانيا‬‎)",
39710         "mr",
39711         "222"
39712       ],
39713       [
39714         "Mauritius (Moris)",
39715         "mu",
39716         "230"
39717       ],
39718       [
39719         "Mayotte",
39720         "yt",
39721         "262",
39722         1
39723       ],
39724       [
39725         "Mexico (México)",
39726         "mx",
39727         "52"
39728       ],
39729       [
39730         "Micronesia",
39731         "fm",
39732         "691"
39733       ],
39734       [
39735         "Moldova (Republica Moldova)",
39736         "md",
39737         "373"
39738       ],
39739       [
39740         "Monaco",
39741         "mc",
39742         "377"
39743       ],
39744       [
39745         "Mongolia (Монгол)",
39746         "mn",
39747         "976"
39748       ],
39749       [
39750         "Montenegro (Crna Gora)",
39751         "me",
39752         "382"
39753       ],
39754       [
39755         "Montserrat",
39756         "ms",
39757         "1664"
39758       ],
39759       [
39760         "Morocco (‫المغرب‬‎)",
39761         "ma",
39762         "212",
39763         0
39764       ],
39765       [
39766         "Mozambique (Moçambique)",
39767         "mz",
39768         "258"
39769       ],
39770       [
39771         "Myanmar (Burma) (မြန်မာ)",
39772         "mm",
39773         "95"
39774       ],
39775       [
39776         "Namibia (Namibië)",
39777         "na",
39778         "264"
39779       ],
39780       [
39781         "Nauru",
39782         "nr",
39783         "674"
39784       ],
39785       [
39786         "Nepal (नेपाल)",
39787         "np",
39788         "977"
39789       ],
39790       [
39791         "Netherlands (Nederland)",
39792         "nl",
39793         "31"
39794       ],
39795       [
39796         "New Caledonia (Nouvelle-Calédonie)",
39797         "nc",
39798         "687"
39799       ],
39800       [
39801         "New Zealand",
39802         "nz",
39803         "64"
39804       ],
39805       [
39806         "Nicaragua",
39807         "ni",
39808         "505"
39809       ],
39810       [
39811         "Niger (Nijar)",
39812         "ne",
39813         "227"
39814       ],
39815       [
39816         "Nigeria",
39817         "ng",
39818         "234"
39819       ],
39820       [
39821         "Niue",
39822         "nu",
39823         "683"
39824       ],
39825       [
39826         "Norfolk Island",
39827         "nf",
39828         "672"
39829       ],
39830       [
39831         "North Korea (조선 민주주의 인민 공화국)",
39832         "kp",
39833         "850"
39834       ],
39835       [
39836         "Northern Mariana Islands",
39837         "mp",
39838         "1670"
39839       ],
39840       [
39841         "Norway (Norge)",
39842         "no",
39843         "47",
39844         0
39845       ],
39846       [
39847         "Oman (‫عُمان‬‎)",
39848         "om",
39849         "968"
39850       ],
39851       [
39852         "Pakistan (‫پاکستان‬‎)",
39853         "pk",
39854         "92"
39855       ],
39856       [
39857         "Palau",
39858         "pw",
39859         "680"
39860       ],
39861       [
39862         "Palestine (‫فلسطين‬‎)",
39863         "ps",
39864         "970"
39865       ],
39866       [
39867         "Panama (Panamá)",
39868         "pa",
39869         "507"
39870       ],
39871       [
39872         "Papua New Guinea",
39873         "pg",
39874         "675"
39875       ],
39876       [
39877         "Paraguay",
39878         "py",
39879         "595"
39880       ],
39881       [
39882         "Peru (Perú)",
39883         "pe",
39884         "51"
39885       ],
39886       [
39887         "Philippines",
39888         "ph",
39889         "63"
39890       ],
39891       [
39892         "Poland (Polska)",
39893         "pl",
39894         "48"
39895       ],
39896       [
39897         "Portugal",
39898         "pt",
39899         "351"
39900       ],
39901       [
39902         "Puerto Rico",
39903         "pr",
39904         "1",
39905         3,
39906         ["787", "939"]
39907       ],
39908       [
39909         "Qatar (‫قطر‬‎)",
39910         "qa",
39911         "974"
39912       ],
39913       [
39914         "Réunion (La Réunion)",
39915         "re",
39916         "262",
39917         0
39918       ],
39919       [
39920         "Romania (România)",
39921         "ro",
39922         "40"
39923       ],
39924       [
39925         "Russia (Россия)",
39926         "ru",
39927         "7",
39928         0
39929       ],
39930       [
39931         "Rwanda",
39932         "rw",
39933         "250"
39934       ],
39935       [
39936         "Saint Barthélemy",
39937         "bl",
39938         "590",
39939         1
39940       ],
39941       [
39942         "Saint Helena",
39943         "sh",
39944         "290"
39945       ],
39946       [
39947         "Saint Kitts and Nevis",
39948         "kn",
39949         "1869"
39950       ],
39951       [
39952         "Saint Lucia",
39953         "lc",
39954         "1758"
39955       ],
39956       [
39957         "Saint Martin (Saint-Martin (partie française))",
39958         "mf",
39959         "590",
39960         2
39961       ],
39962       [
39963         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39964         "pm",
39965         "508"
39966       ],
39967       [
39968         "Saint Vincent and the Grenadines",
39969         "vc",
39970         "1784"
39971       ],
39972       [
39973         "Samoa",
39974         "ws",
39975         "685"
39976       ],
39977       [
39978         "San Marino",
39979         "sm",
39980         "378"
39981       ],
39982       [
39983         "São Tomé and Príncipe (São Tomé e Príncipe)",
39984         "st",
39985         "239"
39986       ],
39987       [
39988         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39989         "sa",
39990         "966"
39991       ],
39992       [
39993         "Senegal (Sénégal)",
39994         "sn",
39995         "221"
39996       ],
39997       [
39998         "Serbia (Србија)",
39999         "rs",
40000         "381"
40001       ],
40002       [
40003         "Seychelles",
40004         "sc",
40005         "248"
40006       ],
40007       [
40008         "Sierra Leone",
40009         "sl",
40010         "232"
40011       ],
40012       [
40013         "Singapore",
40014         "sg",
40015         "65"
40016       ],
40017       [
40018         "Sint Maarten",
40019         "sx",
40020         "1721"
40021       ],
40022       [
40023         "Slovakia (Slovensko)",
40024         "sk",
40025         "421"
40026       ],
40027       [
40028         "Slovenia (Slovenija)",
40029         "si",
40030         "386"
40031       ],
40032       [
40033         "Solomon Islands",
40034         "sb",
40035         "677"
40036       ],
40037       [
40038         "Somalia (Soomaaliya)",
40039         "so",
40040         "252"
40041       ],
40042       [
40043         "South Africa",
40044         "za",
40045         "27"
40046       ],
40047       [
40048         "South Korea (대한민국)",
40049         "kr",
40050         "82"
40051       ],
40052       [
40053         "South Sudan (‫جنوب السودان‬‎)",
40054         "ss",
40055         "211"
40056       ],
40057       [
40058         "Spain (España)",
40059         "es",
40060         "34"
40061       ],
40062       [
40063         "Sri Lanka (ශ්‍රී ලංකාව)",
40064         "lk",
40065         "94"
40066       ],
40067       [
40068         "Sudan (‫السودان‬‎)",
40069         "sd",
40070         "249"
40071       ],
40072       [
40073         "Suriname",
40074         "sr",
40075         "597"
40076       ],
40077       [
40078         "Svalbard and Jan Mayen",
40079         "sj",
40080         "47",
40081         1
40082       ],
40083       [
40084         "Swaziland",
40085         "sz",
40086         "268"
40087       ],
40088       [
40089         "Sweden (Sverige)",
40090         "se",
40091         "46"
40092       ],
40093       [
40094         "Switzerland (Schweiz)",
40095         "ch",
40096         "41"
40097       ],
40098       [
40099         "Syria (‫سوريا‬‎)",
40100         "sy",
40101         "963"
40102       ],
40103       [
40104         "Taiwan (台灣)",
40105         "tw",
40106         "886"
40107       ],
40108       [
40109         "Tajikistan",
40110         "tj",
40111         "992"
40112       ],
40113       [
40114         "Tanzania",
40115         "tz",
40116         "255"
40117       ],
40118       [
40119         "Thailand (ไทย)",
40120         "th",
40121         "66"
40122       ],
40123       [
40124         "Timor-Leste",
40125         "tl",
40126         "670"
40127       ],
40128       [
40129         "Togo",
40130         "tg",
40131         "228"
40132       ],
40133       [
40134         "Tokelau",
40135         "tk",
40136         "690"
40137       ],
40138       [
40139         "Tonga",
40140         "to",
40141         "676"
40142       ],
40143       [
40144         "Trinidad and Tobago",
40145         "tt",
40146         "1868"
40147       ],
40148       [
40149         "Tunisia (‫تونس‬‎)",
40150         "tn",
40151         "216"
40152       ],
40153       [
40154         "Turkey (Türkiye)",
40155         "tr",
40156         "90"
40157       ],
40158       [
40159         "Turkmenistan",
40160         "tm",
40161         "993"
40162       ],
40163       [
40164         "Turks and Caicos Islands",
40165         "tc",
40166         "1649"
40167       ],
40168       [
40169         "Tuvalu",
40170         "tv",
40171         "688"
40172       ],
40173       [
40174         "U.S. Virgin Islands",
40175         "vi",
40176         "1340"
40177       ],
40178       [
40179         "Uganda",
40180         "ug",
40181         "256"
40182       ],
40183       [
40184         "Ukraine (Україна)",
40185         "ua",
40186         "380"
40187       ],
40188       [
40189         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
40190         "ae",
40191         "971"
40192       ],
40193       [
40194         "United Kingdom",
40195         "gb",
40196         "44",
40197         0
40198       ],
40199       [
40200         "United States",
40201         "us",
40202         "1",
40203         0
40204       ],
40205       [
40206         "Uruguay",
40207         "uy",
40208         "598"
40209       ],
40210       [
40211         "Uzbekistan (Oʻzbekiston)",
40212         "uz",
40213         "998"
40214       ],
40215       [
40216         "Vanuatu",
40217         "vu",
40218         "678"
40219       ],
40220       [
40221         "Vatican City (Città del Vaticano)",
40222         "va",
40223         "39",
40224         1
40225       ],
40226       [
40227         "Venezuela",
40228         "ve",
40229         "58"
40230       ],
40231       [
40232         "Vietnam (Việt Nam)",
40233         "vn",
40234         "84"
40235       ],
40236       [
40237         "Wallis and Futuna (Wallis-et-Futuna)",
40238         "wf",
40239         "681"
40240       ],
40241       [
40242         "Western Sahara (‫الصحراء الغربية‬‎)",
40243         "eh",
40244         "212",
40245         1
40246       ],
40247       [
40248         "Yemen (‫اليمن‬‎)",
40249         "ye",
40250         "967"
40251       ],
40252       [
40253         "Zambia",
40254         "zm",
40255         "260"
40256       ],
40257       [
40258         "Zimbabwe",
40259         "zw",
40260         "263"
40261       ],
40262       [
40263         "Åland Islands",
40264         "ax",
40265         "358",
40266         1
40267       ]
40268   ];
40269   
40270   return d;
40271 }/**
40272 *    This script refer to:
40273 *    Title: International Telephone Input
40274 *    Author: Jack O'Connor
40275 *    Code version:  v12.1.12
40276 *    Availability: https://github.com/jackocnr/intl-tel-input.git
40277 **/
40278
40279 /**
40280  * @class Roo.bootstrap.PhoneInput
40281  * @extends Roo.bootstrap.TriggerField
40282  * An input with International dial-code selection
40283  
40284  * @cfg {String} defaultDialCode default '+852'
40285  * @cfg {Array} preferedCountries default []
40286   
40287  * @constructor
40288  * Create a new PhoneInput.
40289  * @param {Object} config Configuration options
40290  */
40291
40292 Roo.bootstrap.PhoneInput = function(config) {
40293     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
40294 };
40295
40296 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
40297         
40298         listWidth: undefined,
40299         
40300         selectedClass: 'active',
40301         
40302         invalidClass : "has-warning",
40303         
40304         validClass: 'has-success',
40305         
40306         allowed: '0123456789',
40307         
40308         max_length: 15,
40309         
40310         /**
40311          * @cfg {String} defaultDialCode The default dial code when initializing the input
40312          */
40313         defaultDialCode: '+852',
40314         
40315         /**
40316          * @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
40317          */
40318         preferedCountries: false,
40319         
40320         getAutoCreate : function()
40321         {
40322             var data = Roo.bootstrap.PhoneInputData();
40323             var align = this.labelAlign || this.parentLabelAlign();
40324             var id = Roo.id();
40325             
40326             this.allCountries = [];
40327             this.dialCodeMapping = [];
40328             
40329             for (var i = 0; i < data.length; i++) {
40330               var c = data[i];
40331               this.allCountries[i] = {
40332                 name: c[0],
40333                 iso2: c[1],
40334                 dialCode: c[2],
40335                 priority: c[3] || 0,
40336                 areaCodes: c[4] || null
40337               };
40338               this.dialCodeMapping[c[2]] = {
40339                   name: c[0],
40340                   iso2: c[1],
40341                   priority: c[3] || 0,
40342                   areaCodes: c[4] || null
40343               };
40344             }
40345             
40346             var cfg = {
40347                 cls: 'form-group',
40348                 cn: []
40349             };
40350             
40351             var input =  {
40352                 tag: 'input',
40353                 id : id,
40354                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40355                 maxlength: this.max_length,
40356                 cls : 'form-control tel-input',
40357                 autocomplete: 'new-password'
40358             };
40359             
40360             var hiddenInput = {
40361                 tag: 'input',
40362                 type: 'hidden',
40363                 cls: 'hidden-tel-input'
40364             };
40365             
40366             if (this.name) {
40367                 hiddenInput.name = this.name;
40368             }
40369             
40370             if (this.disabled) {
40371                 input.disabled = true;
40372             }
40373             
40374             var flag_container = {
40375                 tag: 'div',
40376                 cls: 'flag-box',
40377                 cn: [
40378                     {
40379                         tag: 'div',
40380                         cls: 'flag'
40381                     },
40382                     {
40383                         tag: 'div',
40384                         cls: 'caret'
40385                     }
40386                 ]
40387             };
40388             
40389             var box = {
40390                 tag: 'div',
40391                 cls: this.hasFeedback ? 'has-feedback' : '',
40392                 cn: [
40393                     hiddenInput,
40394                     input,
40395                     {
40396                         tag: 'input',
40397                         cls: 'dial-code-holder',
40398                         disabled: true
40399                     }
40400                 ]
40401             };
40402             
40403             var container = {
40404                 cls: 'roo-select2-container input-group',
40405                 cn: [
40406                     flag_container,
40407                     box
40408                 ]
40409             };
40410             
40411             if (this.fieldLabel.length) {
40412                 var indicator = {
40413                     tag: 'i',
40414                     tooltip: 'This field is required'
40415                 };
40416                 
40417                 var label = {
40418                     tag: 'label',
40419                     'for':  id,
40420                     cls: 'control-label',
40421                     cn: []
40422                 };
40423                 
40424                 var label_text = {
40425                     tag: 'span',
40426                     html: this.fieldLabel
40427                 };
40428                 
40429                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40430                 label.cn = [
40431                     indicator,
40432                     label_text
40433                 ];
40434                 
40435                 if(this.indicatorpos == 'right') {
40436                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40437                     label.cn = [
40438                         label_text,
40439                         indicator
40440                     ];
40441                 }
40442                 
40443                 if(align == 'left') {
40444                     container = {
40445                         tag: 'div',
40446                         cn: [
40447                             container
40448                         ]
40449                     };
40450                     
40451                     if(this.labelWidth > 12){
40452                         label.style = "width: " + this.labelWidth + 'px';
40453                     }
40454                     if(this.labelWidth < 13 && this.labelmd == 0){
40455                         this.labelmd = this.labelWidth;
40456                     }
40457                     if(this.labellg > 0){
40458                         label.cls += ' col-lg-' + this.labellg;
40459                         input.cls += ' col-lg-' + (12 - this.labellg);
40460                     }
40461                     if(this.labelmd > 0){
40462                         label.cls += ' col-md-' + this.labelmd;
40463                         container.cls += ' col-md-' + (12 - this.labelmd);
40464                     }
40465                     if(this.labelsm > 0){
40466                         label.cls += ' col-sm-' + this.labelsm;
40467                         container.cls += ' col-sm-' + (12 - this.labelsm);
40468                     }
40469                     if(this.labelxs > 0){
40470                         label.cls += ' col-xs-' + this.labelxs;
40471                         container.cls += ' col-xs-' + (12 - this.labelxs);
40472                     }
40473                 }
40474             }
40475             
40476             cfg.cn = [
40477                 label,
40478                 container
40479             ];
40480             
40481             var settings = this;
40482             
40483             ['xs','sm','md','lg'].map(function(size){
40484                 if (settings[size]) {
40485                     cfg.cls += ' col-' + size + '-' + settings[size];
40486                 }
40487             });
40488             
40489             this.store = new Roo.data.Store({
40490                 proxy : new Roo.data.MemoryProxy({}),
40491                 reader : new Roo.data.JsonReader({
40492                     fields : [
40493                         {
40494                             'name' : 'name',
40495                             'type' : 'string'
40496                         },
40497                         {
40498                             'name' : 'iso2',
40499                             'type' : 'string'
40500                         },
40501                         {
40502                             'name' : 'dialCode',
40503                             'type' : 'string'
40504                         },
40505                         {
40506                             'name' : 'priority',
40507                             'type' : 'string'
40508                         },
40509                         {
40510                             'name' : 'areaCodes',
40511                             'type' : 'string'
40512                         }
40513                     ]
40514                 })
40515             });
40516             
40517             if(!this.preferedCountries) {
40518                 this.preferedCountries = [
40519                     'hk',
40520                     'gb',
40521                     'us'
40522                 ];
40523             }
40524             
40525             var p = this.preferedCountries.reverse();
40526             
40527             if(p) {
40528                 for (var i = 0; i < p.length; i++) {
40529                     for (var j = 0; j < this.allCountries.length; j++) {
40530                         if(this.allCountries[j].iso2 == p[i]) {
40531                             var t = this.allCountries[j];
40532                             this.allCountries.splice(j,1);
40533                             this.allCountries.unshift(t);
40534                         }
40535                     } 
40536                 }
40537             }
40538             
40539             this.store.proxy.data = {
40540                 success: true,
40541                 data: this.allCountries
40542             };
40543             
40544             return cfg;
40545         },
40546         
40547         initEvents : function()
40548         {
40549             this.createList();
40550             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40551             
40552             this.indicator = this.indicatorEl();
40553             this.flag = this.flagEl();
40554             this.dialCodeHolder = this.dialCodeHolderEl();
40555             
40556             this.trigger = this.el.select('div.flag-box',true).first();
40557             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40558             
40559             var _this = this;
40560             
40561             (function(){
40562                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40563                 _this.list.setWidth(lw);
40564             }).defer(100);
40565             
40566             this.list.on('mouseover', this.onViewOver, this);
40567             this.list.on('mousemove', this.onViewMove, this);
40568             this.inputEl().on("keyup", this.onKeyUp, this);
40569             this.inputEl().on("keypress", this.onKeyPress, this);
40570             
40571             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40572
40573             this.view = new Roo.View(this.list, this.tpl, {
40574                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40575             });
40576             
40577             this.view.on('click', this.onViewClick, this);
40578             this.setValue(this.defaultDialCode);
40579         },
40580         
40581         onTriggerClick : function(e)
40582         {
40583             Roo.log('trigger click');
40584             if(this.disabled){
40585                 return;
40586             }
40587             
40588             if(this.isExpanded()){
40589                 this.collapse();
40590                 this.hasFocus = false;
40591             }else {
40592                 this.store.load({});
40593                 this.hasFocus = true;
40594                 this.expand();
40595             }
40596         },
40597         
40598         isExpanded : function()
40599         {
40600             return this.list.isVisible();
40601         },
40602         
40603         collapse : function()
40604         {
40605             if(!this.isExpanded()){
40606                 return;
40607             }
40608             this.list.hide();
40609             Roo.get(document).un('mousedown', this.collapseIf, this);
40610             Roo.get(document).un('mousewheel', this.collapseIf, this);
40611             this.fireEvent('collapse', this);
40612             this.validate();
40613         },
40614         
40615         expand : function()
40616         {
40617             Roo.log('expand');
40618
40619             if(this.isExpanded() || !this.hasFocus){
40620                 return;
40621             }
40622             
40623             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40624             this.list.setWidth(lw);
40625             
40626             this.list.show();
40627             this.restrictHeight();
40628             
40629             Roo.get(document).on('mousedown', this.collapseIf, this);
40630             Roo.get(document).on('mousewheel', this.collapseIf, this);
40631             
40632             this.fireEvent('expand', this);
40633         },
40634         
40635         restrictHeight : function()
40636         {
40637             this.list.alignTo(this.inputEl(), this.listAlign);
40638             this.list.alignTo(this.inputEl(), this.listAlign);
40639         },
40640         
40641         onViewOver : function(e, t)
40642         {
40643             if(this.inKeyMode){
40644                 return;
40645             }
40646             var item = this.view.findItemFromChild(t);
40647             
40648             if(item){
40649                 var index = this.view.indexOf(item);
40650                 this.select(index, false);
40651             }
40652         },
40653
40654         // private
40655         onViewClick : function(view, doFocus, el, e)
40656         {
40657             var index = this.view.getSelectedIndexes()[0];
40658             
40659             var r = this.store.getAt(index);
40660             
40661             if(r){
40662                 this.onSelect(r, index);
40663             }
40664             if(doFocus !== false && !this.blockFocus){
40665                 this.inputEl().focus();
40666             }
40667         },
40668         
40669         onViewMove : function(e, t)
40670         {
40671             this.inKeyMode = false;
40672         },
40673         
40674         select : function(index, scrollIntoView)
40675         {
40676             this.selectedIndex = index;
40677             this.view.select(index);
40678             if(scrollIntoView !== false){
40679                 var el = this.view.getNode(index);
40680                 if(el){
40681                     this.list.scrollChildIntoView(el, false);
40682                 }
40683             }
40684         },
40685         
40686         createList : function()
40687         {
40688             this.list = Roo.get(document.body).createChild({
40689                 tag: 'ul',
40690                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40691                 style: 'display:none'
40692             });
40693             
40694             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40695         },
40696         
40697         collapseIf : function(e)
40698         {
40699             var in_combo  = e.within(this.el);
40700             var in_list =  e.within(this.list);
40701             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40702             
40703             if (in_combo || in_list || is_list) {
40704                 return;
40705             }
40706             this.collapse();
40707         },
40708         
40709         onSelect : function(record, index)
40710         {
40711             if(this.fireEvent('beforeselect', this, record, index) !== false){
40712                 
40713                 this.setFlagClass(record.data.iso2);
40714                 this.setDialCode(record.data.dialCode);
40715                 this.hasFocus = false;
40716                 this.collapse();
40717                 this.fireEvent('select', this, record, index);
40718             }
40719         },
40720         
40721         flagEl : function()
40722         {
40723             var flag = this.el.select('div.flag',true).first();
40724             if(!flag){
40725                 return false;
40726             }
40727             return flag;
40728         },
40729         
40730         dialCodeHolderEl : function()
40731         {
40732             var d = this.el.select('input.dial-code-holder',true).first();
40733             if(!d){
40734                 return false;
40735             }
40736             return d;
40737         },
40738         
40739         setDialCode : function(v)
40740         {
40741             this.dialCodeHolder.dom.value = '+'+v;
40742         },
40743         
40744         setFlagClass : function(n)
40745         {
40746             this.flag.dom.className = 'flag '+n;
40747         },
40748         
40749         getValue : function()
40750         {
40751             var v = this.inputEl().getValue();
40752             if(this.dialCodeHolder) {
40753                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40754             }
40755             return v;
40756         },
40757         
40758         setValue : function(v)
40759         {
40760             var d = this.getDialCode(v);
40761             
40762             //invalid dial code
40763             if(v.length == 0 || !d || d.length == 0) {
40764                 if(this.rendered){
40765                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40766                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40767                 }
40768                 return;
40769             }
40770             
40771             //valid dial code
40772             this.setFlagClass(this.dialCodeMapping[d].iso2);
40773             this.setDialCode(d);
40774             this.inputEl().dom.value = v.replace('+'+d,'');
40775             this.hiddenEl().dom.value = this.getValue();
40776             
40777             this.validate();
40778         },
40779         
40780         getDialCode : function(v)
40781         {
40782             v = v ||  '';
40783             
40784             if (v.length == 0) {
40785                 return this.dialCodeHolder.dom.value;
40786             }
40787             
40788             var dialCode = "";
40789             if (v.charAt(0) != "+") {
40790                 return false;
40791             }
40792             var numericChars = "";
40793             for (var i = 1; i < v.length; i++) {
40794               var c = v.charAt(i);
40795               if (!isNaN(c)) {
40796                 numericChars += c;
40797                 if (this.dialCodeMapping[numericChars]) {
40798                   dialCode = v.substr(1, i);
40799                 }
40800                 if (numericChars.length == 4) {
40801                   break;
40802                 }
40803               }
40804             }
40805             return dialCode;
40806         },
40807         
40808         reset : function()
40809         {
40810             this.setValue(this.defaultDialCode);
40811             this.validate();
40812         },
40813         
40814         hiddenEl : function()
40815         {
40816             return this.el.select('input.hidden-tel-input',true).first();
40817         },
40818         
40819         // after setting val
40820         onKeyUp : function(e){
40821             this.setValue(this.getValue());
40822         },
40823         
40824         onKeyPress : function(e){
40825             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40826                 e.stopEvent();
40827             }
40828         }
40829         
40830 });
40831 /**
40832  * @class Roo.bootstrap.MoneyField
40833  * @extends Roo.bootstrap.ComboBox
40834  * Bootstrap MoneyField class
40835  * 
40836  * @constructor
40837  * Create a new MoneyField.
40838  * @param {Object} config Configuration options
40839  */
40840
40841 Roo.bootstrap.MoneyField = function(config) {
40842     
40843     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40844     
40845 };
40846
40847 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40848     
40849     /**
40850      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40851      */
40852     allowDecimals : true,
40853     /**
40854      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40855      */
40856     decimalSeparator : ".",
40857     /**
40858      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40859      */
40860     decimalPrecision : 0,
40861     /**
40862      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40863      */
40864     allowNegative : true,
40865     /**
40866      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40867      */
40868     allowZero: true,
40869     /**
40870      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40871      */
40872     minValue : Number.NEGATIVE_INFINITY,
40873     /**
40874      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40875      */
40876     maxValue : Number.MAX_VALUE,
40877     /**
40878      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40879      */
40880     minText : "The minimum value for this field is {0}",
40881     /**
40882      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40883      */
40884     maxText : "The maximum value for this field is {0}",
40885     /**
40886      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40887      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40888      */
40889     nanText : "{0} is not a valid number",
40890     /**
40891      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40892      */
40893     castInt : true,
40894     /**
40895      * @cfg {String} defaults currency of the MoneyField
40896      * value should be in lkey
40897      */
40898     defaultCurrency : false,
40899     /**
40900      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40901      */
40902     thousandsDelimiter : false,
40903     /**
40904      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40905      */
40906     max_length: false,
40907     
40908     inputlg : 9,
40909     inputmd : 9,
40910     inputsm : 9,
40911     inputxs : 6,
40912     
40913     store : false,
40914     
40915     getAutoCreate : function()
40916     {
40917         var align = this.labelAlign || this.parentLabelAlign();
40918         
40919         var id = Roo.id();
40920
40921         var cfg = {
40922             cls: 'form-group',
40923             cn: []
40924         };
40925
40926         var input =  {
40927             tag: 'input',
40928             id : id,
40929             cls : 'form-control roo-money-amount-input',
40930             autocomplete: 'new-password'
40931         };
40932         
40933         var hiddenInput = {
40934             tag: 'input',
40935             type: 'hidden',
40936             id: Roo.id(),
40937             cls: 'hidden-number-input'
40938         };
40939         
40940         if(this.max_length) {
40941             input.maxlength = this.max_length; 
40942         }
40943         
40944         if (this.name) {
40945             hiddenInput.name = this.name;
40946         }
40947
40948         if (this.disabled) {
40949             input.disabled = true;
40950         }
40951
40952         var clg = 12 - this.inputlg;
40953         var cmd = 12 - this.inputmd;
40954         var csm = 12 - this.inputsm;
40955         var cxs = 12 - this.inputxs;
40956         
40957         var container = {
40958             tag : 'div',
40959             cls : 'row roo-money-field',
40960             cn : [
40961                 {
40962                     tag : 'div',
40963                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40964                     cn : [
40965                         {
40966                             tag : 'div',
40967                             cls: 'roo-select2-container input-group',
40968                             cn: [
40969                                 {
40970                                     tag : 'input',
40971                                     cls : 'form-control roo-money-currency-input',
40972                                     autocomplete: 'new-password',
40973                                     readOnly : 1,
40974                                     name : this.currencyName
40975                                 },
40976                                 {
40977                                     tag :'span',
40978                                     cls : 'input-group-addon',
40979                                     cn : [
40980                                         {
40981                                             tag: 'span',
40982                                             cls: 'caret'
40983                                         }
40984                                     ]
40985                                 }
40986                             ]
40987                         }
40988                     ]
40989                 },
40990                 {
40991                     tag : 'div',
40992                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40993                     cn : [
40994                         {
40995                             tag: 'div',
40996                             cls: this.hasFeedback ? 'has-feedback' : '',
40997                             cn: [
40998                                 input
40999                             ]
41000                         }
41001                     ]
41002                 }
41003             ]
41004             
41005         };
41006         
41007         if (this.fieldLabel.length) {
41008             var indicator = {
41009                 tag: 'i',
41010                 tooltip: 'This field is required'
41011             };
41012
41013             var label = {
41014                 tag: 'label',
41015                 'for':  id,
41016                 cls: 'control-label',
41017                 cn: []
41018             };
41019
41020             var label_text = {
41021                 tag: 'span',
41022                 html: this.fieldLabel
41023             };
41024
41025             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
41026             label.cn = [
41027                 indicator,
41028                 label_text
41029             ];
41030
41031             if(this.indicatorpos == 'right') {
41032                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
41033                 label.cn = [
41034                     label_text,
41035                     indicator
41036                 ];
41037             }
41038
41039             if(align == 'left') {
41040                 container = {
41041                     tag: 'div',
41042                     cn: [
41043                         container
41044                     ]
41045                 };
41046
41047                 if(this.labelWidth > 12){
41048                     label.style = "width: " + this.labelWidth + 'px';
41049                 }
41050                 if(this.labelWidth < 13 && this.labelmd == 0){
41051                     this.labelmd = this.labelWidth;
41052                 }
41053                 if(this.labellg > 0){
41054                     label.cls += ' col-lg-' + this.labellg;
41055                     input.cls += ' col-lg-' + (12 - this.labellg);
41056                 }
41057                 if(this.labelmd > 0){
41058                     label.cls += ' col-md-' + this.labelmd;
41059                     container.cls += ' col-md-' + (12 - this.labelmd);
41060                 }
41061                 if(this.labelsm > 0){
41062                     label.cls += ' col-sm-' + this.labelsm;
41063                     container.cls += ' col-sm-' + (12 - this.labelsm);
41064                 }
41065                 if(this.labelxs > 0){
41066                     label.cls += ' col-xs-' + this.labelxs;
41067                     container.cls += ' col-xs-' + (12 - this.labelxs);
41068                 }
41069             }
41070         }
41071
41072         cfg.cn = [
41073             label,
41074             container,
41075             hiddenInput
41076         ];
41077         
41078         var settings = this;
41079
41080         ['xs','sm','md','lg'].map(function(size){
41081             if (settings[size]) {
41082                 cfg.cls += ' col-' + size + '-' + settings[size];
41083             }
41084         });
41085         
41086         return cfg;
41087     },
41088     
41089     initEvents : function()
41090     {
41091         this.indicator = this.indicatorEl();
41092         
41093         this.initCurrencyEvent();
41094         
41095         this.initNumberEvent();
41096     },
41097     
41098     initCurrencyEvent : function()
41099     {
41100         if (!this.store) {
41101             throw "can not find store for combo";
41102         }
41103         
41104         this.store = Roo.factory(this.store, Roo.data);
41105         this.store.parent = this;
41106         
41107         this.createList();
41108         
41109         this.triggerEl = this.el.select('.input-group-addon', true).first();
41110         
41111         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
41112         
41113         var _this = this;
41114         
41115         (function(){
41116             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
41117             _this.list.setWidth(lw);
41118         }).defer(100);
41119         
41120         this.list.on('mouseover', this.onViewOver, this);
41121         this.list.on('mousemove', this.onViewMove, this);
41122         this.list.on('scroll', this.onViewScroll, this);
41123         
41124         if(!this.tpl){
41125             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
41126         }
41127         
41128         this.view = new Roo.View(this.list, this.tpl, {
41129             singleSelect:true, store: this.store, selectedClass: this.selectedClass
41130         });
41131         
41132         this.view.on('click', this.onViewClick, this);
41133         
41134         this.store.on('beforeload', this.onBeforeLoad, this);
41135         this.store.on('load', this.onLoad, this);
41136         this.store.on('loadexception', this.onLoadException, this);
41137         
41138         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
41139             "up" : function(e){
41140                 this.inKeyMode = true;
41141                 this.selectPrev();
41142             },
41143
41144             "down" : function(e){
41145                 if(!this.isExpanded()){
41146                     this.onTriggerClick();
41147                 }else{
41148                     this.inKeyMode = true;
41149                     this.selectNext();
41150                 }
41151             },
41152
41153             "enter" : function(e){
41154                 this.collapse();
41155                 
41156                 if(this.fireEvent("specialkey", this, e)){
41157                     this.onViewClick(false);
41158                 }
41159                 
41160                 return true;
41161             },
41162
41163             "esc" : function(e){
41164                 this.collapse();
41165             },
41166
41167             "tab" : function(e){
41168                 this.collapse();
41169                 
41170                 if(this.fireEvent("specialkey", this, e)){
41171                     this.onViewClick(false);
41172                 }
41173                 
41174                 return true;
41175             },
41176
41177             scope : this,
41178
41179             doRelay : function(foo, bar, hname){
41180                 if(hname == 'down' || this.scope.isExpanded()){
41181                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41182                 }
41183                 return true;
41184             },
41185
41186             forceKeyDown: true
41187         });
41188         
41189         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
41190         
41191     },
41192     
41193     initNumberEvent : function(e)
41194     {
41195         this.inputEl().on("keydown" , this.fireKey,  this);
41196         this.inputEl().on("focus", this.onFocus,  this);
41197         this.inputEl().on("blur", this.onBlur,  this);
41198         
41199         this.inputEl().relayEvent('keyup', this);
41200         
41201         if(this.indicator){
41202             this.indicator.addClass('invisible');
41203         }
41204  
41205         this.originalValue = this.getValue();
41206         
41207         if(this.validationEvent == 'keyup'){
41208             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
41209             this.inputEl().on('keyup', this.filterValidation, this);
41210         }
41211         else if(this.validationEvent !== false){
41212             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
41213         }
41214         
41215         if(this.selectOnFocus){
41216             this.on("focus", this.preFocus, this);
41217             
41218         }
41219         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41220             this.inputEl().on("keypress", this.filterKeys, this);
41221         } else {
41222             this.inputEl().relayEvent('keypress', this);
41223         }
41224         
41225         var allowed = "0123456789";
41226         
41227         if(this.allowDecimals){
41228             allowed += this.decimalSeparator;
41229         }
41230         
41231         if(this.allowNegative){
41232             allowed += "-";
41233         }
41234         
41235         if(this.thousandsDelimiter) {
41236             allowed += ",";
41237         }
41238         
41239         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41240         
41241         var keyPress = function(e){
41242             
41243             var k = e.getKey();
41244             
41245             var c = e.getCharCode();
41246             
41247             if(
41248                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41249                     allowed.indexOf(String.fromCharCode(c)) === -1
41250             ){
41251                 e.stopEvent();
41252                 return;
41253             }
41254             
41255             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41256                 return;
41257             }
41258             
41259             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41260                 e.stopEvent();
41261             }
41262         };
41263         
41264         this.inputEl().on("keypress", keyPress, this);
41265         
41266     },
41267     
41268     onTriggerClick : function(e)
41269     {   
41270         if(this.disabled){
41271             return;
41272         }
41273         
41274         this.page = 0;
41275         this.loadNext = false;
41276         
41277         if(this.isExpanded()){
41278             this.collapse();
41279             return;
41280         }
41281         
41282         this.hasFocus = true;
41283         
41284         if(this.triggerAction == 'all') {
41285             this.doQuery(this.allQuery, true);
41286             return;
41287         }
41288         
41289         this.doQuery(this.getRawValue());
41290     },
41291     
41292     getCurrency : function()
41293     {   
41294         var v = this.currencyEl().getValue();
41295         
41296         return v;
41297     },
41298     
41299     restrictHeight : function()
41300     {
41301         this.list.alignTo(this.currencyEl(), this.listAlign);
41302         this.list.alignTo(this.currencyEl(), this.listAlign);
41303     },
41304     
41305     onViewClick : function(view, doFocus, el, e)
41306     {
41307         var index = this.view.getSelectedIndexes()[0];
41308         
41309         var r = this.store.getAt(index);
41310         
41311         if(r){
41312             this.onSelect(r, index);
41313         }
41314     },
41315     
41316     onSelect : function(record, index){
41317         
41318         if(this.fireEvent('beforeselect', this, record, index) !== false){
41319         
41320             this.setFromCurrencyData(index > -1 ? record.data : false);
41321             
41322             this.collapse();
41323             
41324             this.fireEvent('select', this, record, index);
41325         }
41326     },
41327     
41328     setFromCurrencyData : function(o)
41329     {
41330         var currency = '';
41331         
41332         this.lastCurrency = o;
41333         
41334         if (this.currencyField) {
41335             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41336         } else {
41337             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
41338         }
41339         
41340         this.lastSelectionText = currency;
41341         
41342         //setting default currency
41343         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41344             this.setCurrency(this.defaultCurrency);
41345             return;
41346         }
41347         
41348         this.setCurrency(currency);
41349     },
41350     
41351     setFromData : function(o)
41352     {
41353         var c = {};
41354         
41355         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41356         
41357         this.setFromCurrencyData(c);
41358         
41359         var value = '';
41360         
41361         if (this.name) {
41362             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41363         } else {
41364             Roo.log('no value set for '+ (this.name ? this.name : this.id));
41365         }
41366         
41367         this.setValue(value);
41368         
41369     },
41370     
41371     setCurrency : function(v)
41372     {   
41373         this.currencyValue = v;
41374         
41375         if(this.rendered){
41376             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41377             this.validate();
41378         }
41379     },
41380     
41381     setValue : function(v)
41382     {
41383         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41384         
41385         this.value = v;
41386         
41387         if(this.rendered){
41388             
41389             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41390             
41391             this.inputEl().dom.value = (v == '') ? '' :
41392                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41393             
41394             if(!this.allowZero && v === '0') {
41395                 this.hiddenEl().dom.value = '';
41396                 this.inputEl().dom.value = '';
41397             }
41398             
41399             this.validate();
41400         }
41401     },
41402     
41403     getRawValue : function()
41404     {
41405         var v = this.inputEl().getValue();
41406         
41407         return v;
41408     },
41409     
41410     getValue : function()
41411     {
41412         return this.fixPrecision(this.parseValue(this.getRawValue()));
41413     },
41414     
41415     parseValue : function(value)
41416     {
41417         if(this.thousandsDelimiter) {
41418             value += "";
41419             r = new RegExp(",", "g");
41420             value = value.replace(r, "");
41421         }
41422         
41423         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41424         return isNaN(value) ? '' : value;
41425         
41426     },
41427     
41428     fixPrecision : function(value)
41429     {
41430         if(this.thousandsDelimiter) {
41431             value += "";
41432             r = new RegExp(",", "g");
41433             value = value.replace(r, "");
41434         }
41435         
41436         var nan = isNaN(value);
41437         
41438         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41439             return nan ? '' : value;
41440         }
41441         return parseFloat(value).toFixed(this.decimalPrecision);
41442     },
41443     
41444     decimalPrecisionFcn : function(v)
41445     {
41446         return Math.floor(v);
41447     },
41448     
41449     validateValue : function(value)
41450     {
41451         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41452             return false;
41453         }
41454         
41455         var num = this.parseValue(value);
41456         
41457         if(isNaN(num)){
41458             this.markInvalid(String.format(this.nanText, value));
41459             return false;
41460         }
41461         
41462         if(num < this.minValue){
41463             this.markInvalid(String.format(this.minText, this.minValue));
41464             return false;
41465         }
41466         
41467         if(num > this.maxValue){
41468             this.markInvalid(String.format(this.maxText, this.maxValue));
41469             return false;
41470         }
41471         
41472         return true;
41473     },
41474     
41475     validate : function()
41476     {
41477         if(this.disabled || this.allowBlank){
41478             this.markValid();
41479             return true;
41480         }
41481         
41482         var currency = this.getCurrency();
41483         
41484         if(this.validateValue(this.getRawValue()) && currency.length){
41485             this.markValid();
41486             return true;
41487         }
41488         
41489         this.markInvalid();
41490         return false;
41491     },
41492     
41493     getName: function()
41494     {
41495         return this.name;
41496     },
41497     
41498     beforeBlur : function()
41499     {
41500         if(!this.castInt){
41501             return;
41502         }
41503         
41504         var v = this.parseValue(this.getRawValue());
41505         
41506         if(v || v == 0){
41507             this.setValue(v);
41508         }
41509     },
41510     
41511     onBlur : function()
41512     {
41513         this.beforeBlur();
41514         
41515         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41516             //this.el.removeClass(this.focusClass);
41517         }
41518         
41519         this.hasFocus = false;
41520         
41521         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41522             this.validate();
41523         }
41524         
41525         var v = this.getValue();
41526         
41527         if(String(v) !== String(this.startValue)){
41528             this.fireEvent('change', this, v, this.startValue);
41529         }
41530         
41531         this.fireEvent("blur", this);
41532     },
41533     
41534     inputEl : function()
41535     {
41536         return this.el.select('.roo-money-amount-input', true).first();
41537     },
41538     
41539     currencyEl : function()
41540     {
41541         return this.el.select('.roo-money-currency-input', true).first();
41542     },
41543     
41544     hiddenEl : function()
41545     {
41546         return this.el.select('input.hidden-number-input',true).first();
41547     }
41548     
41549 });/**
41550  * @class Roo.bootstrap.BezierSignature
41551  * @extends Roo.bootstrap.Component
41552  * Bootstrap BezierSignature class
41553  * This script refer to:
41554  *    Title: Signature Pad
41555  *    Author: szimek
41556  *    Availability: https://github.com/szimek/signature_pad
41557  *
41558  * @constructor
41559  * Create a new BezierSignature
41560  * @param {Object} config The config object
41561  */
41562
41563 Roo.bootstrap.BezierSignature = function(config){
41564     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
41565     this.addEvents({
41566         "resize" : true
41567     });
41568 };
41569
41570 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
41571 {
41572      
41573     curve_data: [],
41574     
41575     is_empty: true,
41576     
41577     mouse_btn_down: true,
41578     
41579     /**
41580      * @cfg {int} canvas height
41581      */
41582     canvas_height: '200px',
41583     
41584     /**
41585      * @cfg {float|function} Radius of a single dot.
41586      */ 
41587     dot_size: false,
41588     
41589     /**
41590      * @cfg {float} Minimum width of a line. Defaults to 0.5.
41591      */
41592     min_width: 0.5,
41593     
41594     /**
41595      * @cfg {float} Maximum width of a line. Defaults to 2.5.
41596      */
41597     max_width: 2.5,
41598     
41599     /**
41600      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
41601      */
41602     throttle: 16,
41603     
41604     /**
41605      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
41606      */
41607     min_distance: 5,
41608     
41609     /**
41610      * @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.
41611      */
41612     bg_color: 'rgba(0, 0, 0, 0)',
41613     
41614     /**
41615      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
41616      */
41617     dot_color: 'black',
41618     
41619     /**
41620      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
41621      */ 
41622     velocity_filter_weight: 0.7,
41623     
41624     /**
41625      * @cfg {function} Callback when stroke begin. 
41626      */
41627     onBegin: false,
41628     
41629     /**
41630      * @cfg {function} Callback when stroke end.
41631      */
41632     onEnd: false,
41633     
41634     getAutoCreate : function()
41635     {
41636         var cls = 'roo-signature column';
41637         
41638         if(this.cls){
41639             cls += ' ' + this.cls;
41640         }
41641         
41642         var col_sizes = [
41643             'lg',
41644             'md',
41645             'sm',
41646             'xs'
41647         ];
41648         
41649         for(var i = 0; i < col_sizes.length; i++) {
41650             if(this[col_sizes[i]]) {
41651                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
41652             }
41653         }
41654         
41655         var cfg = {
41656             tag: 'div',
41657             cls: cls,
41658             cn: [
41659                 {
41660                     tag: 'div',
41661                     cls: 'roo-signature-body',
41662                     cn: [
41663                         {
41664                             tag: 'canvas',
41665                             cls: 'roo-signature-body-canvas',
41666                             height: this.canvas_height,
41667                             width: this.canvas_width
41668                         }
41669                     ]
41670                 },
41671                 {
41672                     tag: 'input',
41673                     type: 'file',
41674                     style: 'display: none'
41675                 }
41676             ]
41677         };
41678         
41679         return cfg;
41680     },
41681     
41682     initEvents: function() 
41683     {
41684         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
41685         
41686         var canvas = this.canvasEl();
41687         
41688         // mouse && touch event swapping...
41689         canvas.dom.style.touchAction = 'none';
41690         canvas.dom.style.msTouchAction = 'none';
41691         
41692         this.mouse_btn_down = false;
41693         canvas.on('mousedown', this._handleMouseDown, this);
41694         canvas.on('mousemove', this._handleMouseMove, this);
41695         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
41696         
41697         if (window.PointerEvent) {
41698             canvas.on('pointerdown', this._handleMouseDown, this);
41699             canvas.on('pointermove', this._handleMouseMove, this);
41700             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
41701         }
41702         
41703         if ('ontouchstart' in window) {
41704             canvas.on('touchstart', this._handleTouchStart, this);
41705             canvas.on('touchmove', this._handleTouchMove, this);
41706             canvas.on('touchend', this._handleTouchEnd, this);
41707         }
41708         
41709         Roo.EventManager.onWindowResize(this.resize, this, true);
41710         
41711         // file input event
41712         this.fileEl().on('change', this.uploadImage, this);
41713         
41714         this.clear();
41715         
41716         this.resize();
41717     },
41718     
41719     resize: function(){
41720         
41721         var canvas = this.canvasEl().dom;
41722         var ctx = this.canvasElCtx();
41723         var img_data = false;
41724         
41725         if(canvas.width > 0) {
41726             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
41727         }
41728         // setting canvas width will clean img data
41729         canvas.width = 0;
41730         
41731         var style = window.getComputedStyle ? 
41732             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
41733             
41734         var padding_left = parseInt(style.paddingLeft) || 0;
41735         var padding_right = parseInt(style.paddingRight) || 0;
41736         
41737         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
41738         
41739         if(img_data) {
41740             ctx.putImageData(img_data, 0, 0);
41741         }
41742     },
41743     
41744     _handleMouseDown: function(e)
41745     {
41746         if (e.browserEvent.which === 1) {
41747             this.mouse_btn_down = true;
41748             this.strokeBegin(e);
41749         }
41750     },
41751     
41752     _handleMouseMove: function (e)
41753     {
41754         if (this.mouse_btn_down) {
41755             this.strokeMoveUpdate(e);
41756         }
41757     },
41758     
41759     _handleMouseUp: function (e)
41760     {
41761         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
41762             this.mouse_btn_down = false;
41763             this.strokeEnd(e);
41764         }
41765     },
41766     
41767     _handleTouchStart: function (e) {
41768         
41769         e.preventDefault();
41770         if (e.browserEvent.targetTouches.length === 1) {
41771             // var touch = e.browserEvent.changedTouches[0];
41772             // this.strokeBegin(touch);
41773             
41774              this.strokeBegin(e); // assume e catching the correct xy...
41775         }
41776     },
41777     
41778     _handleTouchMove: function (e) {
41779         e.preventDefault();
41780         // var touch = event.targetTouches[0];
41781         // _this._strokeMoveUpdate(touch);
41782         this.strokeMoveUpdate(e);
41783     },
41784     
41785     _handleTouchEnd: function (e) {
41786         var wasCanvasTouched = e.target === this.canvasEl().dom;
41787         if (wasCanvasTouched) {
41788             e.preventDefault();
41789             // var touch = event.changedTouches[0];
41790             // _this._strokeEnd(touch);
41791             this.strokeEnd(e);
41792         }
41793     },
41794     
41795     reset: function () {
41796         this._lastPoints = [];
41797         this._lastVelocity = 0;
41798         this._lastWidth = (this.min_width + this.max_width) / 2;
41799         this.canvasElCtx().fillStyle = this.dot_color;
41800     },
41801     
41802     strokeMoveUpdate: function(e)
41803     {
41804         this.strokeUpdate(e);
41805         
41806         if (this.throttle) {
41807             this.throttleStroke(this.strokeUpdate, this.throttle);
41808         }
41809         else {
41810             this.strokeUpdate(e);
41811         }
41812     },
41813     
41814     strokeBegin: function(e)
41815     {
41816         var newPointGroup = {
41817             color: this.dot_color,
41818             points: []
41819         };
41820         
41821         if (typeof this.onBegin === 'function') {
41822             this.onBegin(e);
41823         }
41824         
41825         this.curve_data.push(newPointGroup);
41826         this.reset();
41827         this.strokeUpdate(e);
41828     },
41829     
41830     strokeUpdate: function(e)
41831     {
41832         var rect = this.canvasEl().dom.getBoundingClientRect();
41833         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
41834         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
41835         var lastPoints = lastPointGroup.points;
41836         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
41837         var isLastPointTooClose = lastPoint
41838             ? point.distanceTo(lastPoint) <= this.min_distance
41839             : false;
41840         var color = lastPointGroup.color;
41841         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
41842             var curve = this.addPoint(point);
41843             if (!lastPoint) {
41844                 this.drawDot({color: color, point: point});
41845             }
41846             else if (curve) {
41847                 this.drawCurve({color: color, curve: curve});
41848             }
41849             lastPoints.push({
41850                 time: point.time,
41851                 x: point.x,
41852                 y: point.y
41853             });
41854         }
41855     },
41856     
41857     strokeEnd: function(e)
41858     {
41859         this.strokeUpdate(e);
41860         if (typeof this.onEnd === 'function') {
41861             this.onEnd(e);
41862         }
41863     },
41864     
41865     addPoint:  function (point) {
41866         var _lastPoints = this._lastPoints;
41867         _lastPoints.push(point);
41868         if (_lastPoints.length > 2) {
41869             if (_lastPoints.length === 3) {
41870                 _lastPoints.unshift(_lastPoints[0]);
41871             }
41872             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
41873             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
41874             _lastPoints.shift();
41875             return curve;
41876         }
41877         return null;
41878     },
41879     
41880     calculateCurveWidths: function (startPoint, endPoint) {
41881         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
41882             (1 - this.velocity_filter_weight) * this._lastVelocity;
41883
41884         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
41885         var widths = {
41886             end: newWidth,
41887             start: this._lastWidth
41888         };
41889         
41890         this._lastVelocity = velocity;
41891         this._lastWidth = newWidth;
41892         return widths;
41893     },
41894     
41895     drawDot: function (_a) {
41896         var color = _a.color, point = _a.point;
41897         var ctx = this.canvasElCtx();
41898         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
41899         ctx.beginPath();
41900         this.drawCurveSegment(point.x, point.y, width);
41901         ctx.closePath();
41902         ctx.fillStyle = color;
41903         ctx.fill();
41904     },
41905     
41906     drawCurve: function (_a) {
41907         var color = _a.color, curve = _a.curve;
41908         var ctx = this.canvasElCtx();
41909         var widthDelta = curve.endWidth - curve.startWidth;
41910         var drawSteps = Math.floor(curve.length()) * 2;
41911         ctx.beginPath();
41912         ctx.fillStyle = color;
41913         for (var i = 0; i < drawSteps; i += 1) {
41914         var t = i / drawSteps;
41915         var tt = t * t;
41916         var ttt = tt * t;
41917         var u = 1 - t;
41918         var uu = u * u;
41919         var uuu = uu * u;
41920         var x = uuu * curve.startPoint.x;
41921         x += 3 * uu * t * curve.control1.x;
41922         x += 3 * u * tt * curve.control2.x;
41923         x += ttt * curve.endPoint.x;
41924         var y = uuu * curve.startPoint.y;
41925         y += 3 * uu * t * curve.control1.y;
41926         y += 3 * u * tt * curve.control2.y;
41927         y += ttt * curve.endPoint.y;
41928         var width = curve.startWidth + ttt * widthDelta;
41929         this.drawCurveSegment(x, y, width);
41930         }
41931         ctx.closePath();
41932         ctx.fill();
41933     },
41934     
41935     drawCurveSegment: function (x, y, width) {
41936         var ctx = this.canvasElCtx();
41937         ctx.moveTo(x, y);
41938         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
41939         this.is_empty = false;
41940     },
41941     
41942     clear: function()
41943     {
41944         var ctx = this.canvasElCtx();
41945         var canvas = this.canvasEl().dom;
41946         ctx.fillStyle = this.bg_color;
41947         ctx.clearRect(0, 0, canvas.width, canvas.height);
41948         ctx.fillRect(0, 0, canvas.width, canvas.height);
41949         this.curve_data = [];
41950         this.reset();
41951         this.is_empty = true;
41952     },
41953     
41954     fileEl: function()
41955     {
41956         return  this.el.select('input',true).first();
41957     },
41958     
41959     canvasEl: function()
41960     {
41961         return this.el.select('canvas',true).first();
41962     },
41963     
41964     canvasElCtx: function()
41965     {
41966         return this.el.select('canvas',true).first().dom.getContext('2d');
41967     },
41968     
41969     getImage: function(type)
41970     {
41971         if(this.is_empty) {
41972             return false;
41973         }
41974         
41975         // encryption ?
41976         return this.canvasEl().dom.toDataURL('image/'+type, 1);
41977     },
41978     
41979     drawFromImage: function(img_src)
41980     {
41981         var img = new Image();
41982         
41983         img.onload = function(){
41984             this.canvasElCtx().drawImage(img, 0, 0);
41985         }.bind(this);
41986         
41987         img.src = img_src;
41988         
41989         this.is_empty = false;
41990     },
41991     
41992     selectImage: function()
41993     {
41994         this.fileEl().dom.click();
41995     },
41996     
41997     uploadImage: function(e)
41998     {
41999         var reader = new FileReader();
42000         
42001         reader.onload = function(e){
42002             var img = new Image();
42003             img.onload = function(){
42004                 this.reset();
42005                 this.canvasElCtx().drawImage(img, 0, 0);
42006             }.bind(this);
42007             img.src = e.target.result;
42008         }.bind(this);
42009         
42010         reader.readAsDataURL(e.target.files[0]);
42011     },
42012     
42013     // Bezier Point Constructor
42014     Point: (function () {
42015         function Point(x, y, time) {
42016             this.x = x;
42017             this.y = y;
42018             this.time = time || Date.now();
42019         }
42020         Point.prototype.distanceTo = function (start) {
42021             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
42022         };
42023         Point.prototype.equals = function (other) {
42024             return this.x === other.x && this.y === other.y && this.time === other.time;
42025         };
42026         Point.prototype.velocityFrom = function (start) {
42027             return this.time !== start.time
42028             ? this.distanceTo(start) / (this.time - start.time)
42029             : 0;
42030         };
42031         return Point;
42032     }()),
42033     
42034     
42035     // Bezier Constructor
42036     Bezier: (function () {
42037         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
42038             this.startPoint = startPoint;
42039             this.control2 = control2;
42040             this.control1 = control1;
42041             this.endPoint = endPoint;
42042             this.startWidth = startWidth;
42043             this.endWidth = endWidth;
42044         }
42045         Bezier.fromPoints = function (points, widths, scope) {
42046             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
42047             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
42048             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
42049         };
42050         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
42051             var dx1 = s1.x - s2.x;
42052             var dy1 = s1.y - s2.y;
42053             var dx2 = s2.x - s3.x;
42054             var dy2 = s2.y - s3.y;
42055             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
42056             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
42057             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
42058             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
42059             var dxm = m1.x - m2.x;
42060             var dym = m1.y - m2.y;
42061             var k = l2 / (l1 + l2);
42062             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
42063             var tx = s2.x - cm.x;
42064             var ty = s2.y - cm.y;
42065             return {
42066                 c1: new scope.Point(m1.x + tx, m1.y + ty),
42067                 c2: new scope.Point(m2.x + tx, m2.y + ty)
42068             };
42069         };
42070         Bezier.prototype.length = function () {
42071             var steps = 10;
42072             var length = 0;
42073             var px;
42074             var py;
42075             for (var i = 0; i <= steps; i += 1) {
42076                 var t = i / steps;
42077                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
42078                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
42079                 if (i > 0) {
42080                     var xdiff = cx - px;
42081                     var ydiff = cy - py;
42082                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
42083                 }
42084                 px = cx;
42085                 py = cy;
42086             }
42087             return length;
42088         };
42089         Bezier.prototype.point = function (t, start, c1, c2, end) {
42090             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
42091             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
42092             + (3.0 * c2 * (1.0 - t) * t * t)
42093             + (end * t * t * t);
42094         };
42095         return Bezier;
42096     }()),
42097     
42098     throttleStroke: function(fn, wait) {
42099       if (wait === void 0) { wait = 250; }
42100       var previous = 0;
42101       var timeout = null;
42102       var result;
42103       var storedContext;
42104       var storedArgs;
42105       var later = function () {
42106           previous = Date.now();
42107           timeout = null;
42108           result = fn.apply(storedContext, storedArgs);
42109           if (!timeout) {
42110               storedContext = null;
42111               storedArgs = [];
42112           }
42113       };
42114       return function wrapper() {
42115           var args = [];
42116           for (var _i = 0; _i < arguments.length; _i++) {
42117               args[_i] = arguments[_i];
42118           }
42119           var now = Date.now();
42120           var remaining = wait - (now - previous);
42121           storedContext = this;
42122           storedArgs = args;
42123           if (remaining <= 0 || remaining > wait) {
42124               if (timeout) {
42125                   clearTimeout(timeout);
42126                   timeout = null;
42127               }
42128               previous = now;
42129               result = fn.apply(storedContext, storedArgs);
42130               if (!timeout) {
42131                   storedContext = null;
42132                   storedArgs = [];
42133               }
42134           }
42135           else if (!timeout) {
42136               timeout = window.setTimeout(later, remaining);
42137           }
42138           return result;
42139       };
42140   }
42141   
42142 });
42143
42144  
42145
42146