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                 
7187                   
7188                 if (!config[size]) { // 0 = hidden
7189                     // BS 4 '0' is treated as hide that column and below.
7190                     td.cls += ' hidden-' + size + ' hidden' + size + 'down';
7191                     return;
7192                 }
7193                 
7194                 td.cls += ' col-' + size + '-' + config[size] + (
7195                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
7196                 );
7197                  
7198
7199             });
7200             
7201             row.cn.push(td);
7202            
7203         }
7204         
7205         row.cellObjects = cellObjects;
7206         
7207         return row;
7208           
7209     },
7210     
7211     
7212     
7213     onBeforeLoad : function()
7214     {
7215         
7216     },
7217      /**
7218      * Remove all rows
7219      */
7220     clear : function()
7221     {
7222         this.el.select('tbody', true).first().dom.innerHTML = '';
7223     },
7224     /**
7225      * Show or hide a row.
7226      * @param {Number} rowIndex to show or hide
7227      * @param {Boolean} state hide
7228      */
7229     setRowVisibility : function(rowIndex, state)
7230     {
7231         var bt = this.mainBody.dom;
7232         
7233         var rows = this.el.select('tbody > tr', true).elements;
7234         
7235         if(typeof(rows[rowIndex]) == 'undefined'){
7236             return;
7237         }
7238         rows[rowIndex].dom.style.display = state ? '' : 'none';
7239     },
7240     
7241     
7242     getSelectionModel : function(){
7243         if(!this.selModel){
7244             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7245         }
7246         return this.selModel;
7247     },
7248     /*
7249      * Render the Roo.bootstrap object from renderder
7250      */
7251     renderCellObject : function(r)
7252     {
7253         var _this = this;
7254         
7255         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7256         
7257         var t = r.cfg.render(r.container);
7258         
7259         if(r.cfg.cn){
7260             Roo.each(r.cfg.cn, function(c){
7261                 var child = {
7262                     container: t.getChildContainer(),
7263                     cfg: c
7264                 };
7265                 _this.renderCellObject(child);
7266             })
7267         }
7268     },
7269     
7270     getRowIndex : function(row)
7271     {
7272         var rowIndex = -1;
7273         
7274         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7275             if(el != row){
7276                 return;
7277             }
7278             
7279             rowIndex = index;
7280         });
7281         
7282         return rowIndex;
7283     },
7284      /**
7285      * Returns the grid's underlying element = used by panel.Grid
7286      * @return {Element} The element
7287      */
7288     getGridEl : function(){
7289         return this.el;
7290     },
7291      /**
7292      * Forces a resize - used by panel.Grid
7293      * @return {Element} The element
7294      */
7295     autoSize : function()
7296     {
7297         //var ctr = Roo.get(this.container.dom.parentElement);
7298         var ctr = Roo.get(this.el.dom);
7299         
7300         var thd = this.getGridEl().select('thead',true).first();
7301         var tbd = this.getGridEl().select('tbody', true).first();
7302         var tfd = this.getGridEl().select('tfoot', true).first();
7303         
7304         var cw = ctr.getWidth();
7305         
7306         if (tbd) {
7307             
7308             tbd.setSize(ctr.getWidth(),
7309                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7310             );
7311             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7312             cw -= barsize;
7313         }
7314         cw = Math.max(cw, this.totalWidth);
7315         this.getGridEl().select('tr',true).setWidth(cw);
7316         // resize 'expandable coloumn?
7317         
7318         return; // we doe not have a view in this design..
7319         
7320     },
7321     onBodyScroll: function()
7322     {
7323         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7324         if(this.mainHead){
7325             this.mainHead.setStyle({
7326                 'position' : 'relative',
7327                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7328             });
7329         }
7330         
7331         if(this.lazyLoad){
7332             
7333             var scrollHeight = this.mainBody.dom.scrollHeight;
7334             
7335             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7336             
7337             var height = this.mainBody.getHeight();
7338             
7339             if(scrollHeight - height == scrollTop) {
7340                 
7341                 var total = this.ds.getTotalCount();
7342                 
7343                 if(this.footer.cursor + this.footer.pageSize < total){
7344                     
7345                     this.footer.ds.load({
7346                         params : {
7347                             start : this.footer.cursor + this.footer.pageSize,
7348                             limit : this.footer.pageSize
7349                         },
7350                         add : true
7351                     });
7352                 }
7353             }
7354             
7355         }
7356     },
7357     
7358     onHeaderChange : function()
7359     {
7360         var header = this.renderHeader();
7361         var table = this.el.select('table', true).first();
7362         
7363         this.mainHead.remove();
7364         this.mainHead = table.createChild(header, this.mainBody, false);
7365     },
7366     
7367     onHiddenChange : function(colModel, colIndex, hidden)
7368     {
7369         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7370         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7371         
7372         this.CSS.updateRule(thSelector, "display", "");
7373         this.CSS.updateRule(tdSelector, "display", "");
7374         
7375         if(hidden){
7376             this.CSS.updateRule(thSelector, "display", "none");
7377             this.CSS.updateRule(tdSelector, "display", "none");
7378         }
7379         
7380         this.onHeaderChange();
7381         this.onLoad();
7382     },
7383     
7384     setColumnWidth: function(col_index, width)
7385     {
7386         // width = "md-2 xs-2..."
7387         if(!this.colModel.config[col_index]) {
7388             return;
7389         }
7390         
7391         var w = width.split(" ");
7392         
7393         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7394         
7395         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7396         
7397         
7398         for(var j = 0; j < w.length; j++) {
7399             
7400             if(!w[j]) {
7401                 continue;
7402             }
7403             
7404             var size_cls = w[j].split("-");
7405             
7406             if(!Number.isInteger(size_cls[1] * 1)) {
7407                 continue;
7408             }
7409             
7410             if(!this.colModel.config[col_index][size_cls[0]]) {
7411                 continue;
7412             }
7413             
7414             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7415                 continue;
7416             }
7417             
7418             h_row[0].classList.replace(
7419                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7420                 "col-"+size_cls[0]+"-"+size_cls[1]
7421             );
7422             
7423             for(var i = 0; i < rows.length; i++) {
7424                 
7425                 var size_cls = w[j].split("-");
7426                 
7427                 if(!Number.isInteger(size_cls[1] * 1)) {
7428                     continue;
7429                 }
7430                 
7431                 if(!this.colModel.config[col_index][size_cls[0]]) {
7432                     continue;
7433                 }
7434                 
7435                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7436                     continue;
7437                 }
7438                 
7439                 rows[i].classList.replace(
7440                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7441                     "col-"+size_cls[0]+"-"+size_cls[1]
7442                 );
7443             }
7444             
7445             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7446         }
7447     }
7448 });
7449
7450  
7451
7452  /*
7453  * - LGPL
7454  *
7455  * table cell
7456  * 
7457  */
7458
7459 /**
7460  * @class Roo.bootstrap.TableCell
7461  * @extends Roo.bootstrap.Component
7462  * Bootstrap TableCell class
7463  * @cfg {String} html cell contain text
7464  * @cfg {String} cls cell class
7465  * @cfg {String} tag cell tag (td|th) default td
7466  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7467  * @cfg {String} align Aligns the content in a cell
7468  * @cfg {String} axis Categorizes cells
7469  * @cfg {String} bgcolor Specifies the background color of a cell
7470  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7471  * @cfg {Number} colspan Specifies the number of columns a cell should span
7472  * @cfg {String} headers Specifies one or more header cells a cell is related to
7473  * @cfg {Number} height Sets the height of a cell
7474  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7475  * @cfg {Number} rowspan Sets the number of rows a cell should span
7476  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7477  * @cfg {String} valign Vertical aligns the content in a cell
7478  * @cfg {Number} width Specifies the width of a cell
7479  * 
7480  * @constructor
7481  * Create a new TableCell
7482  * @param {Object} config The config object
7483  */
7484
7485 Roo.bootstrap.TableCell = function(config){
7486     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7487 };
7488
7489 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7490     
7491     html: false,
7492     cls: false,
7493     tag: false,
7494     abbr: false,
7495     align: false,
7496     axis: false,
7497     bgcolor: false,
7498     charoff: false,
7499     colspan: false,
7500     headers: false,
7501     height: false,
7502     nowrap: false,
7503     rowspan: false,
7504     scope: false,
7505     valign: false,
7506     width: false,
7507     
7508     
7509     getAutoCreate : function(){
7510         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7511         
7512         cfg = {
7513             tag: 'td'
7514         };
7515         
7516         if(this.tag){
7517             cfg.tag = this.tag;
7518         }
7519         
7520         if (this.html) {
7521             cfg.html=this.html
7522         }
7523         if (this.cls) {
7524             cfg.cls=this.cls
7525         }
7526         if (this.abbr) {
7527             cfg.abbr=this.abbr
7528         }
7529         if (this.align) {
7530             cfg.align=this.align
7531         }
7532         if (this.axis) {
7533             cfg.axis=this.axis
7534         }
7535         if (this.bgcolor) {
7536             cfg.bgcolor=this.bgcolor
7537         }
7538         if (this.charoff) {
7539             cfg.charoff=this.charoff
7540         }
7541         if (this.colspan) {
7542             cfg.colspan=this.colspan
7543         }
7544         if (this.headers) {
7545             cfg.headers=this.headers
7546         }
7547         if (this.height) {
7548             cfg.height=this.height
7549         }
7550         if (this.nowrap) {
7551             cfg.nowrap=this.nowrap
7552         }
7553         if (this.rowspan) {
7554             cfg.rowspan=this.rowspan
7555         }
7556         if (this.scope) {
7557             cfg.scope=this.scope
7558         }
7559         if (this.valign) {
7560             cfg.valign=this.valign
7561         }
7562         if (this.width) {
7563             cfg.width=this.width
7564         }
7565         
7566         
7567         return cfg;
7568     }
7569    
7570 });
7571
7572  
7573
7574  /*
7575  * - LGPL
7576  *
7577  * table row
7578  * 
7579  */
7580
7581 /**
7582  * @class Roo.bootstrap.TableRow
7583  * @extends Roo.bootstrap.Component
7584  * Bootstrap TableRow class
7585  * @cfg {String} cls row class
7586  * @cfg {String} align Aligns the content in a table row
7587  * @cfg {String} bgcolor Specifies a background color for a table row
7588  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7589  * @cfg {String} valign Vertical aligns the content in a table row
7590  * 
7591  * @constructor
7592  * Create a new TableRow
7593  * @param {Object} config The config object
7594  */
7595
7596 Roo.bootstrap.TableRow = function(config){
7597     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7598 };
7599
7600 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7601     
7602     cls: false,
7603     align: false,
7604     bgcolor: false,
7605     charoff: false,
7606     valign: false,
7607     
7608     getAutoCreate : function(){
7609         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7610         
7611         cfg = {
7612             tag: 'tr'
7613         };
7614             
7615         if(this.cls){
7616             cfg.cls = this.cls;
7617         }
7618         if(this.align){
7619             cfg.align = this.align;
7620         }
7621         if(this.bgcolor){
7622             cfg.bgcolor = this.bgcolor;
7623         }
7624         if(this.charoff){
7625             cfg.charoff = this.charoff;
7626         }
7627         if(this.valign){
7628             cfg.valign = this.valign;
7629         }
7630         
7631         return cfg;
7632     }
7633    
7634 });
7635
7636  
7637
7638  /*
7639  * - LGPL
7640  *
7641  * table body
7642  * 
7643  */
7644
7645 /**
7646  * @class Roo.bootstrap.TableBody
7647  * @extends Roo.bootstrap.Component
7648  * Bootstrap TableBody class
7649  * @cfg {String} cls element class
7650  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7651  * @cfg {String} align Aligns the content inside the element
7652  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7653  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7654  * 
7655  * @constructor
7656  * Create a new TableBody
7657  * @param {Object} config The config object
7658  */
7659
7660 Roo.bootstrap.TableBody = function(config){
7661     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7662 };
7663
7664 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7665     
7666     cls: false,
7667     tag: false,
7668     align: false,
7669     charoff: false,
7670     valign: false,
7671     
7672     getAutoCreate : function(){
7673         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7674         
7675         cfg = {
7676             tag: 'tbody'
7677         };
7678             
7679         if (this.cls) {
7680             cfg.cls=this.cls
7681         }
7682         if(this.tag){
7683             cfg.tag = this.tag;
7684         }
7685         
7686         if(this.align){
7687             cfg.align = this.align;
7688         }
7689         if(this.charoff){
7690             cfg.charoff = this.charoff;
7691         }
7692         if(this.valign){
7693             cfg.valign = this.valign;
7694         }
7695         
7696         return cfg;
7697     }
7698     
7699     
7700 //    initEvents : function()
7701 //    {
7702 //        
7703 //        if(!this.store){
7704 //            return;
7705 //        }
7706 //        
7707 //        this.store = Roo.factory(this.store, Roo.data);
7708 //        this.store.on('load', this.onLoad, this);
7709 //        
7710 //        this.store.load();
7711 //        
7712 //    },
7713 //    
7714 //    onLoad: function () 
7715 //    {   
7716 //        this.fireEvent('load', this);
7717 //    }
7718 //    
7719 //   
7720 });
7721
7722  
7723
7724  /*
7725  * Based on:
7726  * Ext JS Library 1.1.1
7727  * Copyright(c) 2006-2007, Ext JS, LLC.
7728  *
7729  * Originally Released Under LGPL - original licence link has changed is not relivant.
7730  *
7731  * Fork - LGPL
7732  * <script type="text/javascript">
7733  */
7734
7735 // as we use this in bootstrap.
7736 Roo.namespace('Roo.form');
7737  /**
7738  * @class Roo.form.Action
7739  * Internal Class used to handle form actions
7740  * @constructor
7741  * @param {Roo.form.BasicForm} el The form element or its id
7742  * @param {Object} config Configuration options
7743  */
7744
7745  
7746  
7747 // define the action interface
7748 Roo.form.Action = function(form, options){
7749     this.form = form;
7750     this.options = options || {};
7751 };
7752 /**
7753  * Client Validation Failed
7754  * @const 
7755  */
7756 Roo.form.Action.CLIENT_INVALID = 'client';
7757 /**
7758  * Server Validation Failed
7759  * @const 
7760  */
7761 Roo.form.Action.SERVER_INVALID = 'server';
7762  /**
7763  * Connect to Server Failed
7764  * @const 
7765  */
7766 Roo.form.Action.CONNECT_FAILURE = 'connect';
7767 /**
7768  * Reading Data from Server Failed
7769  * @const 
7770  */
7771 Roo.form.Action.LOAD_FAILURE = 'load';
7772
7773 Roo.form.Action.prototype = {
7774     type : 'default',
7775     failureType : undefined,
7776     response : undefined,
7777     result : undefined,
7778
7779     // interface method
7780     run : function(options){
7781
7782     },
7783
7784     // interface method
7785     success : function(response){
7786
7787     },
7788
7789     // interface method
7790     handleResponse : function(response){
7791
7792     },
7793
7794     // default connection failure
7795     failure : function(response){
7796         
7797         this.response = response;
7798         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7799         this.form.afterAction(this, false);
7800     },
7801
7802     processResponse : function(response){
7803         this.response = response;
7804         if(!response.responseText){
7805             return true;
7806         }
7807         this.result = this.handleResponse(response);
7808         return this.result;
7809     },
7810
7811     // utility functions used internally
7812     getUrl : function(appendParams){
7813         var url = this.options.url || this.form.url || this.form.el.dom.action;
7814         if(appendParams){
7815             var p = this.getParams();
7816             if(p){
7817                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7818             }
7819         }
7820         return url;
7821     },
7822
7823     getMethod : function(){
7824         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7825     },
7826
7827     getParams : function(){
7828         var bp = this.form.baseParams;
7829         var p = this.options.params;
7830         if(p){
7831             if(typeof p == "object"){
7832                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7833             }else if(typeof p == 'string' && bp){
7834                 p += '&' + Roo.urlEncode(bp);
7835             }
7836         }else if(bp){
7837             p = Roo.urlEncode(bp);
7838         }
7839         return p;
7840     },
7841
7842     createCallback : function(){
7843         return {
7844             success: this.success,
7845             failure: this.failure,
7846             scope: this,
7847             timeout: (this.form.timeout*1000),
7848             upload: this.form.fileUpload ? this.success : undefined
7849         };
7850     }
7851 };
7852
7853 Roo.form.Action.Submit = function(form, options){
7854     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7855 };
7856
7857 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7858     type : 'submit',
7859
7860     haveProgress : false,
7861     uploadComplete : false,
7862     
7863     // uploadProgress indicator.
7864     uploadProgress : function()
7865     {
7866         if (!this.form.progressUrl) {
7867             return;
7868         }
7869         
7870         if (!this.haveProgress) {
7871             Roo.MessageBox.progress("Uploading", "Uploading");
7872         }
7873         if (this.uploadComplete) {
7874            Roo.MessageBox.hide();
7875            return;
7876         }
7877         
7878         this.haveProgress = true;
7879    
7880         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7881         
7882         var c = new Roo.data.Connection();
7883         c.request({
7884             url : this.form.progressUrl,
7885             params: {
7886                 id : uid
7887             },
7888             method: 'GET',
7889             success : function(req){
7890                //console.log(data);
7891                 var rdata = false;
7892                 var edata;
7893                 try  {
7894                    rdata = Roo.decode(req.responseText)
7895                 } catch (e) {
7896                     Roo.log("Invalid data from server..");
7897                     Roo.log(edata);
7898                     return;
7899                 }
7900                 if (!rdata || !rdata.success) {
7901                     Roo.log(rdata);
7902                     Roo.MessageBox.alert(Roo.encode(rdata));
7903                     return;
7904                 }
7905                 var data = rdata.data;
7906                 
7907                 if (this.uploadComplete) {
7908                    Roo.MessageBox.hide();
7909                    return;
7910                 }
7911                    
7912                 if (data){
7913                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7914                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7915                     );
7916                 }
7917                 this.uploadProgress.defer(2000,this);
7918             },
7919        
7920             failure: function(data) {
7921                 Roo.log('progress url failed ');
7922                 Roo.log(data);
7923             },
7924             scope : this
7925         });
7926            
7927     },
7928     
7929     
7930     run : function()
7931     {
7932         // run get Values on the form, so it syncs any secondary forms.
7933         this.form.getValues();
7934         
7935         var o = this.options;
7936         var method = this.getMethod();
7937         var isPost = method == 'POST';
7938         if(o.clientValidation === false || this.form.isValid()){
7939             
7940             if (this.form.progressUrl) {
7941                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7942                     (new Date() * 1) + '' + Math.random());
7943                     
7944             } 
7945             
7946             
7947             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7948                 form:this.form.el.dom,
7949                 url:this.getUrl(!isPost),
7950                 method: method,
7951                 params:isPost ? this.getParams() : null,
7952                 isUpload: this.form.fileUpload,
7953                 formData : this.form.formData
7954             }));
7955             
7956             this.uploadProgress();
7957
7958         }else if (o.clientValidation !== false){ // client validation failed
7959             this.failureType = Roo.form.Action.CLIENT_INVALID;
7960             this.form.afterAction(this, false);
7961         }
7962     },
7963
7964     success : function(response)
7965     {
7966         this.uploadComplete= true;
7967         if (this.haveProgress) {
7968             Roo.MessageBox.hide();
7969         }
7970         
7971         
7972         var result = this.processResponse(response);
7973         if(result === true || result.success){
7974             this.form.afterAction(this, true);
7975             return;
7976         }
7977         if(result.errors){
7978             this.form.markInvalid(result.errors);
7979             this.failureType = Roo.form.Action.SERVER_INVALID;
7980         }
7981         this.form.afterAction(this, false);
7982     },
7983     failure : function(response)
7984     {
7985         this.uploadComplete= true;
7986         if (this.haveProgress) {
7987             Roo.MessageBox.hide();
7988         }
7989         
7990         this.response = response;
7991         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7992         this.form.afterAction(this, false);
7993     },
7994     
7995     handleResponse : function(response){
7996         if(this.form.errorReader){
7997             var rs = this.form.errorReader.read(response);
7998             var errors = [];
7999             if(rs.records){
8000                 for(var i = 0, len = rs.records.length; i < len; i++) {
8001                     var r = rs.records[i];
8002                     errors[i] = r.data;
8003                 }
8004             }
8005             if(errors.length < 1){
8006                 errors = null;
8007             }
8008             return {
8009                 success : rs.success,
8010                 errors : errors
8011             };
8012         }
8013         var ret = false;
8014         try {
8015             ret = Roo.decode(response.responseText);
8016         } catch (e) {
8017             ret = {
8018                 success: false,
8019                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
8020                 errors : []
8021             };
8022         }
8023         return ret;
8024         
8025     }
8026 });
8027
8028
8029 Roo.form.Action.Load = function(form, options){
8030     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
8031     this.reader = this.form.reader;
8032 };
8033
8034 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
8035     type : 'load',
8036
8037     run : function(){
8038         
8039         Roo.Ajax.request(Roo.apply(
8040                 this.createCallback(), {
8041                     method:this.getMethod(),
8042                     url:this.getUrl(false),
8043                     params:this.getParams()
8044         }));
8045     },
8046
8047     success : function(response){
8048         
8049         var result = this.processResponse(response);
8050         if(result === true || !result.success || !result.data){
8051             this.failureType = Roo.form.Action.LOAD_FAILURE;
8052             this.form.afterAction(this, false);
8053             return;
8054         }
8055         this.form.clearInvalid();
8056         this.form.setValues(result.data);
8057         this.form.afterAction(this, true);
8058     },
8059
8060     handleResponse : function(response){
8061         if(this.form.reader){
8062             var rs = this.form.reader.read(response);
8063             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
8064             return {
8065                 success : rs.success,
8066                 data : data
8067             };
8068         }
8069         return Roo.decode(response.responseText);
8070     }
8071 });
8072
8073 Roo.form.Action.ACTION_TYPES = {
8074     'load' : Roo.form.Action.Load,
8075     'submit' : Roo.form.Action.Submit
8076 };/*
8077  * - LGPL
8078  *
8079  * form
8080  *
8081  */
8082
8083 /**
8084  * @class Roo.bootstrap.Form
8085  * @extends Roo.bootstrap.Component
8086  * Bootstrap Form class
8087  * @cfg {String} method  GET | POST (default POST)
8088  * @cfg {String} labelAlign top | left (default top)
8089  * @cfg {String} align left  | right - for navbars
8090  * @cfg {Boolean} loadMask load mask when submit (default true)
8091
8092  *
8093  * @constructor
8094  * Create a new Form
8095  * @param {Object} config The config object
8096  */
8097
8098
8099 Roo.bootstrap.Form = function(config){
8100     
8101     Roo.bootstrap.Form.superclass.constructor.call(this, config);
8102     
8103     Roo.bootstrap.Form.popover.apply();
8104     
8105     this.addEvents({
8106         /**
8107          * @event clientvalidation
8108          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
8109          * @param {Form} this
8110          * @param {Boolean} valid true if the form has passed client-side validation
8111          */
8112         clientvalidation: true,
8113         /**
8114          * @event beforeaction
8115          * Fires before any action is performed. Return false to cancel the action.
8116          * @param {Form} this
8117          * @param {Action} action The action to be performed
8118          */
8119         beforeaction: true,
8120         /**
8121          * @event actionfailed
8122          * Fires when an action fails.
8123          * @param {Form} this
8124          * @param {Action} action The action that failed
8125          */
8126         actionfailed : true,
8127         /**
8128          * @event actioncomplete
8129          * Fires when an action is completed.
8130          * @param {Form} this
8131          * @param {Action} action The action that completed
8132          */
8133         actioncomplete : true
8134     });
8135 };
8136
8137 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
8138
8139      /**
8140      * @cfg {String} method
8141      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8142      */
8143     method : 'POST',
8144     /**
8145      * @cfg {String} url
8146      * The URL to use for form actions if one isn't supplied in the action options.
8147      */
8148     /**
8149      * @cfg {Boolean} fileUpload
8150      * Set to true if this form is a file upload.
8151      */
8152
8153     /**
8154      * @cfg {Object} baseParams
8155      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8156      */
8157
8158     /**
8159      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8160      */
8161     timeout: 30,
8162     /**
8163      * @cfg {Sting} align (left|right) for navbar forms
8164      */
8165     align : 'left',
8166
8167     // private
8168     activeAction : null,
8169
8170     /**
8171      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8172      * element by passing it or its id or mask the form itself by passing in true.
8173      * @type Mixed
8174      */
8175     waitMsgTarget : false,
8176
8177     loadMask : true,
8178     
8179     /**
8180      * @cfg {Boolean} errorMask (true|false) default false
8181      */
8182     errorMask : false,
8183     
8184     /**
8185      * @cfg {Number} maskOffset Default 100
8186      */
8187     maskOffset : 100,
8188     
8189     /**
8190      * @cfg {Boolean} maskBody
8191      */
8192     maskBody : false,
8193
8194     getAutoCreate : function(){
8195
8196         var cfg = {
8197             tag: 'form',
8198             method : this.method || 'POST',
8199             id : this.id || Roo.id(),
8200             cls : ''
8201         };
8202         if (this.parent().xtype.match(/^Nav/)) {
8203             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8204
8205         }
8206
8207         if (this.labelAlign == 'left' ) {
8208             cfg.cls += ' form-horizontal';
8209         }
8210
8211
8212         return cfg;
8213     },
8214     initEvents : function()
8215     {
8216         this.el.on('submit', this.onSubmit, this);
8217         // this was added as random key presses on the form where triggering form submit.
8218         this.el.on('keypress', function(e) {
8219             if (e.getCharCode() != 13) {
8220                 return true;
8221             }
8222             // we might need to allow it for textareas.. and some other items.
8223             // check e.getTarget().
8224
8225             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8226                 return true;
8227             }
8228
8229             Roo.log("keypress blocked");
8230
8231             e.preventDefault();
8232             return false;
8233         });
8234         
8235     },
8236     // private
8237     onSubmit : function(e){
8238         e.stopEvent();
8239     },
8240
8241      /**
8242      * Returns true if client-side validation on the form is successful.
8243      * @return Boolean
8244      */
8245     isValid : function(){
8246         var items = this.getItems();
8247         var valid = true;
8248         var target = false;
8249         
8250         items.each(function(f){
8251             
8252             if(f.validate()){
8253                 return;
8254             }
8255             
8256             Roo.log('invalid field: ' + f.name);
8257             
8258             valid = false;
8259
8260             if(!target && f.el.isVisible(true)){
8261                 target = f;
8262             }
8263            
8264         });
8265         
8266         if(this.errorMask && !valid){
8267             Roo.bootstrap.Form.popover.mask(this, target);
8268         }
8269         
8270         return valid;
8271     },
8272     
8273     /**
8274      * Returns true if any fields in this form have changed since their original load.
8275      * @return Boolean
8276      */
8277     isDirty : function(){
8278         var dirty = false;
8279         var items = this.getItems();
8280         items.each(function(f){
8281            if(f.isDirty()){
8282                dirty = true;
8283                return false;
8284            }
8285            return true;
8286         });
8287         return dirty;
8288     },
8289      /**
8290      * Performs a predefined action (submit or load) or custom actions you define on this form.
8291      * @param {String} actionName The name of the action type
8292      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8293      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8294      * accept other config options):
8295      * <pre>
8296 Property          Type             Description
8297 ----------------  ---------------  ----------------------------------------------------------------------------------
8298 url               String           The url for the action (defaults to the form's url)
8299 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8300 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8301 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8302                                    validate the form on the client (defaults to false)
8303      * </pre>
8304      * @return {BasicForm} this
8305      */
8306     doAction : function(action, options){
8307         if(typeof action == 'string'){
8308             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8309         }
8310         if(this.fireEvent('beforeaction', this, action) !== false){
8311             this.beforeAction(action);
8312             action.run.defer(100, action);
8313         }
8314         return this;
8315     },
8316
8317     // private
8318     beforeAction : function(action){
8319         var o = action.options;
8320         
8321         if(this.loadMask){
8322             
8323             if(this.maskBody){
8324                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8325             } else {
8326                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8327             }
8328         }
8329         // not really supported yet.. ??
8330
8331         //if(this.waitMsgTarget === true){
8332         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8333         //}else if(this.waitMsgTarget){
8334         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8335         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8336         //}else {
8337         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8338        // }
8339
8340     },
8341
8342     // private
8343     afterAction : function(action, success){
8344         this.activeAction = null;
8345         var o = action.options;
8346
8347         if(this.loadMask){
8348             
8349             if(this.maskBody){
8350                 Roo.get(document.body).unmask();
8351             } else {
8352                 this.el.unmask();
8353             }
8354         }
8355         
8356         //if(this.waitMsgTarget === true){
8357 //            this.el.unmask();
8358         //}else if(this.waitMsgTarget){
8359         //    this.waitMsgTarget.unmask();
8360         //}else{
8361         //    Roo.MessageBox.updateProgress(1);
8362         //    Roo.MessageBox.hide();
8363        // }
8364         //
8365         if(success){
8366             if(o.reset){
8367                 this.reset();
8368             }
8369             Roo.callback(o.success, o.scope, [this, action]);
8370             this.fireEvent('actioncomplete', this, action);
8371
8372         }else{
8373
8374             // failure condition..
8375             // we have a scenario where updates need confirming.
8376             // eg. if a locking scenario exists..
8377             // we look for { errors : { needs_confirm : true }} in the response.
8378             if (
8379                 (typeof(action.result) != 'undefined')  &&
8380                 (typeof(action.result.errors) != 'undefined')  &&
8381                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8382            ){
8383                 var _t = this;
8384                 Roo.log("not supported yet");
8385                  /*
8386
8387                 Roo.MessageBox.confirm(
8388                     "Change requires confirmation",
8389                     action.result.errorMsg,
8390                     function(r) {
8391                         if (r != 'yes') {
8392                             return;
8393                         }
8394                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8395                     }
8396
8397                 );
8398                 */
8399
8400
8401                 return;
8402             }
8403
8404             Roo.callback(o.failure, o.scope, [this, action]);
8405             // show an error message if no failed handler is set..
8406             if (!this.hasListener('actionfailed')) {
8407                 Roo.log("need to add dialog support");
8408                 /*
8409                 Roo.MessageBox.alert("Error",
8410                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8411                         action.result.errorMsg :
8412                         "Saving Failed, please check your entries or try again"
8413                 );
8414                 */
8415             }
8416
8417             this.fireEvent('actionfailed', this, action);
8418         }
8419
8420     },
8421     /**
8422      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8423      * @param {String} id The value to search for
8424      * @return Field
8425      */
8426     findField : function(id){
8427         var items = this.getItems();
8428         var field = items.get(id);
8429         if(!field){
8430              items.each(function(f){
8431                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8432                     field = f;
8433                     return false;
8434                 }
8435                 return true;
8436             });
8437         }
8438         return field || null;
8439     },
8440      /**
8441      * Mark fields in this form invalid in bulk.
8442      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8443      * @return {BasicForm} this
8444      */
8445     markInvalid : function(errors){
8446         if(errors instanceof Array){
8447             for(var i = 0, len = errors.length; i < len; i++){
8448                 var fieldError = errors[i];
8449                 var f = this.findField(fieldError.id);
8450                 if(f){
8451                     f.markInvalid(fieldError.msg);
8452                 }
8453             }
8454         }else{
8455             var field, id;
8456             for(id in errors){
8457                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8458                     field.markInvalid(errors[id]);
8459                 }
8460             }
8461         }
8462         //Roo.each(this.childForms || [], function (f) {
8463         //    f.markInvalid(errors);
8464         //});
8465
8466         return this;
8467     },
8468
8469     /**
8470      * Set values for fields in this form in bulk.
8471      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8472      * @return {BasicForm} this
8473      */
8474     setValues : function(values){
8475         if(values instanceof Array){ // array of objects
8476             for(var i = 0, len = values.length; i < len; i++){
8477                 var v = values[i];
8478                 var f = this.findField(v.id);
8479                 if(f){
8480                     f.setValue(v.value);
8481                     if(this.trackResetOnLoad){
8482                         f.originalValue = f.getValue();
8483                     }
8484                 }
8485             }
8486         }else{ // object hash
8487             var field, id;
8488             for(id in values){
8489                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8490
8491                     if (field.setFromData &&
8492                         field.valueField &&
8493                         field.displayField &&
8494                         // combos' with local stores can
8495                         // be queried via setValue()
8496                         // to set their value..
8497                         (field.store && !field.store.isLocal)
8498                         ) {
8499                         // it's a combo
8500                         var sd = { };
8501                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8502                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8503                         field.setFromData(sd);
8504
8505                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8506                         
8507                         field.setFromData(values);
8508                         
8509                     } else {
8510                         field.setValue(values[id]);
8511                     }
8512
8513
8514                     if(this.trackResetOnLoad){
8515                         field.originalValue = field.getValue();
8516                     }
8517                 }
8518             }
8519         }
8520
8521         //Roo.each(this.childForms || [], function (f) {
8522         //    f.setValues(values);
8523         //});
8524
8525         return this;
8526     },
8527
8528     /**
8529      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8530      * they are returned as an array.
8531      * @param {Boolean} asString
8532      * @return {Object}
8533      */
8534     getValues : function(asString){
8535         //if (this.childForms) {
8536             // copy values from the child forms
8537         //    Roo.each(this.childForms, function (f) {
8538         //        this.setValues(f.getValues());
8539         //    }, this);
8540         //}
8541
8542
8543
8544         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8545         if(asString === true){
8546             return fs;
8547         }
8548         return Roo.urlDecode(fs);
8549     },
8550
8551     /**
8552      * Returns the fields in this form as an object with key/value pairs.
8553      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8554      * @return {Object}
8555      */
8556     getFieldValues : function(with_hidden)
8557     {
8558         var items = this.getItems();
8559         var ret = {};
8560         items.each(function(f){
8561             
8562             if (!f.getName()) {
8563                 return;
8564             }
8565             
8566             var v = f.getValue();
8567             
8568             if (f.inputType =='radio') {
8569                 if (typeof(ret[f.getName()]) == 'undefined') {
8570                     ret[f.getName()] = ''; // empty..
8571                 }
8572
8573                 if (!f.el.dom.checked) {
8574                     return;
8575
8576                 }
8577                 v = f.el.dom.value;
8578
8579             }
8580             
8581             if(f.xtype == 'MoneyField'){
8582                 ret[f.currencyName] = f.getCurrency();
8583             }
8584
8585             // not sure if this supported any more..
8586             if ((typeof(v) == 'object') && f.getRawValue) {
8587                 v = f.getRawValue() ; // dates..
8588             }
8589             // combo boxes where name != hiddenName...
8590             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8591                 ret[f.name] = f.getRawValue();
8592             }
8593             ret[f.getName()] = v;
8594         });
8595
8596         return ret;
8597     },
8598
8599     /**
8600      * Clears all invalid messages in this form.
8601      * @return {BasicForm} this
8602      */
8603     clearInvalid : function(){
8604         var items = this.getItems();
8605
8606         items.each(function(f){
8607            f.clearInvalid();
8608         });
8609
8610         return this;
8611     },
8612
8613     /**
8614      * Resets this form.
8615      * @return {BasicForm} this
8616      */
8617     reset : function(){
8618         var items = this.getItems();
8619         items.each(function(f){
8620             f.reset();
8621         });
8622
8623         Roo.each(this.childForms || [], function (f) {
8624             f.reset();
8625         });
8626
8627
8628         return this;
8629     },
8630     
8631     getItems : function()
8632     {
8633         var r=new Roo.util.MixedCollection(false, function(o){
8634             return o.id || (o.id = Roo.id());
8635         });
8636         var iter = function(el) {
8637             if (el.inputEl) {
8638                 r.add(el);
8639             }
8640             if (!el.items) {
8641                 return;
8642             }
8643             Roo.each(el.items,function(e) {
8644                 iter(e);
8645             });
8646         };
8647
8648         iter(this);
8649         return r;
8650     },
8651     
8652     hideFields : function(items)
8653     {
8654         Roo.each(items, function(i){
8655             
8656             var f = this.findField(i);
8657             
8658             if(!f){
8659                 return;
8660             }
8661             
8662             f.hide();
8663             
8664         }, this);
8665     },
8666     
8667     showFields : function(items)
8668     {
8669         Roo.each(items, function(i){
8670             
8671             var f = this.findField(i);
8672             
8673             if(!f){
8674                 return;
8675             }
8676             
8677             f.show();
8678             
8679         }, this);
8680     }
8681
8682 });
8683
8684 Roo.apply(Roo.bootstrap.Form, {
8685     
8686     popover : {
8687         
8688         padding : 5,
8689         
8690         isApplied : false,
8691         
8692         isMasked : false,
8693         
8694         form : false,
8695         
8696         target : false,
8697         
8698         toolTip : false,
8699         
8700         intervalID : false,
8701         
8702         maskEl : false,
8703         
8704         apply : function()
8705         {
8706             if(this.isApplied){
8707                 return;
8708             }
8709             
8710             this.maskEl = {
8711                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8712                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8713                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8714                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8715             };
8716             
8717             this.maskEl.top.enableDisplayMode("block");
8718             this.maskEl.left.enableDisplayMode("block");
8719             this.maskEl.bottom.enableDisplayMode("block");
8720             this.maskEl.right.enableDisplayMode("block");
8721             
8722             this.toolTip = new Roo.bootstrap.Tooltip({
8723                 cls : 'roo-form-error-popover',
8724                 alignment : {
8725                     'left' : ['r-l', [-2,0], 'right'],
8726                     'right' : ['l-r', [2,0], 'left'],
8727                     'bottom' : ['tl-bl', [0,2], 'top'],
8728                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8729                 }
8730             });
8731             
8732             this.toolTip.render(Roo.get(document.body));
8733
8734             this.toolTip.el.enableDisplayMode("block");
8735             
8736             Roo.get(document.body).on('click', function(){
8737                 this.unmask();
8738             }, this);
8739             
8740             Roo.get(document.body).on('touchstart', function(){
8741                 this.unmask();
8742             }, this);
8743             
8744             this.isApplied = true
8745         },
8746         
8747         mask : function(form, target)
8748         {
8749             this.form = form;
8750             
8751             this.target = target;
8752             
8753             if(!this.form.errorMask || !target.el){
8754                 return;
8755             }
8756             
8757             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8758             
8759             Roo.log(scrollable);
8760             
8761             var ot = this.target.el.calcOffsetsTo(scrollable);
8762             
8763             var scrollTo = ot[1] - this.form.maskOffset;
8764             
8765             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8766             
8767             scrollable.scrollTo('top', scrollTo);
8768             
8769             var box = this.target.el.getBox();
8770             Roo.log(box);
8771             var zIndex = Roo.bootstrap.Modal.zIndex++;
8772
8773             
8774             this.maskEl.top.setStyle('position', 'absolute');
8775             this.maskEl.top.setStyle('z-index', zIndex);
8776             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8777             this.maskEl.top.setLeft(0);
8778             this.maskEl.top.setTop(0);
8779             this.maskEl.top.show();
8780             
8781             this.maskEl.left.setStyle('position', 'absolute');
8782             this.maskEl.left.setStyle('z-index', zIndex);
8783             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8784             this.maskEl.left.setLeft(0);
8785             this.maskEl.left.setTop(box.y - this.padding);
8786             this.maskEl.left.show();
8787
8788             this.maskEl.bottom.setStyle('position', 'absolute');
8789             this.maskEl.bottom.setStyle('z-index', zIndex);
8790             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8791             this.maskEl.bottom.setLeft(0);
8792             this.maskEl.bottom.setTop(box.bottom + this.padding);
8793             this.maskEl.bottom.show();
8794
8795             this.maskEl.right.setStyle('position', 'absolute');
8796             this.maskEl.right.setStyle('z-index', zIndex);
8797             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8798             this.maskEl.right.setLeft(box.right + this.padding);
8799             this.maskEl.right.setTop(box.y - this.padding);
8800             this.maskEl.right.show();
8801
8802             this.toolTip.bindEl = this.target.el;
8803
8804             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8805
8806             var tip = this.target.blankText;
8807
8808             if(this.target.getValue() !== '' ) {
8809                 
8810                 if (this.target.invalidText.length) {
8811                     tip = this.target.invalidText;
8812                 } else if (this.target.regexText.length){
8813                     tip = this.target.regexText;
8814                 }
8815             }
8816
8817             this.toolTip.show(tip);
8818
8819             this.intervalID = window.setInterval(function() {
8820                 Roo.bootstrap.Form.popover.unmask();
8821             }, 10000);
8822
8823             window.onwheel = function(){ return false;};
8824             
8825             (function(){ this.isMasked = true; }).defer(500, this);
8826             
8827         },
8828         
8829         unmask : function()
8830         {
8831             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8832                 return;
8833             }
8834             
8835             this.maskEl.top.setStyle('position', 'absolute');
8836             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8837             this.maskEl.top.hide();
8838
8839             this.maskEl.left.setStyle('position', 'absolute');
8840             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8841             this.maskEl.left.hide();
8842
8843             this.maskEl.bottom.setStyle('position', 'absolute');
8844             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8845             this.maskEl.bottom.hide();
8846
8847             this.maskEl.right.setStyle('position', 'absolute');
8848             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8849             this.maskEl.right.hide();
8850             
8851             this.toolTip.hide();
8852             
8853             this.toolTip.el.hide();
8854             
8855             window.onwheel = function(){ return true;};
8856             
8857             if(this.intervalID){
8858                 window.clearInterval(this.intervalID);
8859                 this.intervalID = false;
8860             }
8861             
8862             this.isMasked = false;
8863             
8864         }
8865         
8866     }
8867     
8868 });
8869
8870 /*
8871  * Based on:
8872  * Ext JS Library 1.1.1
8873  * Copyright(c) 2006-2007, Ext JS, LLC.
8874  *
8875  * Originally Released Under LGPL - original licence link has changed is not relivant.
8876  *
8877  * Fork - LGPL
8878  * <script type="text/javascript">
8879  */
8880 /**
8881  * @class Roo.form.VTypes
8882  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8883  * @singleton
8884  */
8885 Roo.form.VTypes = function(){
8886     // closure these in so they are only created once.
8887     var alpha = /^[a-zA-Z_]+$/;
8888     var alphanum = /^[a-zA-Z0-9_]+$/;
8889     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8890     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8891
8892     // All these messages and functions are configurable
8893     return {
8894         /**
8895          * The function used to validate email addresses
8896          * @param {String} value The email address
8897          */
8898         'email' : function(v){
8899             return email.test(v);
8900         },
8901         /**
8902          * The error text to display when the email validation function returns false
8903          * @type String
8904          */
8905         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8906         /**
8907          * The keystroke filter mask to be applied on email input
8908          * @type RegExp
8909          */
8910         'emailMask' : /[a-z0-9_\.\-@]/i,
8911
8912         /**
8913          * The function used to validate URLs
8914          * @param {String} value The URL
8915          */
8916         'url' : function(v){
8917             return url.test(v);
8918         },
8919         /**
8920          * The error text to display when the url validation function returns false
8921          * @type String
8922          */
8923         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8924         
8925         /**
8926          * The function used to validate alpha values
8927          * @param {String} value The value
8928          */
8929         'alpha' : function(v){
8930             return alpha.test(v);
8931         },
8932         /**
8933          * The error text to display when the alpha validation function returns false
8934          * @type String
8935          */
8936         'alphaText' : 'This field should only contain letters and _',
8937         /**
8938          * The keystroke filter mask to be applied on alpha input
8939          * @type RegExp
8940          */
8941         'alphaMask' : /[a-z_]/i,
8942
8943         /**
8944          * The function used to validate alphanumeric values
8945          * @param {String} value The value
8946          */
8947         'alphanum' : function(v){
8948             return alphanum.test(v);
8949         },
8950         /**
8951          * The error text to display when the alphanumeric validation function returns false
8952          * @type String
8953          */
8954         'alphanumText' : 'This field should only contain letters, numbers and _',
8955         /**
8956          * The keystroke filter mask to be applied on alphanumeric input
8957          * @type RegExp
8958          */
8959         'alphanumMask' : /[a-z0-9_]/i
8960     };
8961 }();/*
8962  * - LGPL
8963  *
8964  * Input
8965  * 
8966  */
8967
8968 /**
8969  * @class Roo.bootstrap.Input
8970  * @extends Roo.bootstrap.Component
8971  * Bootstrap Input class
8972  * @cfg {Boolean} disabled is it disabled
8973  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8974  * @cfg {String} name name of the input
8975  * @cfg {string} fieldLabel - the label associated
8976  * @cfg {string} placeholder - placeholder to put in text.
8977  * @cfg {string}  before - input group add on before
8978  * @cfg {string} after - input group add on after
8979  * @cfg {string} size - (lg|sm) or leave empty..
8980  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8981  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8982  * @cfg {Number} md colspan out of 12 for computer-sized screens
8983  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8984  * @cfg {string} value default value of the input
8985  * @cfg {Number} labelWidth set the width of label 
8986  * @cfg {Number} labellg set the width of label (1-12)
8987  * @cfg {Number} labelmd set the width of label (1-12)
8988  * @cfg {Number} labelsm set the width of label (1-12)
8989  * @cfg {Number} labelxs set the width of label (1-12)
8990  * @cfg {String} labelAlign (top|left)
8991  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8992  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8993  * @cfg {String} indicatorpos (left|right) default left
8994  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8995  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8996
8997  * @cfg {String} align (left|center|right) Default left
8998  * @cfg {Boolean} forceFeedback (true|false) Default false
8999  * 
9000  * @constructor
9001  * Create a new Input
9002  * @param {Object} config The config object
9003  */
9004
9005 Roo.bootstrap.Input = function(config){
9006     
9007     Roo.bootstrap.Input.superclass.constructor.call(this, config);
9008     
9009     this.addEvents({
9010         /**
9011          * @event focus
9012          * Fires when this field receives input focus.
9013          * @param {Roo.form.Field} this
9014          */
9015         focus : true,
9016         /**
9017          * @event blur
9018          * Fires when this field loses input focus.
9019          * @param {Roo.form.Field} this
9020          */
9021         blur : true,
9022         /**
9023          * @event specialkey
9024          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
9025          * {@link Roo.EventObject#getKey} to determine which key was pressed.
9026          * @param {Roo.form.Field} this
9027          * @param {Roo.EventObject} e The event object
9028          */
9029         specialkey : true,
9030         /**
9031          * @event change
9032          * Fires just before the field blurs if the field value has changed.
9033          * @param {Roo.form.Field} this
9034          * @param {Mixed} newValue The new value
9035          * @param {Mixed} oldValue The original value
9036          */
9037         change : true,
9038         /**
9039          * @event invalid
9040          * Fires after the field has been marked as invalid.
9041          * @param {Roo.form.Field} this
9042          * @param {String} msg The validation message
9043          */
9044         invalid : true,
9045         /**
9046          * @event valid
9047          * Fires after the field has been validated with no errors.
9048          * @param {Roo.form.Field} this
9049          */
9050         valid : true,
9051          /**
9052          * @event keyup
9053          * Fires after the key up
9054          * @param {Roo.form.Field} this
9055          * @param {Roo.EventObject}  e The event Object
9056          */
9057         keyup : true
9058     });
9059 };
9060
9061 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
9062      /**
9063      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
9064       automatic validation (defaults to "keyup").
9065      */
9066     validationEvent : "keyup",
9067      /**
9068      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
9069      */
9070     validateOnBlur : true,
9071     /**
9072      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
9073      */
9074     validationDelay : 250,
9075      /**
9076      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
9077      */
9078     focusClass : "x-form-focus",  // not needed???
9079     
9080        
9081     /**
9082      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9083      */
9084     invalidClass : "has-warning",
9085     
9086     /**
9087      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9088      */
9089     validClass : "has-success",
9090     
9091     /**
9092      * @cfg {Boolean} hasFeedback (true|false) default true
9093      */
9094     hasFeedback : true,
9095     
9096     /**
9097      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9098      */
9099     invalidFeedbackClass : "glyphicon-warning-sign",
9100     
9101     /**
9102      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9103      */
9104     validFeedbackClass : "glyphicon-ok",
9105     
9106     /**
9107      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
9108      */
9109     selectOnFocus : false,
9110     
9111      /**
9112      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
9113      */
9114     maskRe : null,
9115        /**
9116      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
9117      */
9118     vtype : null,
9119     
9120       /**
9121      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
9122      */
9123     disableKeyFilter : false,
9124     
9125        /**
9126      * @cfg {Boolean} disabled True to disable the field (defaults to false).
9127      */
9128     disabled : false,
9129      /**
9130      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
9131      */
9132     allowBlank : true,
9133     /**
9134      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9135      */
9136     blankText : "Please complete this mandatory field",
9137     
9138      /**
9139      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9140      */
9141     minLength : 0,
9142     /**
9143      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9144      */
9145     maxLength : Number.MAX_VALUE,
9146     /**
9147      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9148      */
9149     minLengthText : "The minimum length for this field is {0}",
9150     /**
9151      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9152      */
9153     maxLengthText : "The maximum length for this field is {0}",
9154   
9155     
9156     /**
9157      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9158      * If available, this function will be called only after the basic validators all return true, and will be passed the
9159      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9160      */
9161     validator : null,
9162     /**
9163      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9164      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9165      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
9166      */
9167     regex : null,
9168     /**
9169      * @cfg {String} regexText -- Depricated - use Invalid Text
9170      */
9171     regexText : "",
9172     
9173     /**
9174      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9175      */
9176     invalidText : "",
9177     
9178     
9179     
9180     autocomplete: false,
9181     
9182     
9183     fieldLabel : '',
9184     inputType : 'text',
9185     
9186     name : false,
9187     placeholder: false,
9188     before : false,
9189     after : false,
9190     size : false,
9191     hasFocus : false,
9192     preventMark: false,
9193     isFormField : true,
9194     value : '',
9195     labelWidth : 2,
9196     labelAlign : false,
9197     readOnly : false,
9198     align : false,
9199     formatedValue : false,
9200     forceFeedback : false,
9201     
9202     indicatorpos : 'left',
9203     
9204     labellg : 0,
9205     labelmd : 0,
9206     labelsm : 0,
9207     labelxs : 0,
9208     
9209     capture : '',
9210     accept : '',
9211     
9212     parentLabelAlign : function()
9213     {
9214         var parent = this;
9215         while (parent.parent()) {
9216             parent = parent.parent();
9217             if (typeof(parent.labelAlign) !='undefined') {
9218                 return parent.labelAlign;
9219             }
9220         }
9221         return 'left';
9222         
9223     },
9224     
9225     getAutoCreate : function()
9226     {
9227         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9228         
9229         var id = Roo.id();
9230         
9231         var cfg = {};
9232         
9233         if(this.inputType != 'hidden'){
9234             cfg.cls = 'form-group' //input-group
9235         }
9236         
9237         var input =  {
9238             tag: 'input',
9239             id : id,
9240             type : this.inputType,
9241             value : this.value,
9242             cls : 'form-control',
9243             placeholder : this.placeholder || '',
9244             autocomplete : this.autocomplete || 'new-password'
9245         };
9246         
9247         if(this.capture.length){
9248             input.capture = this.capture;
9249         }
9250         
9251         if(this.accept.length){
9252             input.accept = this.accept + "/*";
9253         }
9254         
9255         if(this.align){
9256             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9257         }
9258         
9259         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9260             input.maxLength = this.maxLength;
9261         }
9262         
9263         if (this.disabled) {
9264             input.disabled=true;
9265         }
9266         
9267         if (this.readOnly) {
9268             input.readonly=true;
9269         }
9270         
9271         if (this.name) {
9272             input.name = this.name;
9273         }
9274         
9275         if (this.size) {
9276             input.cls += ' input-' + this.size;
9277         }
9278         
9279         var settings=this;
9280         ['xs','sm','md','lg'].map(function(size){
9281             if (settings[size]) {
9282                 cfg.cls += ' col-' + size + '-' + settings[size];
9283             }
9284         });
9285         
9286         var inputblock = input;
9287         
9288         var feedback = {
9289             tag: 'span',
9290             cls: 'glyphicon form-control-feedback'
9291         };
9292             
9293         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9294             
9295             inputblock = {
9296                 cls : 'has-feedback',
9297                 cn :  [
9298                     input,
9299                     feedback
9300                 ] 
9301             };  
9302         }
9303         
9304         if (this.before || this.after) {
9305             
9306             inputblock = {
9307                 cls : 'input-group',
9308                 cn :  [] 
9309             };
9310             
9311             if (this.before && typeof(this.before) == 'string') {
9312                 
9313                 inputblock.cn.push({
9314                     tag :'span',
9315                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9316                     html : this.before
9317                 });
9318             }
9319             if (this.before && typeof(this.before) == 'object') {
9320                 this.before = Roo.factory(this.before);
9321                 
9322                 inputblock.cn.push({
9323                     tag :'span',
9324                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9325                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9326                 });
9327             }
9328             
9329             inputblock.cn.push(input);
9330             
9331             if (this.after && typeof(this.after) == 'string') {
9332                 inputblock.cn.push({
9333                     tag :'span',
9334                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9335                     html : this.after
9336                 });
9337             }
9338             if (this.after && typeof(this.after) == 'object') {
9339                 this.after = Roo.factory(this.after);
9340                 
9341                 inputblock.cn.push({
9342                     tag :'span',
9343                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
9344                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9345                 });
9346             }
9347             
9348             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9349                 inputblock.cls += ' has-feedback';
9350                 inputblock.cn.push(feedback);
9351             }
9352         };
9353         var indicator = {
9354             tag : 'i',
9355             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9356             tooltip : 'This field is required'
9357         };
9358         if (Roo.bootstrap.version == 4) {
9359             indicator = {
9360                 tag : 'i',
9361                 style : 'display-none'
9362             };
9363         }
9364         if (align ==='left' && this.fieldLabel.length) {
9365             
9366             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
9367             
9368             cfg.cn = [
9369                 indicator,
9370                 {
9371                     tag: 'label',
9372                     'for' :  id,
9373                     cls : 'control-label col-form-label',
9374                     html : this.fieldLabel
9375
9376                 },
9377                 {
9378                     cls : "", 
9379                     cn: [
9380                         inputblock
9381                     ]
9382                 }
9383             ];
9384             
9385             var labelCfg = cfg.cn[1];
9386             var contentCfg = cfg.cn[2];
9387             
9388             if(this.indicatorpos == 'right'){
9389                 cfg.cn = [
9390                     {
9391                         tag: 'label',
9392                         'for' :  id,
9393                         cls : 'control-label col-form-label',
9394                         cn : [
9395                             {
9396                                 tag : 'span',
9397                                 html : this.fieldLabel
9398                             },
9399                             indicator
9400                         ]
9401                     },
9402                     {
9403                         cls : "",
9404                         cn: [
9405                             inputblock
9406                         ]
9407                     }
9408
9409                 ];
9410                 
9411                 labelCfg = cfg.cn[0];
9412                 contentCfg = cfg.cn[1];
9413             
9414             }
9415             
9416             if(this.labelWidth > 12){
9417                 labelCfg.style = "width: " + this.labelWidth + 'px';
9418             }
9419             
9420             if(this.labelWidth < 13 && this.labelmd == 0){
9421                 this.labelmd = this.labelWidth;
9422             }
9423             
9424             if(this.labellg > 0){
9425                 labelCfg.cls += ' col-lg-' + this.labellg;
9426                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9427             }
9428             
9429             if(this.labelmd > 0){
9430                 labelCfg.cls += ' col-md-' + this.labelmd;
9431                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9432             }
9433             
9434             if(this.labelsm > 0){
9435                 labelCfg.cls += ' col-sm-' + this.labelsm;
9436                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9437             }
9438             
9439             if(this.labelxs > 0){
9440                 labelCfg.cls += ' col-xs-' + this.labelxs;
9441                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9442             }
9443             
9444             
9445         } else if ( this.fieldLabel.length) {
9446                 
9447             cfg.cn = [
9448                 {
9449                     tag : 'i',
9450                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9451                     tooltip : 'This field is required'
9452                 },
9453                 {
9454                     tag: 'label',
9455                    //cls : 'input-group-addon',
9456                     html : this.fieldLabel
9457
9458                 },
9459
9460                inputblock
9461
9462            ];
9463            
9464            if(this.indicatorpos == 'right'){
9465                 
9466                 cfg.cn = [
9467                     {
9468                         tag: 'label',
9469                        //cls : 'input-group-addon',
9470                         html : this.fieldLabel
9471
9472                     },
9473                     {
9474                         tag : 'i',
9475                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9476                         tooltip : 'This field is required'
9477                     },
9478
9479                    inputblock
9480
9481                ];
9482
9483             }
9484
9485         } else {
9486             
9487             cfg.cn = [
9488
9489                     inputblock
9490
9491             ];
9492                 
9493                 
9494         };
9495         
9496         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9497            cfg.cls += ' navbar-form';
9498         }
9499         
9500         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
9501             // on BS4 we do this only if not form 
9502             cfg.cls += ' navbar-form';
9503             cfg.tag = 'li';
9504         }
9505         
9506         return cfg;
9507         
9508     },
9509     /**
9510      * return the real input element.
9511      */
9512     inputEl: function ()
9513     {
9514         return this.el.select('input.form-control',true).first();
9515     },
9516     
9517     tooltipEl : function()
9518     {
9519         return this.inputEl();
9520     },
9521     
9522     indicatorEl : function()
9523     {
9524         if (Roo.bootstrap.version == 4) {
9525             return false; // not enabled in v4 yet.
9526         }
9527         
9528         var indicator = this.el.select('i.roo-required-indicator',true).first();
9529         
9530         if(!indicator){
9531             return false;
9532         }
9533         
9534         return indicator;
9535         
9536     },
9537     
9538     setDisabled : function(v)
9539     {
9540         var i  = this.inputEl().dom;
9541         if (!v) {
9542             i.removeAttribute('disabled');
9543             return;
9544             
9545         }
9546         i.setAttribute('disabled','true');
9547     },
9548     initEvents : function()
9549     {
9550           
9551         this.inputEl().on("keydown" , this.fireKey,  this);
9552         this.inputEl().on("focus", this.onFocus,  this);
9553         this.inputEl().on("blur", this.onBlur,  this);
9554         
9555         this.inputEl().relayEvent('keyup', this);
9556         
9557         this.indicator = this.indicatorEl();
9558         
9559         if(this.indicator){
9560             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9561         }
9562  
9563         // reference to original value for reset
9564         this.originalValue = this.getValue();
9565         //Roo.form.TextField.superclass.initEvents.call(this);
9566         if(this.validationEvent == 'keyup'){
9567             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9568             this.inputEl().on('keyup', this.filterValidation, this);
9569         }
9570         else if(this.validationEvent !== false){
9571             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9572         }
9573         
9574         if(this.selectOnFocus){
9575             this.on("focus", this.preFocus, this);
9576             
9577         }
9578         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9579             this.inputEl().on("keypress", this.filterKeys, this);
9580         } else {
9581             this.inputEl().relayEvent('keypress', this);
9582         }
9583        /* if(this.grow){
9584             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9585             this.el.on("click", this.autoSize,  this);
9586         }
9587         */
9588         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9589             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9590         }
9591         
9592         if (typeof(this.before) == 'object') {
9593             this.before.render(this.el.select('.roo-input-before',true).first());
9594         }
9595         if (typeof(this.after) == 'object') {
9596             this.after.render(this.el.select('.roo-input-after',true).first());
9597         }
9598         
9599         this.inputEl().on('change', this.onChange, this);
9600         
9601     },
9602     filterValidation : function(e){
9603         if(!e.isNavKeyPress()){
9604             this.validationTask.delay(this.validationDelay);
9605         }
9606     },
9607      /**
9608      * Validates the field value
9609      * @return {Boolean} True if the value is valid, else false
9610      */
9611     validate : function(){
9612         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9613         if(this.disabled || this.validateValue(this.getRawValue())){
9614             this.markValid();
9615             return true;
9616         }
9617         
9618         this.markInvalid();
9619         return false;
9620     },
9621     
9622     
9623     /**
9624      * Validates a value according to the field's validation rules and marks the field as invalid
9625      * if the validation fails
9626      * @param {Mixed} value The value to validate
9627      * @return {Boolean} True if the value is valid, else false
9628      */
9629     validateValue : function(value)
9630     {
9631         if(this.getVisibilityEl().hasClass('hidden')){
9632             return true;
9633         }
9634         
9635         if(value.length < 1)  { // if it's blank
9636             if(this.allowBlank){
9637                 return true;
9638             }
9639             return false;
9640         }
9641         
9642         if(value.length < this.minLength){
9643             return false;
9644         }
9645         if(value.length > this.maxLength){
9646             return false;
9647         }
9648         if(this.vtype){
9649             var vt = Roo.form.VTypes;
9650             if(!vt[this.vtype](value, this)){
9651                 return false;
9652             }
9653         }
9654         if(typeof this.validator == "function"){
9655             var msg = this.validator(value);
9656             if(msg !== true){
9657                 return false;
9658             }
9659             if (typeof(msg) == 'string') {
9660                 this.invalidText = msg;
9661             }
9662         }
9663         
9664         if(this.regex && !this.regex.test(value)){
9665             return false;
9666         }
9667         
9668         return true;
9669     },
9670     
9671      // private
9672     fireKey : function(e){
9673         //Roo.log('field ' + e.getKey());
9674         if(e.isNavKeyPress()){
9675             this.fireEvent("specialkey", this, e);
9676         }
9677     },
9678     focus : function (selectText){
9679         if(this.rendered){
9680             this.inputEl().focus();
9681             if(selectText === true){
9682                 this.inputEl().dom.select();
9683             }
9684         }
9685         return this;
9686     } ,
9687     
9688     onFocus : function(){
9689         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9690            // this.el.addClass(this.focusClass);
9691         }
9692         if(!this.hasFocus){
9693             this.hasFocus = true;
9694             this.startValue = this.getValue();
9695             this.fireEvent("focus", this);
9696         }
9697     },
9698     
9699     beforeBlur : Roo.emptyFn,
9700
9701     
9702     // private
9703     onBlur : function(){
9704         this.beforeBlur();
9705         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9706             //this.el.removeClass(this.focusClass);
9707         }
9708         this.hasFocus = false;
9709         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9710             this.validate();
9711         }
9712         var v = this.getValue();
9713         if(String(v) !== String(this.startValue)){
9714             this.fireEvent('change', this, v, this.startValue);
9715         }
9716         this.fireEvent("blur", this);
9717     },
9718     
9719     onChange : function(e)
9720     {
9721         var v = this.getValue();
9722         if(String(v) !== String(this.startValue)){
9723             this.fireEvent('change', this, v, this.startValue);
9724         }
9725         
9726     },
9727     
9728     /**
9729      * Resets the current field value to the originally loaded value and clears any validation messages
9730      */
9731     reset : function(){
9732         this.setValue(this.originalValue);
9733         this.validate();
9734     },
9735      /**
9736      * Returns the name of the field
9737      * @return {Mixed} name The name field
9738      */
9739     getName: function(){
9740         return this.name;
9741     },
9742      /**
9743      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9744      * @return {Mixed} value The field value
9745      */
9746     getValue : function(){
9747         
9748         var v = this.inputEl().getValue();
9749         
9750         return v;
9751     },
9752     /**
9753      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9754      * @return {Mixed} value The field value
9755      */
9756     getRawValue : function(){
9757         var v = this.inputEl().getValue();
9758         
9759         return v;
9760     },
9761     
9762     /**
9763      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9764      * @param {Mixed} value The value to set
9765      */
9766     setRawValue : function(v){
9767         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9768     },
9769     
9770     selectText : function(start, end){
9771         var v = this.getRawValue();
9772         if(v.length > 0){
9773             start = start === undefined ? 0 : start;
9774             end = end === undefined ? v.length : end;
9775             var d = this.inputEl().dom;
9776             if(d.setSelectionRange){
9777                 d.setSelectionRange(start, end);
9778             }else if(d.createTextRange){
9779                 var range = d.createTextRange();
9780                 range.moveStart("character", start);
9781                 range.moveEnd("character", v.length-end);
9782                 range.select();
9783             }
9784         }
9785     },
9786     
9787     /**
9788      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9789      * @param {Mixed} value The value to set
9790      */
9791     setValue : function(v){
9792         this.value = v;
9793         if(this.rendered){
9794             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9795             this.validate();
9796         }
9797     },
9798     
9799     /*
9800     processValue : function(value){
9801         if(this.stripCharsRe){
9802             var newValue = value.replace(this.stripCharsRe, '');
9803             if(newValue !== value){
9804                 this.setRawValue(newValue);
9805                 return newValue;
9806             }
9807         }
9808         return value;
9809     },
9810   */
9811     preFocus : function(){
9812         
9813         if(this.selectOnFocus){
9814             this.inputEl().dom.select();
9815         }
9816     },
9817     filterKeys : function(e){
9818         var k = e.getKey();
9819         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9820             return;
9821         }
9822         var c = e.getCharCode(), cc = String.fromCharCode(c);
9823         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9824             return;
9825         }
9826         if(!this.maskRe.test(cc)){
9827             e.stopEvent();
9828         }
9829     },
9830      /**
9831      * Clear any invalid styles/messages for this field
9832      */
9833     clearInvalid : function(){
9834         
9835         if(!this.el || this.preventMark){ // not rendered
9836             return;
9837         }
9838         
9839         
9840         this.el.removeClass([this.invalidClass, 'is-invalid']);
9841         
9842         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9843             
9844             var feedback = this.el.select('.form-control-feedback', true).first();
9845             
9846             if(feedback){
9847                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9848             }
9849             
9850         }
9851         
9852         if(this.indicator){
9853             this.indicator.removeClass('visible');
9854             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9855         }
9856         
9857         this.fireEvent('valid', this);
9858     },
9859     
9860      /**
9861      * Mark this field as valid
9862      */
9863     markValid : function()
9864     {
9865         if(!this.el  || this.preventMark){ // not rendered...
9866             return;
9867         }
9868         
9869         this.el.removeClass([this.invalidClass, this.validClass]);
9870         this.inputEl().removeClass(['is-valid', 'is-invalid']);
9871
9872         var feedback = this.el.select('.form-control-feedback', true).first();
9873             
9874         if(feedback){
9875             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9876         }
9877         
9878         if(this.indicator){
9879             this.indicator.removeClass('visible');
9880             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9881         }
9882         
9883         if(this.disabled){
9884             return;
9885         }
9886         
9887         if(this.allowBlank && !this.getRawValue().length){
9888             return;
9889         }
9890         if (Roo.bootstrap.version == 3) {
9891             this.el.addClass(this.validClass);
9892         } else {
9893             this.inputEl().addClass('is-valid');
9894         }
9895
9896         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9897             
9898             var feedback = this.el.select('.form-control-feedback', true).first();
9899             
9900             if(feedback){
9901                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9902                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9903             }
9904             
9905         }
9906         
9907         this.fireEvent('valid', this);
9908     },
9909     
9910      /**
9911      * Mark this field as invalid
9912      * @param {String} msg The validation message
9913      */
9914     markInvalid : function(msg)
9915     {
9916         if(!this.el  || this.preventMark){ // not rendered
9917             return;
9918         }
9919         
9920         this.el.removeClass([this.invalidClass, this.validClass]);
9921         this.inputEl().removeClass(['is-valid', 'is-invalid']);
9922         
9923         var feedback = this.el.select('.form-control-feedback', true).first();
9924             
9925         if(feedback){
9926             this.el.select('.form-control-feedback', true).first().removeClass(
9927                     [this.invalidFeedbackClass, this.validFeedbackClass]);
9928         }
9929
9930         if(this.disabled){
9931             return;
9932         }
9933         
9934         if(this.allowBlank && !this.getRawValue().length){
9935             return;
9936         }
9937         
9938         if(this.indicator){
9939             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9940             this.indicator.addClass('visible');
9941         }
9942         if (Roo.bootstrap.version == 3) {
9943             this.el.addClass(this.invalidClass);
9944         } else {
9945             this.inputEl().addClass('is-invalid');
9946         }
9947         
9948         
9949         
9950         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9951             
9952             var feedback = this.el.select('.form-control-feedback', true).first();
9953             
9954             if(feedback){
9955                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9956                 
9957                 if(this.getValue().length || this.forceFeedback){
9958                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9959                 }
9960                 
9961             }
9962             
9963         }
9964         
9965         this.fireEvent('invalid', this, msg);
9966     },
9967     // private
9968     SafariOnKeyDown : function(event)
9969     {
9970         // this is a workaround for a password hang bug on chrome/ webkit.
9971         if (this.inputEl().dom.type != 'password') {
9972             return;
9973         }
9974         
9975         var isSelectAll = false;
9976         
9977         if(this.inputEl().dom.selectionEnd > 0){
9978             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9979         }
9980         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9981             event.preventDefault();
9982             this.setValue('');
9983             return;
9984         }
9985         
9986         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9987             
9988             event.preventDefault();
9989             // this is very hacky as keydown always get's upper case.
9990             //
9991             var cc = String.fromCharCode(event.getCharCode());
9992             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9993             
9994         }
9995     },
9996     adjustWidth : function(tag, w){
9997         tag = tag.toLowerCase();
9998         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9999             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
10000                 if(tag == 'input'){
10001                     return w + 2;
10002                 }
10003                 if(tag == 'textarea'){
10004                     return w-2;
10005                 }
10006             }else if(Roo.isOpera){
10007                 if(tag == 'input'){
10008                     return w + 2;
10009                 }
10010                 if(tag == 'textarea'){
10011                     return w-2;
10012                 }
10013             }
10014         }
10015         return w;
10016     },
10017     
10018     setFieldLabel : function(v)
10019     {
10020         if(!this.rendered){
10021             return;
10022         }
10023         
10024         if(this.indicatorEl()){
10025             var ar = this.el.select('label > span',true);
10026             
10027             if (ar.elements.length) {
10028                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10029                 this.fieldLabel = v;
10030                 return;
10031             }
10032             
10033             var br = this.el.select('label',true);
10034             
10035             if(br.elements.length) {
10036                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10037                 this.fieldLabel = v;
10038                 return;
10039             }
10040             
10041             Roo.log('Cannot Found any of label > span || label in input');
10042             return;
10043         }
10044         
10045         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10046         this.fieldLabel = v;
10047         
10048         
10049     }
10050 });
10051
10052  
10053 /*
10054  * - LGPL
10055  *
10056  * Input
10057  * 
10058  */
10059
10060 /**
10061  * @class Roo.bootstrap.TextArea
10062  * @extends Roo.bootstrap.Input
10063  * Bootstrap TextArea class
10064  * @cfg {Number} cols Specifies the visible width of a text area
10065  * @cfg {Number} rows Specifies the visible number of lines in a text area
10066  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
10067  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
10068  * @cfg {string} html text
10069  * 
10070  * @constructor
10071  * Create a new TextArea
10072  * @param {Object} config The config object
10073  */
10074
10075 Roo.bootstrap.TextArea = function(config){
10076     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
10077    
10078 };
10079
10080 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
10081      
10082     cols : false,
10083     rows : 5,
10084     readOnly : false,
10085     warp : 'soft',
10086     resize : false,
10087     value: false,
10088     html: false,
10089     
10090     getAutoCreate : function(){
10091         
10092         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10093         
10094         var id = Roo.id();
10095         
10096         var cfg = {};
10097         
10098         if(this.inputType != 'hidden'){
10099             cfg.cls = 'form-group' //input-group
10100         }
10101         
10102         var input =  {
10103             tag: 'textarea',
10104             id : id,
10105             warp : this.warp,
10106             rows : this.rows,
10107             value : this.value || '',
10108             html: this.html || '',
10109             cls : 'form-control',
10110             placeholder : this.placeholder || '' 
10111             
10112         };
10113         
10114         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10115             input.maxLength = this.maxLength;
10116         }
10117         
10118         if(this.resize){
10119             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
10120         }
10121         
10122         if(this.cols){
10123             input.cols = this.cols;
10124         }
10125         
10126         if (this.readOnly) {
10127             input.readonly = true;
10128         }
10129         
10130         if (this.name) {
10131             input.name = this.name;
10132         }
10133         
10134         if (this.size) {
10135             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
10136         }
10137         
10138         var settings=this;
10139         ['xs','sm','md','lg'].map(function(size){
10140             if (settings[size]) {
10141                 cfg.cls += ' col-' + size + '-' + settings[size];
10142             }
10143         });
10144         
10145         var inputblock = input;
10146         
10147         if(this.hasFeedback && !this.allowBlank){
10148             
10149             var feedback = {
10150                 tag: 'span',
10151                 cls: 'glyphicon form-control-feedback'
10152             };
10153
10154             inputblock = {
10155                 cls : 'has-feedback',
10156                 cn :  [
10157                     input,
10158                     feedback
10159                 ] 
10160             };  
10161         }
10162         
10163         
10164         if (this.before || this.after) {
10165             
10166             inputblock = {
10167                 cls : 'input-group',
10168                 cn :  [] 
10169             };
10170             if (this.before) {
10171                 inputblock.cn.push({
10172                     tag :'span',
10173                     cls : 'input-group-addon',
10174                     html : this.before
10175                 });
10176             }
10177             
10178             inputblock.cn.push(input);
10179             
10180             if(this.hasFeedback && !this.allowBlank){
10181                 inputblock.cls += ' has-feedback';
10182                 inputblock.cn.push(feedback);
10183             }
10184             
10185             if (this.after) {
10186                 inputblock.cn.push({
10187                     tag :'span',
10188                     cls : 'input-group-addon',
10189                     html : this.after
10190                 });
10191             }
10192             
10193         }
10194         
10195         if (align ==='left' && this.fieldLabel.length) {
10196             cfg.cn = [
10197                 {
10198                     tag: 'label',
10199                     'for' :  id,
10200                     cls : 'control-label',
10201                     html : this.fieldLabel
10202                 },
10203                 {
10204                     cls : "",
10205                     cn: [
10206                         inputblock
10207                     ]
10208                 }
10209
10210             ];
10211             
10212             if(this.labelWidth > 12){
10213                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10214             }
10215
10216             if(this.labelWidth < 13 && this.labelmd == 0){
10217                 this.labelmd = this.labelWidth;
10218             }
10219
10220             if(this.labellg > 0){
10221                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10222                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10223             }
10224
10225             if(this.labelmd > 0){
10226                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10227                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10228             }
10229
10230             if(this.labelsm > 0){
10231                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10232                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10233             }
10234
10235             if(this.labelxs > 0){
10236                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10237                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10238             }
10239             
10240         } else if ( this.fieldLabel.length) {
10241             cfg.cn = [
10242
10243                {
10244                    tag: 'label',
10245                    //cls : 'input-group-addon',
10246                    html : this.fieldLabel
10247
10248                },
10249
10250                inputblock
10251
10252            ];
10253
10254         } else {
10255
10256             cfg.cn = [
10257
10258                 inputblock
10259
10260             ];
10261                 
10262         }
10263         
10264         if (this.disabled) {
10265             input.disabled=true;
10266         }
10267         
10268         return cfg;
10269         
10270     },
10271     /**
10272      * return the real textarea element.
10273      */
10274     inputEl: function ()
10275     {
10276         return this.el.select('textarea.form-control',true).first();
10277     },
10278     
10279     /**
10280      * Clear any invalid styles/messages for this field
10281      */
10282     clearInvalid : function()
10283     {
10284         
10285         if(!this.el || this.preventMark){ // not rendered
10286             return;
10287         }
10288         
10289         var label = this.el.select('label', true).first();
10290         var icon = this.el.select('i.fa-star', true).first();
10291         
10292         if(label && icon){
10293             icon.remove();
10294         }
10295         this.el.removeClass( this.validClass);
10296         this.inputEl().removeClass('is-invalid');
10297          
10298         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10299             
10300             var feedback = this.el.select('.form-control-feedback', true).first();
10301             
10302             if(feedback){
10303                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10304             }
10305             
10306         }
10307         
10308         this.fireEvent('valid', this);
10309     },
10310     
10311      /**
10312      * Mark this field as valid
10313      */
10314     markValid : function()
10315     {
10316         if(!this.el  || this.preventMark){ // not rendered
10317             return;
10318         }
10319         
10320         this.el.removeClass([this.invalidClass, this.validClass]);
10321         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10322         
10323         var feedback = this.el.select('.form-control-feedback', true).first();
10324             
10325         if(feedback){
10326             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10327         }
10328
10329         if(this.disabled || this.allowBlank){
10330             return;
10331         }
10332         
10333         var label = this.el.select('label', true).first();
10334         var icon = this.el.select('i.fa-star', true).first();
10335         
10336         if(label && icon){
10337             icon.remove();
10338         }
10339         if (Roo.bootstrap.version == 3) {
10340             this.el.addClass(this.validClass);
10341         } else {
10342             this.inputEl().addClass('is-valid');
10343         }
10344         
10345         
10346         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10347             
10348             var feedback = this.el.select('.form-control-feedback', true).first();
10349             
10350             if(feedback){
10351                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10352                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10353             }
10354             
10355         }
10356         
10357         this.fireEvent('valid', this);
10358     },
10359     
10360      /**
10361      * Mark this field as invalid
10362      * @param {String} msg The validation message
10363      */
10364     markInvalid : function(msg)
10365     {
10366         if(!this.el  || this.preventMark){ // not rendered
10367             return;
10368         }
10369         
10370         this.el.removeClass([this.invalidClass, this.validClass]);
10371         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10372         
10373         var feedback = this.el.select('.form-control-feedback', true).first();
10374             
10375         if(feedback){
10376             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10377         }
10378
10379         if(this.disabled || this.allowBlank){
10380             return;
10381         }
10382         
10383         var label = this.el.select('label', true).first();
10384         var icon = this.el.select('i.fa-star', true).first();
10385         
10386         if(!this.getValue().length && label && !icon){
10387             this.el.createChild({
10388                 tag : 'i',
10389                 cls : 'text-danger fa fa-lg fa-star',
10390                 tooltip : 'This field is required',
10391                 style : 'margin-right:5px;'
10392             }, label, true);
10393         }
10394         
10395         if (Roo.bootstrap.version == 3) {
10396             this.el.addClass(this.invalidClass);
10397         } else {
10398             this.inputEl().addClass('is-invalid');
10399         }
10400         
10401         // fixme ... this may be depricated need to test..
10402         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10403             
10404             var feedback = this.el.select('.form-control-feedback', true).first();
10405             
10406             if(feedback){
10407                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10408                 
10409                 if(this.getValue().length || this.forceFeedback){
10410                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10411                 }
10412                 
10413             }
10414             
10415         }
10416         
10417         this.fireEvent('invalid', this, msg);
10418     }
10419 });
10420
10421  
10422 /*
10423  * - LGPL
10424  *
10425  * trigger field - base class for combo..
10426  * 
10427  */
10428  
10429 /**
10430  * @class Roo.bootstrap.TriggerField
10431  * @extends Roo.bootstrap.Input
10432  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10433  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10434  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10435  * for which you can provide a custom implementation.  For example:
10436  * <pre><code>
10437 var trigger = new Roo.bootstrap.TriggerField();
10438 trigger.onTriggerClick = myTriggerFn;
10439 trigger.applyTo('my-field');
10440 </code></pre>
10441  *
10442  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10443  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10444  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10445  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10446  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10447
10448  * @constructor
10449  * Create a new TriggerField.
10450  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10451  * to the base TextField)
10452  */
10453 Roo.bootstrap.TriggerField = function(config){
10454     this.mimicing = false;
10455     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10456 };
10457
10458 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10459     /**
10460      * @cfg {String} triggerClass A CSS class to apply to the trigger
10461      */
10462      /**
10463      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10464      */
10465     hideTrigger:false,
10466
10467     /**
10468      * @cfg {Boolean} removable (true|false) special filter default false
10469      */
10470     removable : false,
10471     
10472     /** @cfg {Boolean} grow @hide */
10473     /** @cfg {Number} growMin @hide */
10474     /** @cfg {Number} growMax @hide */
10475
10476     /**
10477      * @hide 
10478      * @method
10479      */
10480     autoSize: Roo.emptyFn,
10481     // private
10482     monitorTab : true,
10483     // private
10484     deferHeight : true,
10485
10486     
10487     actionMode : 'wrap',
10488     
10489     caret : false,
10490     
10491     
10492     getAutoCreate : function(){
10493        
10494         var align = this.labelAlign || this.parentLabelAlign();
10495         
10496         var id = Roo.id();
10497         
10498         var cfg = {
10499             cls: 'form-group' //input-group
10500         };
10501         
10502         
10503         var input =  {
10504             tag: 'input',
10505             id : id,
10506             type : this.inputType,
10507             cls : 'form-control',
10508             autocomplete: 'new-password',
10509             placeholder : this.placeholder || '' 
10510             
10511         };
10512         if (this.name) {
10513             input.name = this.name;
10514         }
10515         if (this.size) {
10516             input.cls += ' input-' + this.size;
10517         }
10518         
10519         if (this.disabled) {
10520             input.disabled=true;
10521         }
10522         
10523         var inputblock = input;
10524         
10525         if(this.hasFeedback && !this.allowBlank){
10526             
10527             var feedback = {
10528                 tag: 'span',
10529                 cls: 'glyphicon form-control-feedback'
10530             };
10531             
10532             if(this.removable && !this.editable && !this.tickable){
10533                 inputblock = {
10534                     cls : 'has-feedback',
10535                     cn :  [
10536                         inputblock,
10537                         {
10538                             tag: 'button',
10539                             html : 'x',
10540                             cls : 'roo-combo-removable-btn close'
10541                         },
10542                         feedback
10543                     ] 
10544                 };
10545             } else {
10546                 inputblock = {
10547                     cls : 'has-feedback',
10548                     cn :  [
10549                         inputblock,
10550                         feedback
10551                     ] 
10552                 };
10553             }
10554
10555         } else {
10556             if(this.removable && !this.editable && !this.tickable){
10557                 inputblock = {
10558                     cls : 'roo-removable',
10559                     cn :  [
10560                         inputblock,
10561                         {
10562                             tag: 'button',
10563                             html : 'x',
10564                             cls : 'roo-combo-removable-btn close'
10565                         }
10566                     ] 
10567                 };
10568             }
10569         }
10570         
10571         if (this.before || this.after) {
10572             
10573             inputblock = {
10574                 cls : 'input-group',
10575                 cn :  [] 
10576             };
10577             if (this.before) {
10578                 inputblock.cn.push({
10579                     tag :'span',
10580                     cls : 'input-group-addon input-group-prepend input-group-text',
10581                     html : this.before
10582                 });
10583             }
10584             
10585             inputblock.cn.push(input);
10586             
10587             if(this.hasFeedback && !this.allowBlank){
10588                 inputblock.cls += ' has-feedback';
10589                 inputblock.cn.push(feedback);
10590             }
10591             
10592             if (this.after) {
10593                 inputblock.cn.push({
10594                     tag :'span',
10595                     cls : 'input-group-addon input-group-append input-group-text',
10596                     html : this.after
10597                 });
10598             }
10599             
10600         };
10601         
10602       
10603         
10604         var ibwrap = inputblock;
10605         
10606         if(this.multiple){
10607             ibwrap = {
10608                 tag: 'ul',
10609                 cls: 'roo-select2-choices',
10610                 cn:[
10611                     {
10612                         tag: 'li',
10613                         cls: 'roo-select2-search-field',
10614                         cn: [
10615
10616                             inputblock
10617                         ]
10618                     }
10619                 ]
10620             };
10621                 
10622         }
10623         
10624         var combobox = {
10625             cls: 'roo-select2-container input-group',
10626             cn: [
10627                  {
10628                     tag: 'input',
10629                     type : 'hidden',
10630                     cls: 'form-hidden-field'
10631                 },
10632                 ibwrap
10633             ]
10634         };
10635         
10636         if(!this.multiple && this.showToggleBtn){
10637             
10638             var caret = {
10639                         tag: 'span',
10640                         cls: 'caret'
10641              };
10642             if (this.caret != false) {
10643                 caret = {
10644                      tag: 'i',
10645                      cls: 'fa fa-' + this.caret
10646                 };
10647                 
10648             }
10649             
10650             combobox.cn.push({
10651                 tag :'span',
10652                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10653                 cn : [
10654                     caret,
10655                     {
10656                         tag: 'span',
10657                         cls: 'combobox-clear',
10658                         cn  : [
10659                             {
10660                                 tag : 'i',
10661                                 cls: 'icon-remove'
10662                             }
10663                         ]
10664                     }
10665                 ]
10666
10667             })
10668         }
10669         
10670         if(this.multiple){
10671             combobox.cls += ' roo-select2-container-multi';
10672         }
10673          var indicator = {
10674             tag : 'i',
10675             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10676             tooltip : 'This field is required'
10677         };
10678         if (Roo.bootstrap.version == 4) {
10679             indicator = {
10680                 tag : 'i',
10681                 style : 'display:none'
10682             };
10683         }
10684         
10685         
10686         if (align ==='left' && this.fieldLabel.length) {
10687             
10688             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10689
10690             cfg.cn = [
10691                 indicator,
10692                 {
10693                     tag: 'label',
10694                     'for' :  id,
10695                     cls : 'control-label',
10696                     html : this.fieldLabel
10697
10698                 },
10699                 {
10700                     cls : "", 
10701                     cn: [
10702                         combobox
10703                     ]
10704                 }
10705
10706             ];
10707             
10708             var labelCfg = cfg.cn[1];
10709             var contentCfg = cfg.cn[2];
10710             
10711             if(this.indicatorpos == 'right'){
10712                 cfg.cn = [
10713                     {
10714                         tag: 'label',
10715                         'for' :  id,
10716                         cls : 'control-label',
10717                         cn : [
10718                             {
10719                                 tag : 'span',
10720                                 html : this.fieldLabel
10721                             },
10722                             indicator
10723                         ]
10724                     },
10725                     {
10726                         cls : "", 
10727                         cn: [
10728                             combobox
10729                         ]
10730                     }
10731
10732                 ];
10733                 
10734                 labelCfg = cfg.cn[0];
10735                 contentCfg = cfg.cn[1];
10736             }
10737             
10738             if(this.labelWidth > 12){
10739                 labelCfg.style = "width: " + this.labelWidth + 'px';
10740             }
10741             
10742             if(this.labelWidth < 13 && this.labelmd == 0){
10743                 this.labelmd = this.labelWidth;
10744             }
10745             
10746             if(this.labellg > 0){
10747                 labelCfg.cls += ' col-lg-' + this.labellg;
10748                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10749             }
10750             
10751             if(this.labelmd > 0){
10752                 labelCfg.cls += ' col-md-' + this.labelmd;
10753                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10754             }
10755             
10756             if(this.labelsm > 0){
10757                 labelCfg.cls += ' col-sm-' + this.labelsm;
10758                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10759             }
10760             
10761             if(this.labelxs > 0){
10762                 labelCfg.cls += ' col-xs-' + this.labelxs;
10763                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10764             }
10765             
10766         } else if ( this.fieldLabel.length) {
10767 //                Roo.log(" label");
10768             cfg.cn = [
10769                 indicator,
10770                {
10771                    tag: 'label',
10772                    //cls : 'input-group-addon',
10773                    html : this.fieldLabel
10774
10775                },
10776
10777                combobox
10778
10779             ];
10780             
10781             if(this.indicatorpos == 'right'){
10782                 
10783                 cfg.cn = [
10784                     {
10785                        tag: 'label',
10786                        cn : [
10787                            {
10788                                tag : 'span',
10789                                html : this.fieldLabel
10790                            },
10791                            indicator
10792                        ]
10793
10794                     },
10795                     combobox
10796
10797                 ];
10798
10799             }
10800
10801         } else {
10802             
10803 //                Roo.log(" no label && no align");
10804                 cfg = combobox
10805                      
10806                 
10807         }
10808         
10809         var settings=this;
10810         ['xs','sm','md','lg'].map(function(size){
10811             if (settings[size]) {
10812                 cfg.cls += ' col-' + size + '-' + settings[size];
10813             }
10814         });
10815         
10816         return cfg;
10817         
10818     },
10819     
10820     
10821     
10822     // private
10823     onResize : function(w, h){
10824 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10825 //        if(typeof w == 'number'){
10826 //            var x = w - this.trigger.getWidth();
10827 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10828 //            this.trigger.setStyle('left', x+'px');
10829 //        }
10830     },
10831
10832     // private
10833     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10834
10835     // private
10836     getResizeEl : function(){
10837         return this.inputEl();
10838     },
10839
10840     // private
10841     getPositionEl : function(){
10842         return this.inputEl();
10843     },
10844
10845     // private
10846     alignErrorIcon : function(){
10847         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10848     },
10849
10850     // private
10851     initEvents : function(){
10852         
10853         this.createList();
10854         
10855         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10856         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10857         if(!this.multiple && this.showToggleBtn){
10858             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10859             if(this.hideTrigger){
10860                 this.trigger.setDisplayed(false);
10861             }
10862             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10863         }
10864         
10865         if(this.multiple){
10866             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10867         }
10868         
10869         if(this.removable && !this.editable && !this.tickable){
10870             var close = this.closeTriggerEl();
10871             
10872             if(close){
10873                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10874                 close.on('click', this.removeBtnClick, this, close);
10875             }
10876         }
10877         
10878         //this.trigger.addClassOnOver('x-form-trigger-over');
10879         //this.trigger.addClassOnClick('x-form-trigger-click');
10880         
10881         //if(!this.width){
10882         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10883         //}
10884     },
10885     
10886     closeTriggerEl : function()
10887     {
10888         var close = this.el.select('.roo-combo-removable-btn', true).first();
10889         return close ? close : false;
10890     },
10891     
10892     removeBtnClick : function(e, h, el)
10893     {
10894         e.preventDefault();
10895         
10896         if(this.fireEvent("remove", this) !== false){
10897             this.reset();
10898             this.fireEvent("afterremove", this)
10899         }
10900     },
10901     
10902     createList : function()
10903     {
10904         this.list = Roo.get(document.body).createChild({
10905             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10906             cls: 'typeahead typeahead-long dropdown-menu',
10907             style: 'display:none'
10908         });
10909         
10910         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10911         
10912     },
10913
10914     // private
10915     initTrigger : function(){
10916        
10917     },
10918
10919     // private
10920     onDestroy : function(){
10921         if(this.trigger){
10922             this.trigger.removeAllListeners();
10923           //  this.trigger.remove();
10924         }
10925         //if(this.wrap){
10926         //    this.wrap.remove();
10927         //}
10928         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10929     },
10930
10931     // private
10932     onFocus : function(){
10933         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10934         /*
10935         if(!this.mimicing){
10936             this.wrap.addClass('x-trigger-wrap-focus');
10937             this.mimicing = true;
10938             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10939             if(this.monitorTab){
10940                 this.el.on("keydown", this.checkTab, this);
10941             }
10942         }
10943         */
10944     },
10945
10946     // private
10947     checkTab : function(e){
10948         if(e.getKey() == e.TAB){
10949             this.triggerBlur();
10950         }
10951     },
10952
10953     // private
10954     onBlur : function(){
10955         // do nothing
10956     },
10957
10958     // private
10959     mimicBlur : function(e, t){
10960         /*
10961         if(!this.wrap.contains(t) && this.validateBlur()){
10962             this.triggerBlur();
10963         }
10964         */
10965     },
10966
10967     // private
10968     triggerBlur : function(){
10969         this.mimicing = false;
10970         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10971         if(this.monitorTab){
10972             this.el.un("keydown", this.checkTab, this);
10973         }
10974         //this.wrap.removeClass('x-trigger-wrap-focus');
10975         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10976     },
10977
10978     // private
10979     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10980     validateBlur : function(e, t){
10981         return true;
10982     },
10983
10984     // private
10985     onDisable : function(){
10986         this.inputEl().dom.disabled = true;
10987         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10988         //if(this.wrap){
10989         //    this.wrap.addClass('x-item-disabled');
10990         //}
10991     },
10992
10993     // private
10994     onEnable : function(){
10995         this.inputEl().dom.disabled = false;
10996         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10997         //if(this.wrap){
10998         //    this.el.removeClass('x-item-disabled');
10999         //}
11000     },
11001
11002     // private
11003     onShow : function(){
11004         var ae = this.getActionEl();
11005         
11006         if(ae){
11007             ae.dom.style.display = '';
11008             ae.dom.style.visibility = 'visible';
11009         }
11010     },
11011
11012     // private
11013     
11014     onHide : function(){
11015         var ae = this.getActionEl();
11016         ae.dom.style.display = 'none';
11017     },
11018
11019     /**
11020      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
11021      * by an implementing function.
11022      * @method
11023      * @param {EventObject} e
11024      */
11025     onTriggerClick : Roo.emptyFn
11026 });
11027  /*
11028  * Based on:
11029  * Ext JS Library 1.1.1
11030  * Copyright(c) 2006-2007, Ext JS, LLC.
11031  *
11032  * Originally Released Under LGPL - original licence link has changed is not relivant.
11033  *
11034  * Fork - LGPL
11035  * <script type="text/javascript">
11036  */
11037
11038
11039 /**
11040  * @class Roo.data.SortTypes
11041  * @singleton
11042  * Defines the default sorting (casting?) comparison functions used when sorting data.
11043  */
11044 Roo.data.SortTypes = {
11045     /**
11046      * Default sort that does nothing
11047      * @param {Mixed} s The value being converted
11048      * @return {Mixed} The comparison value
11049      */
11050     none : function(s){
11051         return s;
11052     },
11053     
11054     /**
11055      * The regular expression used to strip tags
11056      * @type {RegExp}
11057      * @property
11058      */
11059     stripTagsRE : /<\/?[^>]+>/gi,
11060     
11061     /**
11062      * Strips all HTML tags to sort on text only
11063      * @param {Mixed} s The value being converted
11064      * @return {String} The comparison value
11065      */
11066     asText : function(s){
11067         return String(s).replace(this.stripTagsRE, "");
11068     },
11069     
11070     /**
11071      * Strips all HTML tags to sort on text only - Case insensitive
11072      * @param {Mixed} s The value being converted
11073      * @return {String} The comparison value
11074      */
11075     asUCText : function(s){
11076         return String(s).toUpperCase().replace(this.stripTagsRE, "");
11077     },
11078     
11079     /**
11080      * Case insensitive string
11081      * @param {Mixed} s The value being converted
11082      * @return {String} The comparison value
11083      */
11084     asUCString : function(s) {
11085         return String(s).toUpperCase();
11086     },
11087     
11088     /**
11089      * Date sorting
11090      * @param {Mixed} s The value being converted
11091      * @return {Number} The comparison value
11092      */
11093     asDate : function(s) {
11094         if(!s){
11095             return 0;
11096         }
11097         if(s instanceof Date){
11098             return s.getTime();
11099         }
11100         return Date.parse(String(s));
11101     },
11102     
11103     /**
11104      * Float sorting
11105      * @param {Mixed} s The value being converted
11106      * @return {Float} The comparison value
11107      */
11108     asFloat : function(s) {
11109         var val = parseFloat(String(s).replace(/,/g, ""));
11110         if(isNaN(val)) {
11111             val = 0;
11112         }
11113         return val;
11114     },
11115     
11116     /**
11117      * Integer sorting
11118      * @param {Mixed} s The value being converted
11119      * @return {Number} The comparison value
11120      */
11121     asInt : function(s) {
11122         var val = parseInt(String(s).replace(/,/g, ""));
11123         if(isNaN(val)) {
11124             val = 0;
11125         }
11126         return val;
11127     }
11128 };/*
11129  * Based on:
11130  * Ext JS Library 1.1.1
11131  * Copyright(c) 2006-2007, Ext JS, LLC.
11132  *
11133  * Originally Released Under LGPL - original licence link has changed is not relivant.
11134  *
11135  * Fork - LGPL
11136  * <script type="text/javascript">
11137  */
11138
11139 /**
11140 * @class Roo.data.Record
11141  * Instances of this class encapsulate both record <em>definition</em> information, and record
11142  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
11143  * to access Records cached in an {@link Roo.data.Store} object.<br>
11144  * <p>
11145  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
11146  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
11147  * objects.<br>
11148  * <p>
11149  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
11150  * @constructor
11151  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
11152  * {@link #create}. The parameters are the same.
11153  * @param {Array} data An associative Array of data values keyed by the field name.
11154  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
11155  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
11156  * not specified an integer id is generated.
11157  */
11158 Roo.data.Record = function(data, id){
11159     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11160     this.data = data;
11161 };
11162
11163 /**
11164  * Generate a constructor for a specific record layout.
11165  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11166  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11167  * Each field definition object may contain the following properties: <ul>
11168  * <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,
11169  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11170  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11171  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11172  * is being used, then this is a string containing the javascript expression to reference the data relative to 
11173  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11174  * to the data item relative to the record element. If the mapping expression is the same as the field name,
11175  * this may be omitted.</p></li>
11176  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11177  * <ul><li>auto (Default, implies no conversion)</li>
11178  * <li>string</li>
11179  * <li>int</li>
11180  * <li>float</li>
11181  * <li>boolean</li>
11182  * <li>date</li></ul></p></li>
11183  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11184  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11185  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11186  * by the Reader into an object that will be stored in the Record. It is passed the
11187  * following parameters:<ul>
11188  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11189  * </ul></p></li>
11190  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11191  * </ul>
11192  * <br>usage:<br><pre><code>
11193 var TopicRecord = Roo.data.Record.create(
11194     {name: 'title', mapping: 'topic_title'},
11195     {name: 'author', mapping: 'username'},
11196     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11197     {name: 'lastPost', mapping: 'post_time', type: 'date'},
11198     {name: 'lastPoster', mapping: 'user2'},
11199     {name: 'excerpt', mapping: 'post_text'}
11200 );
11201
11202 var myNewRecord = new TopicRecord({
11203     title: 'Do my job please',
11204     author: 'noobie',
11205     totalPosts: 1,
11206     lastPost: new Date(),
11207     lastPoster: 'Animal',
11208     excerpt: 'No way dude!'
11209 });
11210 myStore.add(myNewRecord);
11211 </code></pre>
11212  * @method create
11213  * @static
11214  */
11215 Roo.data.Record.create = function(o){
11216     var f = function(){
11217         f.superclass.constructor.apply(this, arguments);
11218     };
11219     Roo.extend(f, Roo.data.Record);
11220     var p = f.prototype;
11221     p.fields = new Roo.util.MixedCollection(false, function(field){
11222         return field.name;
11223     });
11224     for(var i = 0, len = o.length; i < len; i++){
11225         p.fields.add(new Roo.data.Field(o[i]));
11226     }
11227     f.getField = function(name){
11228         return p.fields.get(name);  
11229     };
11230     return f;
11231 };
11232
11233 Roo.data.Record.AUTO_ID = 1000;
11234 Roo.data.Record.EDIT = 'edit';
11235 Roo.data.Record.REJECT = 'reject';
11236 Roo.data.Record.COMMIT = 'commit';
11237
11238 Roo.data.Record.prototype = {
11239     /**
11240      * Readonly flag - true if this record has been modified.
11241      * @type Boolean
11242      */
11243     dirty : false,
11244     editing : false,
11245     error: null,
11246     modified: null,
11247
11248     // private
11249     join : function(store){
11250         this.store = store;
11251     },
11252
11253     /**
11254      * Set the named field to the specified value.
11255      * @param {String} name The name of the field to set.
11256      * @param {Object} value The value to set the field to.
11257      */
11258     set : function(name, value){
11259         if(this.data[name] == value){
11260             return;
11261         }
11262         this.dirty = true;
11263         if(!this.modified){
11264             this.modified = {};
11265         }
11266         if(typeof this.modified[name] == 'undefined'){
11267             this.modified[name] = this.data[name];
11268         }
11269         this.data[name] = value;
11270         if(!this.editing && this.store){
11271             this.store.afterEdit(this);
11272         }       
11273     },
11274
11275     /**
11276      * Get the value of the named field.
11277      * @param {String} name The name of the field to get the value of.
11278      * @return {Object} The value of the field.
11279      */
11280     get : function(name){
11281         return this.data[name]; 
11282     },
11283
11284     // private
11285     beginEdit : function(){
11286         this.editing = true;
11287         this.modified = {}; 
11288     },
11289
11290     // private
11291     cancelEdit : function(){
11292         this.editing = false;
11293         delete this.modified;
11294     },
11295
11296     // private
11297     endEdit : function(){
11298         this.editing = false;
11299         if(this.dirty && this.store){
11300             this.store.afterEdit(this);
11301         }
11302     },
11303
11304     /**
11305      * Usually called by the {@link Roo.data.Store} which owns the Record.
11306      * Rejects all changes made to the Record since either creation, or the last commit operation.
11307      * Modified fields are reverted to their original values.
11308      * <p>
11309      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11310      * of reject operations.
11311      */
11312     reject : function(){
11313         var m = this.modified;
11314         for(var n in m){
11315             if(typeof m[n] != "function"){
11316                 this.data[n] = m[n];
11317             }
11318         }
11319         this.dirty = false;
11320         delete this.modified;
11321         this.editing = false;
11322         if(this.store){
11323             this.store.afterReject(this);
11324         }
11325     },
11326
11327     /**
11328      * Usually called by the {@link Roo.data.Store} which owns the Record.
11329      * Commits all changes made to the Record since either creation, or the last commit operation.
11330      * <p>
11331      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11332      * of commit operations.
11333      */
11334     commit : function(){
11335         this.dirty = false;
11336         delete this.modified;
11337         this.editing = false;
11338         if(this.store){
11339             this.store.afterCommit(this);
11340         }
11341     },
11342
11343     // private
11344     hasError : function(){
11345         return this.error != null;
11346     },
11347
11348     // private
11349     clearError : function(){
11350         this.error = null;
11351     },
11352
11353     /**
11354      * Creates a copy of this record.
11355      * @param {String} id (optional) A new record id if you don't want to use this record's id
11356      * @return {Record}
11357      */
11358     copy : function(newId) {
11359         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11360     }
11361 };/*
11362  * Based on:
11363  * Ext JS Library 1.1.1
11364  * Copyright(c) 2006-2007, Ext JS, LLC.
11365  *
11366  * Originally Released Under LGPL - original licence link has changed is not relivant.
11367  *
11368  * Fork - LGPL
11369  * <script type="text/javascript">
11370  */
11371
11372
11373
11374 /**
11375  * @class Roo.data.Store
11376  * @extends Roo.util.Observable
11377  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11378  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11379  * <p>
11380  * 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
11381  * has no knowledge of the format of the data returned by the Proxy.<br>
11382  * <p>
11383  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11384  * instances from the data object. These records are cached and made available through accessor functions.
11385  * @constructor
11386  * Creates a new Store.
11387  * @param {Object} config A config object containing the objects needed for the Store to access data,
11388  * and read the data into Records.
11389  */
11390 Roo.data.Store = function(config){
11391     this.data = new Roo.util.MixedCollection(false);
11392     this.data.getKey = function(o){
11393         return o.id;
11394     };
11395     this.baseParams = {};
11396     // private
11397     this.paramNames = {
11398         "start" : "start",
11399         "limit" : "limit",
11400         "sort" : "sort",
11401         "dir" : "dir",
11402         "multisort" : "_multisort"
11403     };
11404
11405     if(config && config.data){
11406         this.inlineData = config.data;
11407         delete config.data;
11408     }
11409
11410     Roo.apply(this, config);
11411     
11412     if(this.reader){ // reader passed
11413         this.reader = Roo.factory(this.reader, Roo.data);
11414         this.reader.xmodule = this.xmodule || false;
11415         if(!this.recordType){
11416             this.recordType = this.reader.recordType;
11417         }
11418         if(this.reader.onMetaChange){
11419             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11420         }
11421     }
11422
11423     if(this.recordType){
11424         this.fields = this.recordType.prototype.fields;
11425     }
11426     this.modified = [];
11427
11428     this.addEvents({
11429         /**
11430          * @event datachanged
11431          * Fires when the data cache has changed, and a widget which is using this Store
11432          * as a Record cache should refresh its view.
11433          * @param {Store} this
11434          */
11435         datachanged : true,
11436         /**
11437          * @event metachange
11438          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11439          * @param {Store} this
11440          * @param {Object} meta The JSON metadata
11441          */
11442         metachange : true,
11443         /**
11444          * @event add
11445          * Fires when Records have been added to the Store
11446          * @param {Store} this
11447          * @param {Roo.data.Record[]} records The array of Records added
11448          * @param {Number} index The index at which the record(s) were added
11449          */
11450         add : true,
11451         /**
11452          * @event remove
11453          * Fires when a Record has been removed from the Store
11454          * @param {Store} this
11455          * @param {Roo.data.Record} record The Record that was removed
11456          * @param {Number} index The index at which the record was removed
11457          */
11458         remove : true,
11459         /**
11460          * @event update
11461          * Fires when a Record has been updated
11462          * @param {Store} this
11463          * @param {Roo.data.Record} record The Record that was updated
11464          * @param {String} operation The update operation being performed.  Value may be one of:
11465          * <pre><code>
11466  Roo.data.Record.EDIT
11467  Roo.data.Record.REJECT
11468  Roo.data.Record.COMMIT
11469          * </code></pre>
11470          */
11471         update : true,
11472         /**
11473          * @event clear
11474          * Fires when the data cache has been cleared.
11475          * @param {Store} this
11476          */
11477         clear : true,
11478         /**
11479          * @event beforeload
11480          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11481          * the load action will be canceled.
11482          * @param {Store} this
11483          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11484          */
11485         beforeload : true,
11486         /**
11487          * @event beforeloadadd
11488          * Fires after a new set of Records has been loaded.
11489          * @param {Store} this
11490          * @param {Roo.data.Record[]} records The Records that were loaded
11491          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11492          */
11493         beforeloadadd : true,
11494         /**
11495          * @event load
11496          * Fires after a new set of Records has been loaded, before they are added to the store.
11497          * @param {Store} this
11498          * @param {Roo.data.Record[]} records The Records that were loaded
11499          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11500          * @params {Object} return from reader
11501          */
11502         load : true,
11503         /**
11504          * @event loadexception
11505          * Fires if an exception occurs in the Proxy during loading.
11506          * Called with the signature of the Proxy's "loadexception" event.
11507          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11508          * 
11509          * @param {Proxy} 
11510          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11511          * @param {Object} load options 
11512          * @param {Object} jsonData from your request (normally this contains the Exception)
11513          */
11514         loadexception : true
11515     });
11516     
11517     if(this.proxy){
11518         this.proxy = Roo.factory(this.proxy, Roo.data);
11519         this.proxy.xmodule = this.xmodule || false;
11520         this.relayEvents(this.proxy,  ["loadexception"]);
11521     }
11522     this.sortToggle = {};
11523     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11524
11525     Roo.data.Store.superclass.constructor.call(this);
11526
11527     if(this.inlineData){
11528         this.loadData(this.inlineData);
11529         delete this.inlineData;
11530     }
11531 };
11532
11533 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11534      /**
11535     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11536     * without a remote query - used by combo/forms at present.
11537     */
11538     
11539     /**
11540     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11541     */
11542     /**
11543     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11544     */
11545     /**
11546     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11547     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11548     */
11549     /**
11550     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11551     * on any HTTP request
11552     */
11553     /**
11554     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11555     */
11556     /**
11557     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11558     */
11559     multiSort: false,
11560     /**
11561     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11562     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11563     */
11564     remoteSort : false,
11565
11566     /**
11567     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11568      * loaded or when a record is removed. (defaults to false).
11569     */
11570     pruneModifiedRecords : false,
11571
11572     // private
11573     lastOptions : null,
11574
11575     /**
11576      * Add Records to the Store and fires the add event.
11577      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11578      */
11579     add : function(records){
11580         records = [].concat(records);
11581         for(var i = 0, len = records.length; i < len; i++){
11582             records[i].join(this);
11583         }
11584         var index = this.data.length;
11585         this.data.addAll(records);
11586         this.fireEvent("add", this, records, index);
11587     },
11588
11589     /**
11590      * Remove a Record from the Store and fires the remove event.
11591      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11592      */
11593     remove : function(record){
11594         var index = this.data.indexOf(record);
11595         this.data.removeAt(index);
11596  
11597         if(this.pruneModifiedRecords){
11598             this.modified.remove(record);
11599         }
11600         this.fireEvent("remove", this, record, index);
11601     },
11602
11603     /**
11604      * Remove all Records from the Store and fires the clear event.
11605      */
11606     removeAll : function(){
11607         this.data.clear();
11608         if(this.pruneModifiedRecords){
11609             this.modified = [];
11610         }
11611         this.fireEvent("clear", this);
11612     },
11613
11614     /**
11615      * Inserts Records to the Store at the given index and fires the add event.
11616      * @param {Number} index The start index at which to insert the passed Records.
11617      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11618      */
11619     insert : function(index, records){
11620         records = [].concat(records);
11621         for(var i = 0, len = records.length; i < len; i++){
11622             this.data.insert(index, records[i]);
11623             records[i].join(this);
11624         }
11625         this.fireEvent("add", this, records, index);
11626     },
11627
11628     /**
11629      * Get the index within the cache of the passed Record.
11630      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11631      * @return {Number} The index of the passed Record. Returns -1 if not found.
11632      */
11633     indexOf : function(record){
11634         return this.data.indexOf(record);
11635     },
11636
11637     /**
11638      * Get the index within the cache of the Record with the passed id.
11639      * @param {String} id The id of the Record to find.
11640      * @return {Number} The index of the Record. Returns -1 if not found.
11641      */
11642     indexOfId : function(id){
11643         return this.data.indexOfKey(id);
11644     },
11645
11646     /**
11647      * Get the Record with the specified id.
11648      * @param {String} id The id of the Record to find.
11649      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11650      */
11651     getById : function(id){
11652         return this.data.key(id);
11653     },
11654
11655     /**
11656      * Get the Record at the specified index.
11657      * @param {Number} index The index of the Record to find.
11658      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11659      */
11660     getAt : function(index){
11661         return this.data.itemAt(index);
11662     },
11663
11664     /**
11665      * Returns a range of Records between specified indices.
11666      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11667      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11668      * @return {Roo.data.Record[]} An array of Records
11669      */
11670     getRange : function(start, end){
11671         return this.data.getRange(start, end);
11672     },
11673
11674     // private
11675     storeOptions : function(o){
11676         o = Roo.apply({}, o);
11677         delete o.callback;
11678         delete o.scope;
11679         this.lastOptions = o;
11680     },
11681
11682     /**
11683      * Loads the Record cache from the configured Proxy using the configured Reader.
11684      * <p>
11685      * If using remote paging, then the first load call must specify the <em>start</em>
11686      * and <em>limit</em> properties in the options.params property to establish the initial
11687      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11688      * <p>
11689      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11690      * and this call will return before the new data has been loaded. Perform any post-processing
11691      * in a callback function, or in a "load" event handler.</strong>
11692      * <p>
11693      * @param {Object} options An object containing properties which control loading options:<ul>
11694      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11695      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11696      * passed the following arguments:<ul>
11697      * <li>r : Roo.data.Record[]</li>
11698      * <li>options: Options object from the load call</li>
11699      * <li>success: Boolean success indicator</li></ul></li>
11700      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11701      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11702      * </ul>
11703      */
11704     load : function(options){
11705         options = options || {};
11706         if(this.fireEvent("beforeload", this, options) !== false){
11707             this.storeOptions(options);
11708             var p = Roo.apply(options.params || {}, this.baseParams);
11709             // if meta was not loaded from remote source.. try requesting it.
11710             if (!this.reader.metaFromRemote) {
11711                 p._requestMeta = 1;
11712             }
11713             if(this.sortInfo && this.remoteSort){
11714                 var pn = this.paramNames;
11715                 p[pn["sort"]] = this.sortInfo.field;
11716                 p[pn["dir"]] = this.sortInfo.direction;
11717             }
11718             if (this.multiSort) {
11719                 var pn = this.paramNames;
11720                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11721             }
11722             
11723             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11724         }
11725     },
11726
11727     /**
11728      * Reloads the Record cache from the configured Proxy using the configured Reader and
11729      * the options from the last load operation performed.
11730      * @param {Object} options (optional) An object containing properties which may override the options
11731      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11732      * the most recently used options are reused).
11733      */
11734     reload : function(options){
11735         this.load(Roo.applyIf(options||{}, this.lastOptions));
11736     },
11737
11738     // private
11739     // Called as a callback by the Reader during a load operation.
11740     loadRecords : function(o, options, success){
11741         if(!o || success === false){
11742             if(success !== false){
11743                 this.fireEvent("load", this, [], options, o);
11744             }
11745             if(options.callback){
11746                 options.callback.call(options.scope || this, [], options, false);
11747             }
11748             return;
11749         }
11750         // if data returned failure - throw an exception.
11751         if (o.success === false) {
11752             // show a message if no listener is registered.
11753             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11754                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11755             }
11756             // loadmask wil be hooked into this..
11757             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11758             return;
11759         }
11760         var r = o.records, t = o.totalRecords || r.length;
11761         
11762         this.fireEvent("beforeloadadd", this, r, options, o);
11763         
11764         if(!options || options.add !== true){
11765             if(this.pruneModifiedRecords){
11766                 this.modified = [];
11767             }
11768             for(var i = 0, len = r.length; i < len; i++){
11769                 r[i].join(this);
11770             }
11771             if(this.snapshot){
11772                 this.data = this.snapshot;
11773                 delete this.snapshot;
11774             }
11775             this.data.clear();
11776             this.data.addAll(r);
11777             this.totalLength = t;
11778             this.applySort();
11779             this.fireEvent("datachanged", this);
11780         }else{
11781             this.totalLength = Math.max(t, this.data.length+r.length);
11782             this.add(r);
11783         }
11784         
11785         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11786                 
11787             var e = new Roo.data.Record({});
11788
11789             e.set(this.parent.displayField, this.parent.emptyTitle);
11790             e.set(this.parent.valueField, '');
11791
11792             this.insert(0, e);
11793         }
11794             
11795         this.fireEvent("load", this, r, options, o);
11796         if(options.callback){
11797             options.callback.call(options.scope || this, r, options, true);
11798         }
11799     },
11800
11801
11802     /**
11803      * Loads data from a passed data block. A Reader which understands the format of the data
11804      * must have been configured in the constructor.
11805      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11806      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11807      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11808      */
11809     loadData : function(o, append){
11810         var r = this.reader.readRecords(o);
11811         this.loadRecords(r, {add: append}, true);
11812     },
11813
11814     /**
11815      * Gets the number of cached records.
11816      * <p>
11817      * <em>If using paging, this may not be the total size of the dataset. If the data object
11818      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11819      * the data set size</em>
11820      */
11821     getCount : function(){
11822         return this.data.length || 0;
11823     },
11824
11825     /**
11826      * Gets the total number of records in the dataset as returned by the server.
11827      * <p>
11828      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11829      * the dataset size</em>
11830      */
11831     getTotalCount : function(){
11832         return this.totalLength || 0;
11833     },
11834
11835     /**
11836      * Returns the sort state of the Store as an object with two properties:
11837      * <pre><code>
11838  field {String} The name of the field by which the Records are sorted
11839  direction {String} The sort order, "ASC" or "DESC"
11840      * </code></pre>
11841      */
11842     getSortState : function(){
11843         return this.sortInfo;
11844     },
11845
11846     // private
11847     applySort : function(){
11848         if(this.sortInfo && !this.remoteSort){
11849             var s = this.sortInfo, f = s.field;
11850             var st = this.fields.get(f).sortType;
11851             var fn = function(r1, r2){
11852                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11853                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11854             };
11855             this.data.sort(s.direction, fn);
11856             if(this.snapshot && this.snapshot != this.data){
11857                 this.snapshot.sort(s.direction, fn);
11858             }
11859         }
11860     },
11861
11862     /**
11863      * Sets the default sort column and order to be used by the next load operation.
11864      * @param {String} fieldName The name of the field to sort by.
11865      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11866      */
11867     setDefaultSort : function(field, dir){
11868         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11869     },
11870
11871     /**
11872      * Sort the Records.
11873      * If remote sorting is used, the sort is performed on the server, and the cache is
11874      * reloaded. If local sorting is used, the cache is sorted internally.
11875      * @param {String} fieldName The name of the field to sort by.
11876      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11877      */
11878     sort : function(fieldName, dir){
11879         var f = this.fields.get(fieldName);
11880         if(!dir){
11881             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11882             
11883             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11884                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11885             }else{
11886                 dir = f.sortDir;
11887             }
11888         }
11889         this.sortToggle[f.name] = dir;
11890         this.sortInfo = {field: f.name, direction: dir};
11891         if(!this.remoteSort){
11892             this.applySort();
11893             this.fireEvent("datachanged", this);
11894         }else{
11895             this.load(this.lastOptions);
11896         }
11897     },
11898
11899     /**
11900      * Calls the specified function for each of the Records in the cache.
11901      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11902      * Returning <em>false</em> aborts and exits the iteration.
11903      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11904      */
11905     each : function(fn, scope){
11906         this.data.each(fn, scope);
11907     },
11908
11909     /**
11910      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11911      * (e.g., during paging).
11912      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11913      */
11914     getModifiedRecords : function(){
11915         return this.modified;
11916     },
11917
11918     // private
11919     createFilterFn : function(property, value, anyMatch){
11920         if(!value.exec){ // not a regex
11921             value = String(value);
11922             if(value.length == 0){
11923                 return false;
11924             }
11925             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11926         }
11927         return function(r){
11928             return value.test(r.data[property]);
11929         };
11930     },
11931
11932     /**
11933      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11934      * @param {String} property A field on your records
11935      * @param {Number} start The record index to start at (defaults to 0)
11936      * @param {Number} end The last record index to include (defaults to length - 1)
11937      * @return {Number} The sum
11938      */
11939     sum : function(property, start, end){
11940         var rs = this.data.items, v = 0;
11941         start = start || 0;
11942         end = (end || end === 0) ? end : rs.length-1;
11943
11944         for(var i = start; i <= end; i++){
11945             v += (rs[i].data[property] || 0);
11946         }
11947         return v;
11948     },
11949
11950     /**
11951      * Filter the records by a specified property.
11952      * @param {String} field A field on your records
11953      * @param {String/RegExp} value Either a string that the field
11954      * should start with or a RegExp to test against the field
11955      * @param {Boolean} anyMatch True to match any part not just the beginning
11956      */
11957     filter : function(property, value, anyMatch){
11958         var fn = this.createFilterFn(property, value, anyMatch);
11959         return fn ? this.filterBy(fn) : this.clearFilter();
11960     },
11961
11962     /**
11963      * Filter by a function. The specified function will be called with each
11964      * record in this data source. If the function returns true the record is included,
11965      * otherwise it is filtered.
11966      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11967      * @param {Object} scope (optional) The scope of the function (defaults to this)
11968      */
11969     filterBy : function(fn, scope){
11970         this.snapshot = this.snapshot || this.data;
11971         this.data = this.queryBy(fn, scope||this);
11972         this.fireEvent("datachanged", this);
11973     },
11974
11975     /**
11976      * Query the records by a specified property.
11977      * @param {String} field A field on your records
11978      * @param {String/RegExp} value Either a string that the field
11979      * should start with or a RegExp to test against the field
11980      * @param {Boolean} anyMatch True to match any part not just the beginning
11981      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11982      */
11983     query : function(property, value, anyMatch){
11984         var fn = this.createFilterFn(property, value, anyMatch);
11985         return fn ? this.queryBy(fn) : this.data.clone();
11986     },
11987
11988     /**
11989      * Query by a function. The specified function will be called with each
11990      * record in this data source. If the function returns true the record is included
11991      * in the results.
11992      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11993      * @param {Object} scope (optional) The scope of the function (defaults to this)
11994       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11995      **/
11996     queryBy : function(fn, scope){
11997         var data = this.snapshot || this.data;
11998         return data.filterBy(fn, scope||this);
11999     },
12000
12001     /**
12002      * Collects unique values for a particular dataIndex from this store.
12003      * @param {String} dataIndex The property to collect
12004      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
12005      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
12006      * @return {Array} An array of the unique values
12007      **/
12008     collect : function(dataIndex, allowNull, bypassFilter){
12009         var d = (bypassFilter === true && this.snapshot) ?
12010                 this.snapshot.items : this.data.items;
12011         var v, sv, r = [], l = {};
12012         for(var i = 0, len = d.length; i < len; i++){
12013             v = d[i].data[dataIndex];
12014             sv = String(v);
12015             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
12016                 l[sv] = true;
12017                 r[r.length] = v;
12018             }
12019         }
12020         return r;
12021     },
12022
12023     /**
12024      * Revert to a view of the Record cache with no filtering applied.
12025      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
12026      */
12027     clearFilter : function(suppressEvent){
12028         if(this.snapshot && this.snapshot != this.data){
12029             this.data = this.snapshot;
12030             delete this.snapshot;
12031             if(suppressEvent !== true){
12032                 this.fireEvent("datachanged", this);
12033             }
12034         }
12035     },
12036
12037     // private
12038     afterEdit : function(record){
12039         if(this.modified.indexOf(record) == -1){
12040             this.modified.push(record);
12041         }
12042         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
12043     },
12044     
12045     // private
12046     afterReject : function(record){
12047         this.modified.remove(record);
12048         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
12049     },
12050
12051     // private
12052     afterCommit : function(record){
12053         this.modified.remove(record);
12054         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
12055     },
12056
12057     /**
12058      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
12059      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
12060      */
12061     commitChanges : function(){
12062         var m = this.modified.slice(0);
12063         this.modified = [];
12064         for(var i = 0, len = m.length; i < len; i++){
12065             m[i].commit();
12066         }
12067     },
12068
12069     /**
12070      * Cancel outstanding changes on all changed records.
12071      */
12072     rejectChanges : function(){
12073         var m = this.modified.slice(0);
12074         this.modified = [];
12075         for(var i = 0, len = m.length; i < len; i++){
12076             m[i].reject();
12077         }
12078     },
12079
12080     onMetaChange : function(meta, rtype, o){
12081         this.recordType = rtype;
12082         this.fields = rtype.prototype.fields;
12083         delete this.snapshot;
12084         this.sortInfo = meta.sortInfo || this.sortInfo;
12085         this.modified = [];
12086         this.fireEvent('metachange', this, this.reader.meta);
12087     },
12088     
12089     moveIndex : function(data, type)
12090     {
12091         var index = this.indexOf(data);
12092         
12093         var newIndex = index + type;
12094         
12095         this.remove(data);
12096         
12097         this.insert(newIndex, data);
12098         
12099     }
12100 });/*
12101  * Based on:
12102  * Ext JS Library 1.1.1
12103  * Copyright(c) 2006-2007, Ext JS, LLC.
12104  *
12105  * Originally Released Under LGPL - original licence link has changed is not relivant.
12106  *
12107  * Fork - LGPL
12108  * <script type="text/javascript">
12109  */
12110
12111 /**
12112  * @class Roo.data.SimpleStore
12113  * @extends Roo.data.Store
12114  * Small helper class to make creating Stores from Array data easier.
12115  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
12116  * @cfg {Array} fields An array of field definition objects, or field name strings.
12117  * @cfg {Array} data The multi-dimensional array of data
12118  * @constructor
12119  * @param {Object} config
12120  */
12121 Roo.data.SimpleStore = function(config){
12122     Roo.data.SimpleStore.superclass.constructor.call(this, {
12123         isLocal : true,
12124         reader: new Roo.data.ArrayReader({
12125                 id: config.id
12126             },
12127             Roo.data.Record.create(config.fields)
12128         ),
12129         proxy : new Roo.data.MemoryProxy(config.data)
12130     });
12131     this.load();
12132 };
12133 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
12134  * Based on:
12135  * Ext JS Library 1.1.1
12136  * Copyright(c) 2006-2007, Ext JS, LLC.
12137  *
12138  * Originally Released Under LGPL - original licence link has changed is not relivant.
12139  *
12140  * Fork - LGPL
12141  * <script type="text/javascript">
12142  */
12143
12144 /**
12145 /**
12146  * @extends Roo.data.Store
12147  * @class Roo.data.JsonStore
12148  * Small helper class to make creating Stores for JSON data easier. <br/>
12149 <pre><code>
12150 var store = new Roo.data.JsonStore({
12151     url: 'get-images.php',
12152     root: 'images',
12153     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
12154 });
12155 </code></pre>
12156  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12157  * JsonReader and HttpProxy (unless inline data is provided).</b>
12158  * @cfg {Array} fields An array of field definition objects, or field name strings.
12159  * @constructor
12160  * @param {Object} config
12161  */
12162 Roo.data.JsonStore = function(c){
12163     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12164         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12165         reader: new Roo.data.JsonReader(c, c.fields)
12166     }));
12167 };
12168 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12169  * Based on:
12170  * Ext JS Library 1.1.1
12171  * Copyright(c) 2006-2007, Ext JS, LLC.
12172  *
12173  * Originally Released Under LGPL - original licence link has changed is not relivant.
12174  *
12175  * Fork - LGPL
12176  * <script type="text/javascript">
12177  */
12178
12179  
12180 Roo.data.Field = function(config){
12181     if(typeof config == "string"){
12182         config = {name: config};
12183     }
12184     Roo.apply(this, config);
12185     
12186     if(!this.type){
12187         this.type = "auto";
12188     }
12189     
12190     var st = Roo.data.SortTypes;
12191     // named sortTypes are supported, here we look them up
12192     if(typeof this.sortType == "string"){
12193         this.sortType = st[this.sortType];
12194     }
12195     
12196     // set default sortType for strings and dates
12197     if(!this.sortType){
12198         switch(this.type){
12199             case "string":
12200                 this.sortType = st.asUCString;
12201                 break;
12202             case "date":
12203                 this.sortType = st.asDate;
12204                 break;
12205             default:
12206                 this.sortType = st.none;
12207         }
12208     }
12209
12210     // define once
12211     var stripRe = /[\$,%]/g;
12212
12213     // prebuilt conversion function for this field, instead of
12214     // switching every time we're reading a value
12215     if(!this.convert){
12216         var cv, dateFormat = this.dateFormat;
12217         switch(this.type){
12218             case "":
12219             case "auto":
12220             case undefined:
12221                 cv = function(v){ return v; };
12222                 break;
12223             case "string":
12224                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12225                 break;
12226             case "int":
12227                 cv = function(v){
12228                     return v !== undefined && v !== null && v !== '' ?
12229                            parseInt(String(v).replace(stripRe, ""), 10) : '';
12230                     };
12231                 break;
12232             case "float":
12233                 cv = function(v){
12234                     return v !== undefined && v !== null && v !== '' ?
12235                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
12236                     };
12237                 break;
12238             case "bool":
12239             case "boolean":
12240                 cv = function(v){ return v === true || v === "true" || v == 1; };
12241                 break;
12242             case "date":
12243                 cv = function(v){
12244                     if(!v){
12245                         return '';
12246                     }
12247                     if(v instanceof Date){
12248                         return v;
12249                     }
12250                     if(dateFormat){
12251                         if(dateFormat == "timestamp"){
12252                             return new Date(v*1000);
12253                         }
12254                         return Date.parseDate(v, dateFormat);
12255                     }
12256                     var parsed = Date.parse(v);
12257                     return parsed ? new Date(parsed) : null;
12258                 };
12259              break;
12260             
12261         }
12262         this.convert = cv;
12263     }
12264 };
12265
12266 Roo.data.Field.prototype = {
12267     dateFormat: null,
12268     defaultValue: "",
12269     mapping: null,
12270     sortType : null,
12271     sortDir : "ASC"
12272 };/*
12273  * Based on:
12274  * Ext JS Library 1.1.1
12275  * Copyright(c) 2006-2007, Ext JS, LLC.
12276  *
12277  * Originally Released Under LGPL - original licence link has changed is not relivant.
12278  *
12279  * Fork - LGPL
12280  * <script type="text/javascript">
12281  */
12282  
12283 // Base class for reading structured data from a data source.  This class is intended to be
12284 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12285
12286 /**
12287  * @class Roo.data.DataReader
12288  * Base class for reading structured data from a data source.  This class is intended to be
12289  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12290  */
12291
12292 Roo.data.DataReader = function(meta, recordType){
12293     
12294     this.meta = meta;
12295     
12296     this.recordType = recordType instanceof Array ? 
12297         Roo.data.Record.create(recordType) : recordType;
12298 };
12299
12300 Roo.data.DataReader.prototype = {
12301      /**
12302      * Create an empty record
12303      * @param {Object} data (optional) - overlay some values
12304      * @return {Roo.data.Record} record created.
12305      */
12306     newRow :  function(d) {
12307         var da =  {};
12308         this.recordType.prototype.fields.each(function(c) {
12309             switch( c.type) {
12310                 case 'int' : da[c.name] = 0; break;
12311                 case 'date' : da[c.name] = new Date(); break;
12312                 case 'float' : da[c.name] = 0.0; break;
12313                 case 'boolean' : da[c.name] = false; break;
12314                 default : da[c.name] = ""; break;
12315             }
12316             
12317         });
12318         return new this.recordType(Roo.apply(da, d));
12319     }
12320     
12321 };/*
12322  * Based on:
12323  * Ext JS Library 1.1.1
12324  * Copyright(c) 2006-2007, Ext JS, LLC.
12325  *
12326  * Originally Released Under LGPL - original licence link has changed is not relivant.
12327  *
12328  * Fork - LGPL
12329  * <script type="text/javascript">
12330  */
12331
12332 /**
12333  * @class Roo.data.DataProxy
12334  * @extends Roo.data.Observable
12335  * This class is an abstract base class for implementations which provide retrieval of
12336  * unformatted data objects.<br>
12337  * <p>
12338  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12339  * (of the appropriate type which knows how to parse the data object) to provide a block of
12340  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12341  * <p>
12342  * Custom implementations must implement the load method as described in
12343  * {@link Roo.data.HttpProxy#load}.
12344  */
12345 Roo.data.DataProxy = function(){
12346     this.addEvents({
12347         /**
12348          * @event beforeload
12349          * Fires before a network request is made to retrieve a data object.
12350          * @param {Object} This DataProxy object.
12351          * @param {Object} params The params parameter to the load function.
12352          */
12353         beforeload : true,
12354         /**
12355          * @event load
12356          * Fires before the load method's callback is called.
12357          * @param {Object} This DataProxy object.
12358          * @param {Object} o The data object.
12359          * @param {Object} arg The callback argument object passed to the load function.
12360          */
12361         load : true,
12362         /**
12363          * @event loadexception
12364          * Fires if an Exception occurs during data retrieval.
12365          * @param {Object} This DataProxy object.
12366          * @param {Object} o The data object.
12367          * @param {Object} arg The callback argument object passed to the load function.
12368          * @param {Object} e The Exception.
12369          */
12370         loadexception : true
12371     });
12372     Roo.data.DataProxy.superclass.constructor.call(this);
12373 };
12374
12375 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12376
12377     /**
12378      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12379      */
12380 /*
12381  * Based on:
12382  * Ext JS Library 1.1.1
12383  * Copyright(c) 2006-2007, Ext JS, LLC.
12384  *
12385  * Originally Released Under LGPL - original licence link has changed is not relivant.
12386  *
12387  * Fork - LGPL
12388  * <script type="text/javascript">
12389  */
12390 /**
12391  * @class Roo.data.MemoryProxy
12392  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12393  * to the Reader when its load method is called.
12394  * @constructor
12395  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12396  */
12397 Roo.data.MemoryProxy = function(data){
12398     if (data.data) {
12399         data = data.data;
12400     }
12401     Roo.data.MemoryProxy.superclass.constructor.call(this);
12402     this.data = data;
12403 };
12404
12405 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12406     
12407     /**
12408      * Load data from the requested source (in this case an in-memory
12409      * data object passed to the constructor), read the data object into
12410      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12411      * process that block using the passed callback.
12412      * @param {Object} params This parameter is not used by the MemoryProxy class.
12413      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12414      * object into a block of Roo.data.Records.
12415      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12416      * The function must be passed <ul>
12417      * <li>The Record block object</li>
12418      * <li>The "arg" argument from the load function</li>
12419      * <li>A boolean success indicator</li>
12420      * </ul>
12421      * @param {Object} scope The scope in which to call the callback
12422      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12423      */
12424     load : function(params, reader, callback, scope, arg){
12425         params = params || {};
12426         var result;
12427         try {
12428             result = reader.readRecords(params.data ? params.data :this.data);
12429         }catch(e){
12430             this.fireEvent("loadexception", this, arg, null, e);
12431             callback.call(scope, null, arg, false);
12432             return;
12433         }
12434         callback.call(scope, result, arg, true);
12435     },
12436     
12437     // private
12438     update : function(params, records){
12439         
12440     }
12441 });/*
12442  * Based on:
12443  * Ext JS Library 1.1.1
12444  * Copyright(c) 2006-2007, Ext JS, LLC.
12445  *
12446  * Originally Released Under LGPL - original licence link has changed is not relivant.
12447  *
12448  * Fork - LGPL
12449  * <script type="text/javascript">
12450  */
12451 /**
12452  * @class Roo.data.HttpProxy
12453  * @extends Roo.data.DataProxy
12454  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12455  * configured to reference a certain URL.<br><br>
12456  * <p>
12457  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12458  * from which the running page was served.<br><br>
12459  * <p>
12460  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12461  * <p>
12462  * Be aware that to enable the browser to parse an XML document, the server must set
12463  * the Content-Type header in the HTTP response to "text/xml".
12464  * @constructor
12465  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12466  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12467  * will be used to make the request.
12468  */
12469 Roo.data.HttpProxy = function(conn){
12470     Roo.data.HttpProxy.superclass.constructor.call(this);
12471     // is conn a conn config or a real conn?
12472     this.conn = conn;
12473     this.useAjax = !conn || !conn.events;
12474   
12475 };
12476
12477 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12478     // thse are take from connection...
12479     
12480     /**
12481      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12482      */
12483     /**
12484      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12485      * extra parameters to each request made by this object. (defaults to undefined)
12486      */
12487     /**
12488      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12489      *  to each request made by this object. (defaults to undefined)
12490      */
12491     /**
12492      * @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)
12493      */
12494     /**
12495      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12496      */
12497      /**
12498      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12499      * @type Boolean
12500      */
12501   
12502
12503     /**
12504      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12505      * @type Boolean
12506      */
12507     /**
12508      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12509      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12510      * a finer-grained basis than the DataProxy events.
12511      */
12512     getConnection : function(){
12513         return this.useAjax ? Roo.Ajax : this.conn;
12514     },
12515
12516     /**
12517      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12518      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12519      * process that block using the passed callback.
12520      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12521      * for the request to the remote server.
12522      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12523      * object into a block of Roo.data.Records.
12524      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12525      * The function must be passed <ul>
12526      * <li>The Record block object</li>
12527      * <li>The "arg" argument from the load function</li>
12528      * <li>A boolean success indicator</li>
12529      * </ul>
12530      * @param {Object} scope The scope in which to call the callback
12531      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12532      */
12533     load : function(params, reader, callback, scope, arg){
12534         if(this.fireEvent("beforeload", this, params) !== false){
12535             var  o = {
12536                 params : params || {},
12537                 request: {
12538                     callback : callback,
12539                     scope : scope,
12540                     arg : arg
12541                 },
12542                 reader: reader,
12543                 callback : this.loadResponse,
12544                 scope: this
12545             };
12546             if(this.useAjax){
12547                 Roo.applyIf(o, this.conn);
12548                 if(this.activeRequest){
12549                     Roo.Ajax.abort(this.activeRequest);
12550                 }
12551                 this.activeRequest = Roo.Ajax.request(o);
12552             }else{
12553                 this.conn.request(o);
12554             }
12555         }else{
12556             callback.call(scope||this, null, arg, false);
12557         }
12558     },
12559
12560     // private
12561     loadResponse : function(o, success, response){
12562         delete this.activeRequest;
12563         if(!success){
12564             this.fireEvent("loadexception", this, o, response);
12565             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12566             return;
12567         }
12568         var result;
12569         try {
12570             result = o.reader.read(response);
12571         }catch(e){
12572             this.fireEvent("loadexception", this, o, response, e);
12573             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12574             return;
12575         }
12576         
12577         this.fireEvent("load", this, o, o.request.arg);
12578         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12579     },
12580
12581     // private
12582     update : function(dataSet){
12583
12584     },
12585
12586     // private
12587     updateResponse : function(dataSet){
12588
12589     }
12590 });/*
12591  * Based on:
12592  * Ext JS Library 1.1.1
12593  * Copyright(c) 2006-2007, Ext JS, LLC.
12594  *
12595  * Originally Released Under LGPL - original licence link has changed is not relivant.
12596  *
12597  * Fork - LGPL
12598  * <script type="text/javascript">
12599  */
12600
12601 /**
12602  * @class Roo.data.ScriptTagProxy
12603  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12604  * other than the originating domain of the running page.<br><br>
12605  * <p>
12606  * <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
12607  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12608  * <p>
12609  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12610  * source code that is used as the source inside a &lt;script> tag.<br><br>
12611  * <p>
12612  * In order for the browser to process the returned data, the server must wrap the data object
12613  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12614  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12615  * depending on whether the callback name was passed:
12616  * <p>
12617  * <pre><code>
12618 boolean scriptTag = false;
12619 String cb = request.getParameter("callback");
12620 if (cb != null) {
12621     scriptTag = true;
12622     response.setContentType("text/javascript");
12623 } else {
12624     response.setContentType("application/x-json");
12625 }
12626 Writer out = response.getWriter();
12627 if (scriptTag) {
12628     out.write(cb + "(");
12629 }
12630 out.print(dataBlock.toJsonString());
12631 if (scriptTag) {
12632     out.write(");");
12633 }
12634 </pre></code>
12635  *
12636  * @constructor
12637  * @param {Object} config A configuration object.
12638  */
12639 Roo.data.ScriptTagProxy = function(config){
12640     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12641     Roo.apply(this, config);
12642     this.head = document.getElementsByTagName("head")[0];
12643 };
12644
12645 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12646
12647 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12648     /**
12649      * @cfg {String} url The URL from which to request the data object.
12650      */
12651     /**
12652      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12653      */
12654     timeout : 30000,
12655     /**
12656      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12657      * the server the name of the callback function set up by the load call to process the returned data object.
12658      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12659      * javascript output which calls this named function passing the data object as its only parameter.
12660      */
12661     callbackParam : "callback",
12662     /**
12663      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12664      * name to the request.
12665      */
12666     nocache : true,
12667
12668     /**
12669      * Load data from the configured URL, read the data object into
12670      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12671      * process that block using the passed callback.
12672      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12673      * for the request to the remote server.
12674      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12675      * object into a block of Roo.data.Records.
12676      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12677      * The function must be passed <ul>
12678      * <li>The Record block object</li>
12679      * <li>The "arg" argument from the load function</li>
12680      * <li>A boolean success indicator</li>
12681      * </ul>
12682      * @param {Object} scope The scope in which to call the callback
12683      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12684      */
12685     load : function(params, reader, callback, scope, arg){
12686         if(this.fireEvent("beforeload", this, params) !== false){
12687
12688             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12689
12690             var url = this.url;
12691             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12692             if(this.nocache){
12693                 url += "&_dc=" + (new Date().getTime());
12694             }
12695             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12696             var trans = {
12697                 id : transId,
12698                 cb : "stcCallback"+transId,
12699                 scriptId : "stcScript"+transId,
12700                 params : params,
12701                 arg : arg,
12702                 url : url,
12703                 callback : callback,
12704                 scope : scope,
12705                 reader : reader
12706             };
12707             var conn = this;
12708
12709             window[trans.cb] = function(o){
12710                 conn.handleResponse(o, trans);
12711             };
12712
12713             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12714
12715             if(this.autoAbort !== false){
12716                 this.abort();
12717             }
12718
12719             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12720
12721             var script = document.createElement("script");
12722             script.setAttribute("src", url);
12723             script.setAttribute("type", "text/javascript");
12724             script.setAttribute("id", trans.scriptId);
12725             this.head.appendChild(script);
12726
12727             this.trans = trans;
12728         }else{
12729             callback.call(scope||this, null, arg, false);
12730         }
12731     },
12732
12733     // private
12734     isLoading : function(){
12735         return this.trans ? true : false;
12736     },
12737
12738     /**
12739      * Abort the current server request.
12740      */
12741     abort : function(){
12742         if(this.isLoading()){
12743             this.destroyTrans(this.trans);
12744         }
12745     },
12746
12747     // private
12748     destroyTrans : function(trans, isLoaded){
12749         this.head.removeChild(document.getElementById(trans.scriptId));
12750         clearTimeout(trans.timeoutId);
12751         if(isLoaded){
12752             window[trans.cb] = undefined;
12753             try{
12754                 delete window[trans.cb];
12755             }catch(e){}
12756         }else{
12757             // if hasn't been loaded, wait for load to remove it to prevent script error
12758             window[trans.cb] = function(){
12759                 window[trans.cb] = undefined;
12760                 try{
12761                     delete window[trans.cb];
12762                 }catch(e){}
12763             };
12764         }
12765     },
12766
12767     // private
12768     handleResponse : function(o, trans){
12769         this.trans = false;
12770         this.destroyTrans(trans, true);
12771         var result;
12772         try {
12773             result = trans.reader.readRecords(o);
12774         }catch(e){
12775             this.fireEvent("loadexception", this, o, trans.arg, e);
12776             trans.callback.call(trans.scope||window, null, trans.arg, false);
12777             return;
12778         }
12779         this.fireEvent("load", this, o, trans.arg);
12780         trans.callback.call(trans.scope||window, result, trans.arg, true);
12781     },
12782
12783     // private
12784     handleFailure : function(trans){
12785         this.trans = false;
12786         this.destroyTrans(trans, false);
12787         this.fireEvent("loadexception", this, null, trans.arg);
12788         trans.callback.call(trans.scope||window, null, trans.arg, false);
12789     }
12790 });/*
12791  * Based on:
12792  * Ext JS Library 1.1.1
12793  * Copyright(c) 2006-2007, Ext JS, LLC.
12794  *
12795  * Originally Released Under LGPL - original licence link has changed is not relivant.
12796  *
12797  * Fork - LGPL
12798  * <script type="text/javascript">
12799  */
12800
12801 /**
12802  * @class Roo.data.JsonReader
12803  * @extends Roo.data.DataReader
12804  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12805  * based on mappings in a provided Roo.data.Record constructor.
12806  * 
12807  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12808  * in the reply previously. 
12809  * 
12810  * <p>
12811  * Example code:
12812  * <pre><code>
12813 var RecordDef = Roo.data.Record.create([
12814     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12815     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12816 ]);
12817 var myReader = new Roo.data.JsonReader({
12818     totalProperty: "results",    // The property which contains the total dataset size (optional)
12819     root: "rows",                // The property which contains an Array of row objects
12820     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12821 }, RecordDef);
12822 </code></pre>
12823  * <p>
12824  * This would consume a JSON file like this:
12825  * <pre><code>
12826 { 'results': 2, 'rows': [
12827     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12828     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12829 }
12830 </code></pre>
12831  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12832  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12833  * paged from the remote server.
12834  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12835  * @cfg {String} root name of the property which contains the Array of row objects.
12836  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12837  * @cfg {Array} fields Array of field definition objects
12838  * @constructor
12839  * Create a new JsonReader
12840  * @param {Object} meta Metadata configuration options
12841  * @param {Object} recordType Either an Array of field definition objects,
12842  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12843  */
12844 Roo.data.JsonReader = function(meta, recordType){
12845     
12846     meta = meta || {};
12847     // set some defaults:
12848     Roo.applyIf(meta, {
12849         totalProperty: 'total',
12850         successProperty : 'success',
12851         root : 'data',
12852         id : 'id'
12853     });
12854     
12855     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12856 };
12857 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12858     
12859     /**
12860      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12861      * Used by Store query builder to append _requestMeta to params.
12862      * 
12863      */
12864     metaFromRemote : false,
12865     /**
12866      * This method is only used by a DataProxy which has retrieved data from a remote server.
12867      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12868      * @return {Object} data A data block which is used by an Roo.data.Store object as
12869      * a cache of Roo.data.Records.
12870      */
12871     read : function(response){
12872         var json = response.responseText;
12873        
12874         var o = /* eval:var:o */ eval("("+json+")");
12875         if(!o) {
12876             throw {message: "JsonReader.read: Json object not found"};
12877         }
12878         
12879         if(o.metaData){
12880             
12881             delete this.ef;
12882             this.metaFromRemote = true;
12883             this.meta = o.metaData;
12884             this.recordType = Roo.data.Record.create(o.metaData.fields);
12885             this.onMetaChange(this.meta, this.recordType, o);
12886         }
12887         return this.readRecords(o);
12888     },
12889
12890     // private function a store will implement
12891     onMetaChange : function(meta, recordType, o){
12892
12893     },
12894
12895     /**
12896          * @ignore
12897          */
12898     simpleAccess: function(obj, subsc) {
12899         return obj[subsc];
12900     },
12901
12902         /**
12903          * @ignore
12904          */
12905     getJsonAccessor: function(){
12906         var re = /[\[\.]/;
12907         return function(expr) {
12908             try {
12909                 return(re.test(expr))
12910                     ? new Function("obj", "return obj." + expr)
12911                     : function(obj){
12912                         return obj[expr];
12913                     };
12914             } catch(e){}
12915             return Roo.emptyFn;
12916         };
12917     }(),
12918
12919     /**
12920      * Create a data block containing Roo.data.Records from an XML document.
12921      * @param {Object} o An object which contains an Array of row objects in the property specified
12922      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12923      * which contains the total size of the dataset.
12924      * @return {Object} data A data block which is used by an Roo.data.Store object as
12925      * a cache of Roo.data.Records.
12926      */
12927     readRecords : function(o){
12928         /**
12929          * After any data loads, the raw JSON data is available for further custom processing.
12930          * @type Object
12931          */
12932         this.o = o;
12933         var s = this.meta, Record = this.recordType,
12934             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12935
12936 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12937         if (!this.ef) {
12938             if(s.totalProperty) {
12939                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12940                 }
12941                 if(s.successProperty) {
12942                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12943                 }
12944                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12945                 if (s.id) {
12946                         var g = this.getJsonAccessor(s.id);
12947                         this.getId = function(rec) {
12948                                 var r = g(rec);  
12949                                 return (r === undefined || r === "") ? null : r;
12950                         };
12951                 } else {
12952                         this.getId = function(){return null;};
12953                 }
12954             this.ef = [];
12955             for(var jj = 0; jj < fl; jj++){
12956                 f = fi[jj];
12957                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12958                 this.ef[jj] = this.getJsonAccessor(map);
12959             }
12960         }
12961
12962         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12963         if(s.totalProperty){
12964             var vt = parseInt(this.getTotal(o), 10);
12965             if(!isNaN(vt)){
12966                 totalRecords = vt;
12967             }
12968         }
12969         if(s.successProperty){
12970             var vs = this.getSuccess(o);
12971             if(vs === false || vs === 'false'){
12972                 success = false;
12973             }
12974         }
12975         var records = [];
12976         for(var i = 0; i < c; i++){
12977                 var n = root[i];
12978             var values = {};
12979             var id = this.getId(n);
12980             for(var j = 0; j < fl; j++){
12981                 f = fi[j];
12982             var v = this.ef[j](n);
12983             if (!f.convert) {
12984                 Roo.log('missing convert for ' + f.name);
12985                 Roo.log(f);
12986                 continue;
12987             }
12988             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12989             }
12990             var record = new Record(values, id);
12991             record.json = n;
12992             records[i] = record;
12993         }
12994         return {
12995             raw : o,
12996             success : success,
12997             records : records,
12998             totalRecords : totalRecords
12999         };
13000     }
13001 });/*
13002  * Based on:
13003  * Ext JS Library 1.1.1
13004  * Copyright(c) 2006-2007, Ext JS, LLC.
13005  *
13006  * Originally Released Under LGPL - original licence link has changed is not relivant.
13007  *
13008  * Fork - LGPL
13009  * <script type="text/javascript">
13010  */
13011
13012 /**
13013  * @class Roo.data.ArrayReader
13014  * @extends Roo.data.DataReader
13015  * Data reader class to create an Array of Roo.data.Record objects from an Array.
13016  * Each element of that Array represents a row of data fields. The
13017  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
13018  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
13019  * <p>
13020  * Example code:.
13021  * <pre><code>
13022 var RecordDef = Roo.data.Record.create([
13023     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
13024     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
13025 ]);
13026 var myReader = new Roo.data.ArrayReader({
13027     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
13028 }, RecordDef);
13029 </code></pre>
13030  * <p>
13031  * This would consume an Array like this:
13032  * <pre><code>
13033 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
13034   </code></pre>
13035  
13036  * @constructor
13037  * Create a new JsonReader
13038  * @param {Object} meta Metadata configuration options.
13039  * @param {Object|Array} recordType Either an Array of field definition objects
13040  * 
13041  * @cfg {Array} fields Array of field definition objects
13042  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
13043  * as specified to {@link Roo.data.Record#create},
13044  * or an {@link Roo.data.Record} object
13045  *
13046  * 
13047  * created using {@link Roo.data.Record#create}.
13048  */
13049 Roo.data.ArrayReader = function(meta, recordType){
13050     
13051      
13052     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
13053 };
13054
13055 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
13056     /**
13057      * Create a data block containing Roo.data.Records from an XML document.
13058      * @param {Object} o An Array of row objects which represents the dataset.
13059      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
13060      * a cache of Roo.data.Records.
13061      */
13062     readRecords : function(o){
13063         var sid = this.meta ? this.meta.id : null;
13064         var recordType = this.recordType, fields = recordType.prototype.fields;
13065         var records = [];
13066         var root = o;
13067             for(var i = 0; i < root.length; i++){
13068                     var n = root[i];
13069                 var values = {};
13070                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
13071                 for(var j = 0, jlen = fields.length; j < jlen; j++){
13072                 var f = fields.items[j];
13073                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
13074                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
13075                 v = f.convert(v);
13076                 values[f.name] = v;
13077             }
13078                 var record = new recordType(values, id);
13079                 record.json = n;
13080                 records[records.length] = record;
13081             }
13082             return {
13083                 records : records,
13084                 totalRecords : records.length
13085             };
13086     }
13087 });/*
13088  * - LGPL
13089  * * 
13090  */
13091
13092 /**
13093  * @class Roo.bootstrap.ComboBox
13094  * @extends Roo.bootstrap.TriggerField
13095  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
13096  * @cfg {Boolean} append (true|false) default false
13097  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
13098  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
13099  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
13100  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
13101  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
13102  * @cfg {Boolean} animate default true
13103  * @cfg {Boolean} emptyResultText only for touch device
13104  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
13105  * @cfg {String} emptyTitle default ''
13106  * @constructor
13107  * Create a new ComboBox.
13108  * @param {Object} config Configuration options
13109  */
13110 Roo.bootstrap.ComboBox = function(config){
13111     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
13112     this.addEvents({
13113         /**
13114          * @event expand
13115          * Fires when the dropdown list is expanded
13116         * @param {Roo.bootstrap.ComboBox} combo This combo box
13117         */
13118         'expand' : true,
13119         /**
13120          * @event collapse
13121          * Fires when the dropdown list is collapsed
13122         * @param {Roo.bootstrap.ComboBox} combo This combo box
13123         */
13124         'collapse' : true,
13125         /**
13126          * @event beforeselect
13127          * Fires before a list item is selected. Return false to cancel the selection.
13128         * @param {Roo.bootstrap.ComboBox} combo This combo box
13129         * @param {Roo.data.Record} record The data record returned from the underlying store
13130         * @param {Number} index The index of the selected item in the dropdown list
13131         */
13132         'beforeselect' : true,
13133         /**
13134          * @event select
13135          * Fires when a list item is selected
13136         * @param {Roo.bootstrap.ComboBox} combo This combo box
13137         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
13138         * @param {Number} index The index of the selected item in the dropdown list
13139         */
13140         'select' : true,
13141         /**
13142          * @event beforequery
13143          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
13144          * The event object passed has these properties:
13145         * @param {Roo.bootstrap.ComboBox} combo This combo box
13146         * @param {String} query The query
13147         * @param {Boolean} forceAll true to force "all" query
13148         * @param {Boolean} cancel true to cancel the query
13149         * @param {Object} e The query event object
13150         */
13151         'beforequery': true,
13152          /**
13153          * @event add
13154          * Fires when the 'add' icon is pressed (add a listener to enable add button)
13155         * @param {Roo.bootstrap.ComboBox} combo This combo box
13156         */
13157         'add' : true,
13158         /**
13159          * @event edit
13160          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
13161         * @param {Roo.bootstrap.ComboBox} combo This combo box
13162         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13163         */
13164         'edit' : true,
13165         /**
13166          * @event remove
13167          * Fires when the remove value from the combobox array
13168         * @param {Roo.bootstrap.ComboBox} combo This combo box
13169         */
13170         'remove' : true,
13171         /**
13172          * @event afterremove
13173          * Fires when the remove value from the combobox array
13174         * @param {Roo.bootstrap.ComboBox} combo This combo box
13175         */
13176         'afterremove' : true,
13177         /**
13178          * @event specialfilter
13179          * Fires when specialfilter
13180             * @param {Roo.bootstrap.ComboBox} combo This combo box
13181             */
13182         'specialfilter' : true,
13183         /**
13184          * @event tick
13185          * Fires when tick the element
13186             * @param {Roo.bootstrap.ComboBox} combo This combo box
13187             */
13188         'tick' : true,
13189         /**
13190          * @event touchviewdisplay
13191          * Fires when touch view require special display (default is using displayField)
13192             * @param {Roo.bootstrap.ComboBox} combo This combo box
13193             * @param {Object} cfg set html .
13194             */
13195         'touchviewdisplay' : true
13196         
13197     });
13198     
13199     this.item = [];
13200     this.tickItems = [];
13201     
13202     this.selectedIndex = -1;
13203     if(this.mode == 'local'){
13204         if(config.queryDelay === undefined){
13205             this.queryDelay = 10;
13206         }
13207         if(config.minChars === undefined){
13208             this.minChars = 0;
13209         }
13210     }
13211 };
13212
13213 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13214      
13215     /**
13216      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13217      * rendering into an Roo.Editor, defaults to false)
13218      */
13219     /**
13220      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13221      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13222      */
13223     /**
13224      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13225      */
13226     /**
13227      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13228      * the dropdown list (defaults to undefined, with no header element)
13229      */
13230
13231      /**
13232      * @cfg {String/Roo.Template} tpl The template to use to render the output
13233      */
13234      
13235      /**
13236      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13237      */
13238     listWidth: undefined,
13239     /**
13240      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13241      * mode = 'remote' or 'text' if mode = 'local')
13242      */
13243     displayField: undefined,
13244     
13245     /**
13246      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13247      * mode = 'remote' or 'value' if mode = 'local'). 
13248      * Note: use of a valueField requires the user make a selection
13249      * in order for a value to be mapped.
13250      */
13251     valueField: undefined,
13252     /**
13253      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13254      */
13255     modalTitle : '',
13256     
13257     /**
13258      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13259      * field's data value (defaults to the underlying DOM element's name)
13260      */
13261     hiddenName: undefined,
13262     /**
13263      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13264      */
13265     listClass: '',
13266     /**
13267      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13268      */
13269     selectedClass: 'active',
13270     
13271     /**
13272      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13273      */
13274     shadow:'sides',
13275     /**
13276      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13277      * anchor positions (defaults to 'tl-bl')
13278      */
13279     listAlign: 'tl-bl?',
13280     /**
13281      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13282      */
13283     maxHeight: 300,
13284     /**
13285      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13286      * query specified by the allQuery config option (defaults to 'query')
13287      */
13288     triggerAction: 'query',
13289     /**
13290      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13291      * (defaults to 4, does not apply if editable = false)
13292      */
13293     minChars : 4,
13294     /**
13295      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13296      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13297      */
13298     typeAhead: false,
13299     /**
13300      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13301      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13302      */
13303     queryDelay: 500,
13304     /**
13305      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13306      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13307      */
13308     pageSize: 0,
13309     /**
13310      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13311      * when editable = true (defaults to false)
13312      */
13313     selectOnFocus:false,
13314     /**
13315      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13316      */
13317     queryParam: 'query',
13318     /**
13319      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13320      * when mode = 'remote' (defaults to 'Loading...')
13321      */
13322     loadingText: 'Loading...',
13323     /**
13324      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13325      */
13326     resizable: false,
13327     /**
13328      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13329      */
13330     handleHeight : 8,
13331     /**
13332      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13333      * traditional select (defaults to true)
13334      */
13335     editable: true,
13336     /**
13337      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13338      */
13339     allQuery: '',
13340     /**
13341      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13342      */
13343     mode: 'remote',
13344     /**
13345      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13346      * listWidth has a higher value)
13347      */
13348     minListWidth : 70,
13349     /**
13350      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13351      * allow the user to set arbitrary text into the field (defaults to false)
13352      */
13353     forceSelection:false,
13354     /**
13355      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13356      * if typeAhead = true (defaults to 250)
13357      */
13358     typeAheadDelay : 250,
13359     /**
13360      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13361      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13362      */
13363     valueNotFoundText : undefined,
13364     /**
13365      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13366      */
13367     blockFocus : false,
13368     
13369     /**
13370      * @cfg {Boolean} disableClear Disable showing of clear button.
13371      */
13372     disableClear : false,
13373     /**
13374      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13375      */
13376     alwaysQuery : false,
13377     
13378     /**
13379      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13380      */
13381     multiple : false,
13382     
13383     /**
13384      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
13385      */
13386     invalidClass : "has-warning",
13387     
13388     /**
13389      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
13390      */
13391     validClass : "has-success",
13392     
13393     /**
13394      * @cfg {Boolean} specialFilter (true|false) special filter default false
13395      */
13396     specialFilter : false,
13397     
13398     /**
13399      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13400      */
13401     mobileTouchView : true,
13402     
13403     /**
13404      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13405      */
13406     useNativeIOS : false,
13407     
13408     /**
13409      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13410      */
13411     mobile_restrict_height : false,
13412     
13413     ios_options : false,
13414     
13415     //private
13416     addicon : false,
13417     editicon: false,
13418     
13419     page: 0,
13420     hasQuery: false,
13421     append: false,
13422     loadNext: false,
13423     autoFocus : true,
13424     tickable : false,
13425     btnPosition : 'right',
13426     triggerList : true,
13427     showToggleBtn : true,
13428     animate : true,
13429     emptyResultText: 'Empty',
13430     triggerText : 'Select',
13431     emptyTitle : '',
13432     
13433     // element that contains real text value.. (when hidden is used..)
13434     
13435     getAutoCreate : function()
13436     {   
13437         var cfg = false;
13438         //render
13439         /*
13440          * Render classic select for iso
13441          */
13442         
13443         if(Roo.isIOS && this.useNativeIOS){
13444             cfg = this.getAutoCreateNativeIOS();
13445             return cfg;
13446         }
13447         
13448         /*
13449          * Touch Devices
13450          */
13451         
13452         if(Roo.isTouch && this.mobileTouchView){
13453             cfg = this.getAutoCreateTouchView();
13454             return cfg;;
13455         }
13456         
13457         /*
13458          *  Normal ComboBox
13459          */
13460         if(!this.tickable){
13461             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13462             return cfg;
13463         }
13464         
13465         /*
13466          *  ComboBox with tickable selections
13467          */
13468              
13469         var align = this.labelAlign || this.parentLabelAlign();
13470         
13471         cfg = {
13472             cls : 'form-group roo-combobox-tickable' //input-group
13473         };
13474         
13475         var btn_text_select = '';
13476         var btn_text_done = '';
13477         var btn_text_cancel = '';
13478         
13479         if (this.btn_text_show) {
13480             btn_text_select = 'Select';
13481             btn_text_done = 'Done';
13482             btn_text_cancel = 'Cancel'; 
13483         }
13484         
13485         var buttons = {
13486             tag : 'div',
13487             cls : 'tickable-buttons',
13488             cn : [
13489                 {
13490                     tag : 'button',
13491                     type : 'button',
13492                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13493                     //html : this.triggerText
13494                     html: btn_text_select
13495                 },
13496                 {
13497                     tag : 'button',
13498                     type : 'button',
13499                     name : 'ok',
13500                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13501                     //html : 'Done'
13502                     html: btn_text_done
13503                 },
13504                 {
13505                     tag : 'button',
13506                     type : 'button',
13507                     name : 'cancel',
13508                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13509                     //html : 'Cancel'
13510                     html: btn_text_cancel
13511                 }
13512             ]
13513         };
13514         
13515         if(this.editable){
13516             buttons.cn.unshift({
13517                 tag: 'input',
13518                 cls: 'roo-select2-search-field-input'
13519             });
13520         }
13521         
13522         var _this = this;
13523         
13524         Roo.each(buttons.cn, function(c){
13525             if (_this.size) {
13526                 c.cls += ' btn-' + _this.size;
13527             }
13528
13529             if (_this.disabled) {
13530                 c.disabled = true;
13531             }
13532         });
13533         
13534         var box = {
13535             tag: 'div',
13536             style : 'display: contents',
13537             cn: [
13538                 {
13539                     tag: 'input',
13540                     type : 'hidden',
13541                     cls: 'form-hidden-field'
13542                 },
13543                 {
13544                     tag: 'ul',
13545                     cls: 'roo-select2-choices',
13546                     cn:[
13547                         {
13548                             tag: 'li',
13549                             cls: 'roo-select2-search-field',
13550                             cn: [
13551                                 buttons
13552                             ]
13553                         }
13554                     ]
13555                 }
13556             ]
13557         };
13558         
13559         var combobox = {
13560             cls: 'roo-select2-container input-group roo-select2-container-multi',
13561             cn: [
13562                 
13563                 box
13564 //                {
13565 //                    tag: 'ul',
13566 //                    cls: 'typeahead typeahead-long dropdown-menu',
13567 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13568 //                }
13569             ]
13570         };
13571         
13572         if(this.hasFeedback && !this.allowBlank){
13573             
13574             var feedback = {
13575                 tag: 'span',
13576                 cls: 'glyphicon form-control-feedback'
13577             };
13578
13579             combobox.cn.push(feedback);
13580         }
13581         
13582         var indicator = {
13583             tag : 'i',
13584             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13585             tooltip : 'This field is required'
13586         };
13587         if (Roo.bootstrap.version == 4) {
13588             indicator = {
13589                 tag : 'i',
13590                 style : 'display:none'
13591             };
13592         }
13593         if (align ==='left' && this.fieldLabel.length) {
13594             
13595             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13596             
13597             cfg.cn = [
13598                 indicator,
13599                 {
13600                     tag: 'label',
13601                     'for' :  id,
13602                     cls : 'control-label col-form-label',
13603                     html : this.fieldLabel
13604
13605                 },
13606                 {
13607                     cls : "", 
13608                     cn: [
13609                         combobox
13610                     ]
13611                 }
13612
13613             ];
13614             
13615             var labelCfg = cfg.cn[1];
13616             var contentCfg = cfg.cn[2];
13617             
13618
13619             if(this.indicatorpos == 'right'){
13620                 
13621                 cfg.cn = [
13622                     {
13623                         tag: 'label',
13624                         'for' :  id,
13625                         cls : 'control-label col-form-label',
13626                         cn : [
13627                             {
13628                                 tag : 'span',
13629                                 html : this.fieldLabel
13630                             },
13631                             indicator
13632                         ]
13633                     },
13634                     {
13635                         cls : "",
13636                         cn: [
13637                             combobox
13638                         ]
13639                     }
13640
13641                 ];
13642                 
13643                 
13644                 
13645                 labelCfg = cfg.cn[0];
13646                 contentCfg = cfg.cn[1];
13647             
13648             }
13649             
13650             if(this.labelWidth > 12){
13651                 labelCfg.style = "width: " + this.labelWidth + 'px';
13652             }
13653             
13654             if(this.labelWidth < 13 && this.labelmd == 0){
13655                 this.labelmd = this.labelWidth;
13656             }
13657             
13658             if(this.labellg > 0){
13659                 labelCfg.cls += ' col-lg-' + this.labellg;
13660                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13661             }
13662             
13663             if(this.labelmd > 0){
13664                 labelCfg.cls += ' col-md-' + this.labelmd;
13665                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13666             }
13667             
13668             if(this.labelsm > 0){
13669                 labelCfg.cls += ' col-sm-' + this.labelsm;
13670                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13671             }
13672             
13673             if(this.labelxs > 0){
13674                 labelCfg.cls += ' col-xs-' + this.labelxs;
13675                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13676             }
13677                 
13678                 
13679         } else if ( this.fieldLabel.length) {
13680 //                Roo.log(" label");
13681                  cfg.cn = [
13682                    indicator,
13683                     {
13684                         tag: 'label',
13685                         //cls : 'input-group-addon',
13686                         html : this.fieldLabel
13687                     },
13688                     combobox
13689                 ];
13690                 
13691                 if(this.indicatorpos == 'right'){
13692                     cfg.cn = [
13693                         {
13694                             tag: 'label',
13695                             //cls : 'input-group-addon',
13696                             html : this.fieldLabel
13697                         },
13698                         indicator,
13699                         combobox
13700                     ];
13701                     
13702                 }
13703
13704         } else {
13705             
13706 //                Roo.log(" no label && no align");
13707                 cfg = combobox
13708                      
13709                 
13710         }
13711          
13712         var settings=this;
13713         ['xs','sm','md','lg'].map(function(size){
13714             if (settings[size]) {
13715                 cfg.cls += ' col-' + size + '-' + settings[size];
13716             }
13717         });
13718         
13719         return cfg;
13720         
13721     },
13722     
13723     _initEventsCalled : false,
13724     
13725     // private
13726     initEvents: function()
13727     {   
13728         if (this._initEventsCalled) { // as we call render... prevent looping...
13729             return;
13730         }
13731         this._initEventsCalled = true;
13732         
13733         if (!this.store) {
13734             throw "can not find store for combo";
13735         }
13736         
13737         this.indicator = this.indicatorEl();
13738         
13739         this.store = Roo.factory(this.store, Roo.data);
13740         this.store.parent = this;
13741         
13742         // if we are building from html. then this element is so complex, that we can not really
13743         // use the rendered HTML.
13744         // so we have to trash and replace the previous code.
13745         if (Roo.XComponent.build_from_html) {
13746             // remove this element....
13747             var e = this.el.dom, k=0;
13748             while (e ) { e = e.previousSibling;  ++k;}
13749
13750             this.el.remove();
13751             
13752             this.el=false;
13753             this.rendered = false;
13754             
13755             this.render(this.parent().getChildContainer(true), k);
13756         }
13757         
13758         if(Roo.isIOS && this.useNativeIOS){
13759             this.initIOSView();
13760             return;
13761         }
13762         
13763         /*
13764          * Touch Devices
13765          */
13766         
13767         if(Roo.isTouch && this.mobileTouchView){
13768             this.initTouchView();
13769             return;
13770         }
13771         
13772         if(this.tickable){
13773             this.initTickableEvents();
13774             return;
13775         }
13776         
13777         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13778         
13779         if(this.hiddenName){
13780             
13781             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13782             
13783             this.hiddenField.dom.value =
13784                 this.hiddenValue !== undefined ? this.hiddenValue :
13785                 this.value !== undefined ? this.value : '';
13786
13787             // prevent input submission
13788             this.el.dom.removeAttribute('name');
13789             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13790              
13791              
13792         }
13793         //if(Roo.isGecko){
13794         //    this.el.dom.setAttribute('autocomplete', 'off');
13795         //}
13796         
13797         var cls = 'x-combo-list';
13798         
13799         //this.list = new Roo.Layer({
13800         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13801         //});
13802         
13803         var _this = this;
13804         
13805         (function(){
13806             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13807             _this.list.setWidth(lw);
13808         }).defer(100);
13809         
13810         this.list.on('mouseover', this.onViewOver, this);
13811         this.list.on('mousemove', this.onViewMove, this);
13812         this.list.on('scroll', this.onViewScroll, this);
13813         
13814         /*
13815         this.list.swallowEvent('mousewheel');
13816         this.assetHeight = 0;
13817
13818         if(this.title){
13819             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13820             this.assetHeight += this.header.getHeight();
13821         }
13822
13823         this.innerList = this.list.createChild({cls:cls+'-inner'});
13824         this.innerList.on('mouseover', this.onViewOver, this);
13825         this.innerList.on('mousemove', this.onViewMove, this);
13826         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13827         
13828         if(this.allowBlank && !this.pageSize && !this.disableClear){
13829             this.footer = this.list.createChild({cls:cls+'-ft'});
13830             this.pageTb = new Roo.Toolbar(this.footer);
13831            
13832         }
13833         if(this.pageSize){
13834             this.footer = this.list.createChild({cls:cls+'-ft'});
13835             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13836                     {pageSize: this.pageSize});
13837             
13838         }
13839         
13840         if (this.pageTb && this.allowBlank && !this.disableClear) {
13841             var _this = this;
13842             this.pageTb.add(new Roo.Toolbar.Fill(), {
13843                 cls: 'x-btn-icon x-btn-clear',
13844                 text: '&#160;',
13845                 handler: function()
13846                 {
13847                     _this.collapse();
13848                     _this.clearValue();
13849                     _this.onSelect(false, -1);
13850                 }
13851             });
13852         }
13853         if (this.footer) {
13854             this.assetHeight += this.footer.getHeight();
13855         }
13856         */
13857             
13858         if(!this.tpl){
13859             this.tpl = Roo.bootstrap.version == 4 ?
13860                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
13861                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13862         }
13863
13864         this.view = new Roo.View(this.list, this.tpl, {
13865             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13866         });
13867         //this.view.wrapEl.setDisplayed(false);
13868         this.view.on('click', this.onViewClick, this);
13869         
13870         
13871         this.store.on('beforeload', this.onBeforeLoad, this);
13872         this.store.on('load', this.onLoad, this);
13873         this.store.on('loadexception', this.onLoadException, this);
13874         /*
13875         if(this.resizable){
13876             this.resizer = new Roo.Resizable(this.list,  {
13877                pinned:true, handles:'se'
13878             });
13879             this.resizer.on('resize', function(r, w, h){
13880                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13881                 this.listWidth = w;
13882                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13883                 this.restrictHeight();
13884             }, this);
13885             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13886         }
13887         */
13888         if(!this.editable){
13889             this.editable = true;
13890             this.setEditable(false);
13891         }
13892         
13893         /*
13894         
13895         if (typeof(this.events.add.listeners) != 'undefined') {
13896             
13897             this.addicon = this.wrap.createChild(
13898                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13899        
13900             this.addicon.on('click', function(e) {
13901                 this.fireEvent('add', this);
13902             }, this);
13903         }
13904         if (typeof(this.events.edit.listeners) != 'undefined') {
13905             
13906             this.editicon = this.wrap.createChild(
13907                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13908             if (this.addicon) {
13909                 this.editicon.setStyle('margin-left', '40px');
13910             }
13911             this.editicon.on('click', function(e) {
13912                 
13913                 // we fire even  if inothing is selected..
13914                 this.fireEvent('edit', this, this.lastData );
13915                 
13916             }, this);
13917         }
13918         */
13919         
13920         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13921             "up" : function(e){
13922                 this.inKeyMode = true;
13923                 this.selectPrev();
13924             },
13925
13926             "down" : function(e){
13927                 if(!this.isExpanded()){
13928                     this.onTriggerClick();
13929                 }else{
13930                     this.inKeyMode = true;
13931                     this.selectNext();
13932                 }
13933             },
13934
13935             "enter" : function(e){
13936 //                this.onViewClick();
13937                 //return true;
13938                 this.collapse();
13939                 
13940                 if(this.fireEvent("specialkey", this, e)){
13941                     this.onViewClick(false);
13942                 }
13943                 
13944                 return true;
13945             },
13946
13947             "esc" : function(e){
13948                 this.collapse();
13949             },
13950
13951             "tab" : function(e){
13952                 this.collapse();
13953                 
13954                 if(this.fireEvent("specialkey", this, e)){
13955                     this.onViewClick(false);
13956                 }
13957                 
13958                 return true;
13959             },
13960
13961             scope : this,
13962
13963             doRelay : function(foo, bar, hname){
13964                 if(hname == 'down' || this.scope.isExpanded()){
13965                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13966                 }
13967                 return true;
13968             },
13969
13970             forceKeyDown: true
13971         });
13972         
13973         
13974         this.queryDelay = Math.max(this.queryDelay || 10,
13975                 this.mode == 'local' ? 10 : 250);
13976         
13977         
13978         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13979         
13980         if(this.typeAhead){
13981             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13982         }
13983         if(this.editable !== false){
13984             this.inputEl().on("keyup", this.onKeyUp, this);
13985         }
13986         if(this.forceSelection){
13987             this.inputEl().on('blur', this.doForce, this);
13988         }
13989         
13990         if(this.multiple){
13991             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13992             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13993         }
13994     },
13995     
13996     initTickableEvents: function()
13997     {   
13998         this.createList();
13999         
14000         if(this.hiddenName){
14001             
14002             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14003             
14004             this.hiddenField.dom.value =
14005                 this.hiddenValue !== undefined ? this.hiddenValue :
14006                 this.value !== undefined ? this.value : '';
14007
14008             // prevent input submission
14009             this.el.dom.removeAttribute('name');
14010             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14011              
14012              
14013         }
14014         
14015 //        this.list = this.el.select('ul.dropdown-menu',true).first();
14016         
14017         this.choices = this.el.select('ul.roo-select2-choices', true).first();
14018         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14019         if(this.triggerList){
14020             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
14021         }
14022          
14023         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
14024         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
14025         
14026         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
14027         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
14028         
14029         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
14030         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
14031         
14032         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
14033         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
14034         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
14035         
14036         this.okBtn.hide();
14037         this.cancelBtn.hide();
14038         
14039         var _this = this;
14040         
14041         (function(){
14042             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
14043             _this.list.setWidth(lw);
14044         }).defer(100);
14045         
14046         this.list.on('mouseover', this.onViewOver, this);
14047         this.list.on('mousemove', this.onViewMove, this);
14048         
14049         this.list.on('scroll', this.onViewScroll, this);
14050         
14051         if(!this.tpl){
14052             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
14053                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
14054         }
14055
14056         this.view = new Roo.View(this.list, this.tpl, {
14057             singleSelect:true,
14058             tickable:true,
14059             parent:this,
14060             store: this.store,
14061             selectedClass: this.selectedClass
14062         });
14063         
14064         //this.view.wrapEl.setDisplayed(false);
14065         this.view.on('click', this.onViewClick, this);
14066         
14067         
14068         
14069         this.store.on('beforeload', this.onBeforeLoad, this);
14070         this.store.on('load', this.onLoad, this);
14071         this.store.on('loadexception', this.onLoadException, this);
14072         
14073         if(this.editable){
14074             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
14075                 "up" : function(e){
14076                     this.inKeyMode = true;
14077                     this.selectPrev();
14078                 },
14079
14080                 "down" : function(e){
14081                     this.inKeyMode = true;
14082                     this.selectNext();
14083                 },
14084
14085                 "enter" : function(e){
14086                     if(this.fireEvent("specialkey", this, e)){
14087                         this.onViewClick(false);
14088                     }
14089                     
14090                     return true;
14091                 },
14092
14093                 "esc" : function(e){
14094                     this.onTickableFooterButtonClick(e, false, false);
14095                 },
14096
14097                 "tab" : function(e){
14098                     this.fireEvent("specialkey", this, e);
14099                     
14100                     this.onTickableFooterButtonClick(e, false, false);
14101                     
14102                     return true;
14103                 },
14104
14105                 scope : this,
14106
14107                 doRelay : function(e, fn, key){
14108                     if(this.scope.isExpanded()){
14109                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14110                     }
14111                     return true;
14112                 },
14113
14114                 forceKeyDown: true
14115             });
14116         }
14117         
14118         this.queryDelay = Math.max(this.queryDelay || 10,
14119                 this.mode == 'local' ? 10 : 250);
14120         
14121         
14122         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14123         
14124         if(this.typeAhead){
14125             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14126         }
14127         
14128         if(this.editable !== false){
14129             this.tickableInputEl().on("keyup", this.onKeyUp, this);
14130         }
14131         
14132         this.indicator = this.indicatorEl();
14133         
14134         if(this.indicator){
14135             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
14136             this.indicator.hide();
14137         }
14138         
14139     },
14140
14141     onDestroy : function(){
14142         if(this.view){
14143             this.view.setStore(null);
14144             this.view.el.removeAllListeners();
14145             this.view.el.remove();
14146             this.view.purgeListeners();
14147         }
14148         if(this.list){
14149             this.list.dom.innerHTML  = '';
14150         }
14151         
14152         if(this.store){
14153             this.store.un('beforeload', this.onBeforeLoad, this);
14154             this.store.un('load', this.onLoad, this);
14155             this.store.un('loadexception', this.onLoadException, this);
14156         }
14157         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
14158     },
14159
14160     // private
14161     fireKey : function(e){
14162         if(e.isNavKeyPress() && !this.list.isVisible()){
14163             this.fireEvent("specialkey", this, e);
14164         }
14165     },
14166
14167     // private
14168     onResize: function(w, h){
14169 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14170 //        
14171 //        if(typeof w != 'number'){
14172 //            // we do not handle it!?!?
14173 //            return;
14174 //        }
14175 //        var tw = this.trigger.getWidth();
14176 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
14177 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
14178 //        var x = w - tw;
14179 //        this.inputEl().setWidth( this.adjustWidth('input', x));
14180 //            
14181 //        //this.trigger.setStyle('left', x+'px');
14182 //        
14183 //        if(this.list && this.listWidth === undefined){
14184 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14185 //            this.list.setWidth(lw);
14186 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14187 //        }
14188         
14189     
14190         
14191     },
14192
14193     /**
14194      * Allow or prevent the user from directly editing the field text.  If false is passed,
14195      * the user will only be able to select from the items defined in the dropdown list.  This method
14196      * is the runtime equivalent of setting the 'editable' config option at config time.
14197      * @param {Boolean} value True to allow the user to directly edit the field text
14198      */
14199     setEditable : function(value){
14200         if(value == this.editable){
14201             return;
14202         }
14203         this.editable = value;
14204         if(!value){
14205             this.inputEl().dom.setAttribute('readOnly', true);
14206             this.inputEl().on('mousedown', this.onTriggerClick,  this);
14207             this.inputEl().addClass('x-combo-noedit');
14208         }else{
14209             this.inputEl().dom.setAttribute('readOnly', false);
14210             this.inputEl().un('mousedown', this.onTriggerClick,  this);
14211             this.inputEl().removeClass('x-combo-noedit');
14212         }
14213     },
14214
14215     // private
14216     
14217     onBeforeLoad : function(combo,opts){
14218         if(!this.hasFocus){
14219             return;
14220         }
14221          if (!opts.add) {
14222             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14223          }
14224         this.restrictHeight();
14225         this.selectedIndex = -1;
14226     },
14227
14228     // private
14229     onLoad : function(){
14230         
14231         this.hasQuery = false;
14232         
14233         if(!this.hasFocus){
14234             return;
14235         }
14236         
14237         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14238             this.loading.hide();
14239         }
14240         
14241         if(this.store.getCount() > 0){
14242             
14243             this.expand();
14244             this.restrictHeight();
14245             if(this.lastQuery == this.allQuery){
14246                 if(this.editable && !this.tickable){
14247                     this.inputEl().dom.select();
14248                 }
14249                 
14250                 if(
14251                     !this.selectByValue(this.value, true) &&
14252                     this.autoFocus && 
14253                     (
14254                         !this.store.lastOptions ||
14255                         typeof(this.store.lastOptions.add) == 'undefined' || 
14256                         this.store.lastOptions.add != true
14257                     )
14258                 ){
14259                     this.select(0, true);
14260                 }
14261             }else{
14262                 if(this.autoFocus){
14263                     this.selectNext();
14264                 }
14265                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14266                     this.taTask.delay(this.typeAheadDelay);
14267                 }
14268             }
14269         }else{
14270             this.onEmptyResults();
14271         }
14272         
14273         //this.el.focus();
14274     },
14275     // private
14276     onLoadException : function()
14277     {
14278         this.hasQuery = false;
14279         
14280         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14281             this.loading.hide();
14282         }
14283         
14284         if(this.tickable && this.editable){
14285             return;
14286         }
14287         
14288         this.collapse();
14289         // only causes errors at present
14290         //Roo.log(this.store.reader.jsonData);
14291         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14292             // fixme
14293             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14294         //}
14295         
14296         
14297     },
14298     // private
14299     onTypeAhead : function(){
14300         if(this.store.getCount() > 0){
14301             var r = this.store.getAt(0);
14302             var newValue = r.data[this.displayField];
14303             var len = newValue.length;
14304             var selStart = this.getRawValue().length;
14305             
14306             if(selStart != len){
14307                 this.setRawValue(newValue);
14308                 this.selectText(selStart, newValue.length);
14309             }
14310         }
14311     },
14312
14313     // private
14314     onSelect : function(record, index){
14315         
14316         if(this.fireEvent('beforeselect', this, record, index) !== false){
14317         
14318             this.setFromData(index > -1 ? record.data : false);
14319             
14320             this.collapse();
14321             this.fireEvent('select', this, record, index);
14322         }
14323     },
14324
14325     /**
14326      * Returns the currently selected field value or empty string if no value is set.
14327      * @return {String} value The selected value
14328      */
14329     getValue : function()
14330     {
14331         if(Roo.isIOS && this.useNativeIOS){
14332             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14333         }
14334         
14335         if(this.multiple){
14336             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14337         }
14338         
14339         if(this.valueField){
14340             return typeof this.value != 'undefined' ? this.value : '';
14341         }else{
14342             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14343         }
14344     },
14345     
14346     getRawValue : function()
14347     {
14348         if(Roo.isIOS && this.useNativeIOS){
14349             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14350         }
14351         
14352         var v = this.inputEl().getValue();
14353         
14354         return v;
14355     },
14356
14357     /**
14358      * Clears any text/value currently set in the field
14359      */
14360     clearValue : function(){
14361         
14362         if(this.hiddenField){
14363             this.hiddenField.dom.value = '';
14364         }
14365         this.value = '';
14366         this.setRawValue('');
14367         this.lastSelectionText = '';
14368         this.lastData = false;
14369         
14370         var close = this.closeTriggerEl();
14371         
14372         if(close){
14373             close.hide();
14374         }
14375         
14376         this.validate();
14377         
14378     },
14379
14380     /**
14381      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14382      * will be displayed in the field.  If the value does not match the data value of an existing item,
14383      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14384      * Otherwise the field will be blank (although the value will still be set).
14385      * @param {String} value The value to match
14386      */
14387     setValue : function(v)
14388     {
14389         if(Roo.isIOS && this.useNativeIOS){
14390             this.setIOSValue(v);
14391             return;
14392         }
14393         
14394         if(this.multiple){
14395             this.syncValue();
14396             return;
14397         }
14398         
14399         var text = v;
14400         if(this.valueField){
14401             var r = this.findRecord(this.valueField, v);
14402             if(r){
14403                 text = r.data[this.displayField];
14404             }else if(this.valueNotFoundText !== undefined){
14405                 text = this.valueNotFoundText;
14406             }
14407         }
14408         this.lastSelectionText = text;
14409         if(this.hiddenField){
14410             this.hiddenField.dom.value = v;
14411         }
14412         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14413         this.value = v;
14414         
14415         var close = this.closeTriggerEl();
14416         
14417         if(close){
14418             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14419         }
14420         
14421         this.validate();
14422     },
14423     /**
14424      * @property {Object} the last set data for the element
14425      */
14426     
14427     lastData : false,
14428     /**
14429      * Sets the value of the field based on a object which is related to the record format for the store.
14430      * @param {Object} value the value to set as. or false on reset?
14431      */
14432     setFromData : function(o){
14433         
14434         if(this.multiple){
14435             this.addItem(o);
14436             return;
14437         }
14438             
14439         var dv = ''; // display value
14440         var vv = ''; // value value..
14441         this.lastData = o;
14442         if (this.displayField) {
14443             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14444         } else {
14445             // this is an error condition!!!
14446             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14447         }
14448         
14449         if(this.valueField){
14450             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14451         }
14452         
14453         var close = this.closeTriggerEl();
14454         
14455         if(close){
14456             if(dv.length || vv * 1 > 0){
14457                 close.show() ;
14458                 this.blockFocus=true;
14459             } else {
14460                 close.hide();
14461             }             
14462         }
14463         
14464         if(this.hiddenField){
14465             this.hiddenField.dom.value = vv;
14466             
14467             this.lastSelectionText = dv;
14468             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14469             this.value = vv;
14470             return;
14471         }
14472         // no hidden field.. - we store the value in 'value', but still display
14473         // display field!!!!
14474         this.lastSelectionText = dv;
14475         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14476         this.value = vv;
14477         
14478         
14479         
14480     },
14481     // private
14482     reset : function(){
14483         // overridden so that last data is reset..
14484         
14485         if(this.multiple){
14486             this.clearItem();
14487             return;
14488         }
14489         
14490         this.setValue(this.originalValue);
14491         //this.clearInvalid();
14492         this.lastData = false;
14493         if (this.view) {
14494             this.view.clearSelections();
14495         }
14496         
14497         this.validate();
14498     },
14499     // private
14500     findRecord : function(prop, value){
14501         var record;
14502         if(this.store.getCount() > 0){
14503             this.store.each(function(r){
14504                 if(r.data[prop] == value){
14505                     record = r;
14506                     return false;
14507                 }
14508                 return true;
14509             });
14510         }
14511         return record;
14512     },
14513     
14514     getName: function()
14515     {
14516         // returns hidden if it's set..
14517         if (!this.rendered) {return ''};
14518         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14519         
14520     },
14521     // private
14522     onViewMove : function(e, t){
14523         this.inKeyMode = false;
14524     },
14525
14526     // private
14527     onViewOver : function(e, t){
14528         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14529             return;
14530         }
14531         var item = this.view.findItemFromChild(t);
14532         
14533         if(item){
14534             var index = this.view.indexOf(item);
14535             this.select(index, false);
14536         }
14537     },
14538
14539     // private
14540     onViewClick : function(view, doFocus, el, e)
14541     {
14542         var index = this.view.getSelectedIndexes()[0];
14543         
14544         var r = this.store.getAt(index);
14545         
14546         if(this.tickable){
14547             
14548             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14549                 return;
14550             }
14551             
14552             var rm = false;
14553             var _this = this;
14554             
14555             Roo.each(this.tickItems, function(v,k){
14556                 
14557                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14558                     Roo.log(v);
14559                     _this.tickItems.splice(k, 1);
14560                     
14561                     if(typeof(e) == 'undefined' && view == false){
14562                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14563                     }
14564                     
14565                     rm = true;
14566                     return;
14567                 }
14568             });
14569             
14570             if(rm){
14571                 return;
14572             }
14573             
14574             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14575                 this.tickItems.push(r.data);
14576             }
14577             
14578             if(typeof(e) == 'undefined' && view == false){
14579                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14580             }
14581                     
14582             return;
14583         }
14584         
14585         if(r){
14586             this.onSelect(r, index);
14587         }
14588         if(doFocus !== false && !this.blockFocus){
14589             this.inputEl().focus();
14590         }
14591     },
14592
14593     // private
14594     restrictHeight : function(){
14595         //this.innerList.dom.style.height = '';
14596         //var inner = this.innerList.dom;
14597         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14598         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14599         //this.list.beginUpdate();
14600         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14601         this.list.alignTo(this.inputEl(), this.listAlign);
14602         this.list.alignTo(this.inputEl(), this.listAlign);
14603         //this.list.endUpdate();
14604     },
14605
14606     // private
14607     onEmptyResults : function(){
14608         
14609         if(this.tickable && this.editable){
14610             this.hasFocus = false;
14611             this.restrictHeight();
14612             return;
14613         }
14614         
14615         this.collapse();
14616     },
14617
14618     /**
14619      * Returns true if the dropdown list is expanded, else false.
14620      */
14621     isExpanded : function(){
14622         return this.list.isVisible();
14623     },
14624
14625     /**
14626      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14627      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14628      * @param {String} value The data value of the item to select
14629      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14630      * selected item if it is not currently in view (defaults to true)
14631      * @return {Boolean} True if the value matched an item in the list, else false
14632      */
14633     selectByValue : function(v, scrollIntoView){
14634         if(v !== undefined && v !== null){
14635             var r = this.findRecord(this.valueField || this.displayField, v);
14636             if(r){
14637                 this.select(this.store.indexOf(r), scrollIntoView);
14638                 return true;
14639             }
14640         }
14641         return false;
14642     },
14643
14644     /**
14645      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14646      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14647      * @param {Number} index The zero-based index of the list item to select
14648      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14649      * selected item if it is not currently in view (defaults to true)
14650      */
14651     select : function(index, scrollIntoView){
14652         this.selectedIndex = index;
14653         this.view.select(index);
14654         if(scrollIntoView !== false){
14655             var el = this.view.getNode(index);
14656             /*
14657              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14658              */
14659             if(el){
14660                 this.list.scrollChildIntoView(el, false);
14661             }
14662         }
14663     },
14664
14665     // private
14666     selectNext : function(){
14667         var ct = this.store.getCount();
14668         if(ct > 0){
14669             if(this.selectedIndex == -1){
14670                 this.select(0);
14671             }else if(this.selectedIndex < ct-1){
14672                 this.select(this.selectedIndex+1);
14673             }
14674         }
14675     },
14676
14677     // private
14678     selectPrev : function(){
14679         var ct = this.store.getCount();
14680         if(ct > 0){
14681             if(this.selectedIndex == -1){
14682                 this.select(0);
14683             }else if(this.selectedIndex != 0){
14684                 this.select(this.selectedIndex-1);
14685             }
14686         }
14687     },
14688
14689     // private
14690     onKeyUp : function(e){
14691         if(this.editable !== false && !e.isSpecialKey()){
14692             this.lastKey = e.getKey();
14693             this.dqTask.delay(this.queryDelay);
14694         }
14695     },
14696
14697     // private
14698     validateBlur : function(){
14699         return !this.list || !this.list.isVisible();   
14700     },
14701
14702     // private
14703     initQuery : function(){
14704         
14705         var v = this.getRawValue();
14706         
14707         if(this.tickable && this.editable){
14708             v = this.tickableInputEl().getValue();
14709         }
14710         
14711         this.doQuery(v);
14712     },
14713
14714     // private
14715     doForce : function(){
14716         if(this.inputEl().dom.value.length > 0){
14717             this.inputEl().dom.value =
14718                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14719              
14720         }
14721     },
14722
14723     /**
14724      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14725      * query allowing the query action to be canceled if needed.
14726      * @param {String} query The SQL query to execute
14727      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14728      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14729      * saved in the current store (defaults to false)
14730      */
14731     doQuery : function(q, forceAll){
14732         
14733         if(q === undefined || q === null){
14734             q = '';
14735         }
14736         var qe = {
14737             query: q,
14738             forceAll: forceAll,
14739             combo: this,
14740             cancel:false
14741         };
14742         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14743             return false;
14744         }
14745         q = qe.query;
14746         
14747         forceAll = qe.forceAll;
14748         if(forceAll === true || (q.length >= this.minChars)){
14749             
14750             this.hasQuery = true;
14751             
14752             if(this.lastQuery != q || this.alwaysQuery){
14753                 this.lastQuery = q;
14754                 if(this.mode == 'local'){
14755                     this.selectedIndex = -1;
14756                     if(forceAll){
14757                         this.store.clearFilter();
14758                     }else{
14759                         
14760                         if(this.specialFilter){
14761                             this.fireEvent('specialfilter', this);
14762                             this.onLoad();
14763                             return;
14764                         }
14765                         
14766                         this.store.filter(this.displayField, q);
14767                     }
14768                     
14769                     this.store.fireEvent("datachanged", this.store);
14770                     
14771                     this.onLoad();
14772                     
14773                     
14774                 }else{
14775                     
14776                     this.store.baseParams[this.queryParam] = q;
14777                     
14778                     var options = {params : this.getParams(q)};
14779                     
14780                     if(this.loadNext){
14781                         options.add = true;
14782                         options.params.start = this.page * this.pageSize;
14783                     }
14784                     
14785                     this.store.load(options);
14786                     
14787                     /*
14788                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14789                      *  we should expand the list on onLoad
14790                      *  so command out it
14791                      */
14792 //                    this.expand();
14793                 }
14794             }else{
14795                 this.selectedIndex = -1;
14796                 this.onLoad();   
14797             }
14798         }
14799         
14800         this.loadNext = false;
14801     },
14802     
14803     // private
14804     getParams : function(q){
14805         var p = {};
14806         //p[this.queryParam] = q;
14807         
14808         if(this.pageSize){
14809             p.start = 0;
14810             p.limit = this.pageSize;
14811         }
14812         return p;
14813     },
14814
14815     /**
14816      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14817      */
14818     collapse : function(){
14819         if(!this.isExpanded()){
14820             return;
14821         }
14822         
14823         this.list.hide();
14824         
14825         this.hasFocus = false;
14826         
14827         if(this.tickable){
14828             this.okBtn.hide();
14829             this.cancelBtn.hide();
14830             this.trigger.show();
14831             
14832             if(this.editable){
14833                 this.tickableInputEl().dom.value = '';
14834                 this.tickableInputEl().blur();
14835             }
14836             
14837         }
14838         
14839         Roo.get(document).un('mousedown', this.collapseIf, this);
14840         Roo.get(document).un('mousewheel', this.collapseIf, this);
14841         if (!this.editable) {
14842             Roo.get(document).un('keydown', this.listKeyPress, this);
14843         }
14844         this.fireEvent('collapse', this);
14845         
14846         this.validate();
14847     },
14848
14849     // private
14850     collapseIf : function(e){
14851         var in_combo  = e.within(this.el);
14852         var in_list =  e.within(this.list);
14853         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14854         
14855         if (in_combo || in_list || is_list) {
14856             //e.stopPropagation();
14857             return;
14858         }
14859         
14860         if(this.tickable){
14861             this.onTickableFooterButtonClick(e, false, false);
14862         }
14863
14864         this.collapse();
14865         
14866     },
14867
14868     /**
14869      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14870      */
14871     expand : function(){
14872        
14873         if(this.isExpanded() || !this.hasFocus){
14874             return;
14875         }
14876         
14877         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14878         this.list.setWidth(lw);
14879         
14880         Roo.log('expand');
14881         
14882         this.list.show();
14883         
14884         this.restrictHeight();
14885         
14886         if(this.tickable){
14887             
14888             this.tickItems = Roo.apply([], this.item);
14889             
14890             this.okBtn.show();
14891             this.cancelBtn.show();
14892             this.trigger.hide();
14893             
14894             if(this.editable){
14895                 this.tickableInputEl().focus();
14896             }
14897             
14898         }
14899         
14900         Roo.get(document).on('mousedown', this.collapseIf, this);
14901         Roo.get(document).on('mousewheel', this.collapseIf, this);
14902         if (!this.editable) {
14903             Roo.get(document).on('keydown', this.listKeyPress, this);
14904         }
14905         
14906         this.fireEvent('expand', this);
14907     },
14908
14909     // private
14910     // Implements the default empty TriggerField.onTriggerClick function
14911     onTriggerClick : function(e)
14912     {
14913         Roo.log('trigger click');
14914         
14915         if(this.disabled || !this.triggerList){
14916             return;
14917         }
14918         
14919         this.page = 0;
14920         this.loadNext = false;
14921         
14922         if(this.isExpanded()){
14923             this.collapse();
14924             if (!this.blockFocus) {
14925                 this.inputEl().focus();
14926             }
14927             
14928         }else {
14929             this.hasFocus = true;
14930             if(this.triggerAction == 'all') {
14931                 this.doQuery(this.allQuery, true);
14932             } else {
14933                 this.doQuery(this.getRawValue());
14934             }
14935             if (!this.blockFocus) {
14936                 this.inputEl().focus();
14937             }
14938         }
14939     },
14940     
14941     onTickableTriggerClick : function(e)
14942     {
14943         if(this.disabled){
14944             return;
14945         }
14946         
14947         this.page = 0;
14948         this.loadNext = false;
14949         this.hasFocus = true;
14950         
14951         if(this.triggerAction == 'all') {
14952             this.doQuery(this.allQuery, true);
14953         } else {
14954             this.doQuery(this.getRawValue());
14955         }
14956     },
14957     
14958     onSearchFieldClick : function(e)
14959     {
14960         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14961             this.onTickableFooterButtonClick(e, false, false);
14962             return;
14963         }
14964         
14965         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14966             return;
14967         }
14968         
14969         this.page = 0;
14970         this.loadNext = false;
14971         this.hasFocus = true;
14972         
14973         if(this.triggerAction == 'all') {
14974             this.doQuery(this.allQuery, true);
14975         } else {
14976             this.doQuery(this.getRawValue());
14977         }
14978     },
14979     
14980     listKeyPress : function(e)
14981     {
14982         //Roo.log('listkeypress');
14983         // scroll to first matching element based on key pres..
14984         if (e.isSpecialKey()) {
14985             return false;
14986         }
14987         var k = String.fromCharCode(e.getKey()).toUpperCase();
14988         //Roo.log(k);
14989         var match  = false;
14990         var csel = this.view.getSelectedNodes();
14991         var cselitem = false;
14992         if (csel.length) {
14993             var ix = this.view.indexOf(csel[0]);
14994             cselitem  = this.store.getAt(ix);
14995             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14996                 cselitem = false;
14997             }
14998             
14999         }
15000         
15001         this.store.each(function(v) { 
15002             if (cselitem) {
15003                 // start at existing selection.
15004                 if (cselitem.id == v.id) {
15005                     cselitem = false;
15006                 }
15007                 return true;
15008             }
15009                 
15010             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
15011                 match = this.store.indexOf(v);
15012                 return false;
15013             }
15014             return true;
15015         }, this);
15016         
15017         if (match === false) {
15018             return true; // no more action?
15019         }
15020         // scroll to?
15021         this.view.select(match);
15022         var sn = Roo.get(this.view.getSelectedNodes()[0]);
15023         sn.scrollIntoView(sn.dom.parentNode, false);
15024     },
15025     
15026     onViewScroll : function(e, t){
15027         
15028         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){
15029             return;
15030         }
15031         
15032         this.hasQuery = true;
15033         
15034         this.loading = this.list.select('.loading', true).first();
15035         
15036         if(this.loading === null){
15037             this.list.createChild({
15038                 tag: 'div',
15039                 cls: 'loading roo-select2-more-results roo-select2-active',
15040                 html: 'Loading more results...'
15041             });
15042             
15043             this.loading = this.list.select('.loading', true).first();
15044             
15045             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
15046             
15047             this.loading.hide();
15048         }
15049         
15050         this.loading.show();
15051         
15052         var _combo = this;
15053         
15054         this.page++;
15055         this.loadNext = true;
15056         
15057         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
15058         
15059         return;
15060     },
15061     
15062     addItem : function(o)
15063     {   
15064         var dv = ''; // display value
15065         
15066         if (this.displayField) {
15067             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
15068         } else {
15069             // this is an error condition!!!
15070             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
15071         }
15072         
15073         if(!dv.length){
15074             return;
15075         }
15076         
15077         var choice = this.choices.createChild({
15078             tag: 'li',
15079             cls: 'roo-select2-search-choice',
15080             cn: [
15081                 {
15082                     tag: 'div',
15083                     html: dv
15084                 },
15085                 {
15086                     tag: 'a',
15087                     href: '#',
15088                     cls: 'roo-select2-search-choice-close fa fa-times',
15089                     tabindex: '-1'
15090                 }
15091             ]
15092             
15093         }, this.searchField);
15094         
15095         var close = choice.select('a.roo-select2-search-choice-close', true).first();
15096         
15097         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
15098         
15099         this.item.push(o);
15100         
15101         this.lastData = o;
15102         
15103         this.syncValue();
15104         
15105         this.inputEl().dom.value = '';
15106         
15107         this.validate();
15108     },
15109     
15110     onRemoveItem : function(e, _self, o)
15111     {
15112         e.preventDefault();
15113         
15114         this.lastItem = Roo.apply([], this.item);
15115         
15116         var index = this.item.indexOf(o.data) * 1;
15117         
15118         if( index < 0){
15119             Roo.log('not this item?!');
15120             return;
15121         }
15122         
15123         this.item.splice(index, 1);
15124         o.item.remove();
15125         
15126         this.syncValue();
15127         
15128         this.fireEvent('remove', this, e);
15129         
15130         this.validate();
15131         
15132     },
15133     
15134     syncValue : function()
15135     {
15136         if(!this.item.length){
15137             this.clearValue();
15138             return;
15139         }
15140             
15141         var value = [];
15142         var _this = this;
15143         Roo.each(this.item, function(i){
15144             if(_this.valueField){
15145                 value.push(i[_this.valueField]);
15146                 return;
15147             }
15148
15149             value.push(i);
15150         });
15151
15152         this.value = value.join(',');
15153
15154         if(this.hiddenField){
15155             this.hiddenField.dom.value = this.value;
15156         }
15157         
15158         this.store.fireEvent("datachanged", this.store);
15159         
15160         this.validate();
15161     },
15162     
15163     clearItem : function()
15164     {
15165         if(!this.multiple){
15166             return;
15167         }
15168         
15169         this.item = [];
15170         
15171         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15172            c.remove();
15173         });
15174         
15175         this.syncValue();
15176         
15177         this.validate();
15178         
15179         if(this.tickable && !Roo.isTouch){
15180             this.view.refresh();
15181         }
15182     },
15183     
15184     inputEl: function ()
15185     {
15186         if(Roo.isIOS && this.useNativeIOS){
15187             return this.el.select('select.roo-ios-select', true).first();
15188         }
15189         
15190         if(Roo.isTouch && this.mobileTouchView){
15191             return this.el.select('input.form-control',true).first();
15192         }
15193         
15194         if(this.tickable){
15195             return this.searchField;
15196         }
15197         
15198         return this.el.select('input.form-control',true).first();
15199     },
15200     
15201     onTickableFooterButtonClick : function(e, btn, el)
15202     {
15203         e.preventDefault();
15204         
15205         this.lastItem = Roo.apply([], this.item);
15206         
15207         if(btn && btn.name == 'cancel'){
15208             this.tickItems = Roo.apply([], this.item);
15209             this.collapse();
15210             return;
15211         }
15212         
15213         this.clearItem();
15214         
15215         var _this = this;
15216         
15217         Roo.each(this.tickItems, function(o){
15218             _this.addItem(o);
15219         });
15220         
15221         this.collapse();
15222         
15223     },
15224     
15225     validate : function()
15226     {
15227         if(this.getVisibilityEl().hasClass('hidden')){
15228             return true;
15229         }
15230         
15231         var v = this.getRawValue();
15232         
15233         if(this.multiple){
15234             v = this.getValue();
15235         }
15236         
15237         if(this.disabled || this.allowBlank || v.length){
15238             this.markValid();
15239             return true;
15240         }
15241         
15242         this.markInvalid();
15243         return false;
15244     },
15245     
15246     tickableInputEl : function()
15247     {
15248         if(!this.tickable || !this.editable){
15249             return this.inputEl();
15250         }
15251         
15252         return this.inputEl().select('.roo-select2-search-field-input', true).first();
15253     },
15254     
15255     
15256     getAutoCreateTouchView : function()
15257     {
15258         var id = Roo.id();
15259         
15260         var cfg = {
15261             cls: 'form-group' //input-group
15262         };
15263         
15264         var input =  {
15265             tag: 'input',
15266             id : id,
15267             type : this.inputType,
15268             cls : 'form-control x-combo-noedit',
15269             autocomplete: 'new-password',
15270             placeholder : this.placeholder || '',
15271             readonly : true
15272         };
15273         
15274         if (this.name) {
15275             input.name = this.name;
15276         }
15277         
15278         if (this.size) {
15279             input.cls += ' input-' + this.size;
15280         }
15281         
15282         if (this.disabled) {
15283             input.disabled = true;
15284         }
15285         
15286         var inputblock = {
15287             cls : '',
15288             cn : [
15289                 input
15290             ]
15291         };
15292         
15293         if(this.before){
15294             inputblock.cls += ' input-group';
15295             
15296             inputblock.cn.unshift({
15297                 tag :'span',
15298                 cls : 'input-group-addon input-group-prepend input-group-text',
15299                 html : this.before
15300             });
15301         }
15302         
15303         if(this.removable && !this.multiple){
15304             inputblock.cls += ' roo-removable';
15305             
15306             inputblock.cn.push({
15307                 tag: 'button',
15308                 html : 'x',
15309                 cls : 'roo-combo-removable-btn close'
15310             });
15311         }
15312
15313         if(this.hasFeedback && !this.allowBlank){
15314             
15315             inputblock.cls += ' has-feedback';
15316             
15317             inputblock.cn.push({
15318                 tag: 'span',
15319                 cls: 'glyphicon form-control-feedback'
15320             });
15321             
15322         }
15323         
15324         if (this.after) {
15325             
15326             inputblock.cls += (this.before) ? '' : ' input-group';
15327             
15328             inputblock.cn.push({
15329                 tag :'span',
15330                 cls : 'input-group-addon input-group-append input-group-text',
15331                 html : this.after
15332             });
15333         }
15334
15335         
15336         var ibwrap = inputblock;
15337         
15338         if(this.multiple){
15339             ibwrap = {
15340                 tag: 'ul',
15341                 cls: 'roo-select2-choices',
15342                 cn:[
15343                     {
15344                         tag: 'li',
15345                         cls: 'roo-select2-search-field',
15346                         cn: [
15347
15348                             inputblock
15349                         ]
15350                     }
15351                 ]
15352             };
15353         
15354             
15355         }
15356         
15357         var combobox = {
15358             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15359             cn: [
15360                 {
15361                     tag: 'input',
15362                     type : 'hidden',
15363                     cls: 'form-hidden-field'
15364                 },
15365                 ibwrap
15366             ]
15367         };
15368         
15369         if(!this.multiple && this.showToggleBtn){
15370             
15371             var caret = {
15372                         tag: 'span',
15373                         cls: 'caret'
15374             };
15375             
15376             if (this.caret != false) {
15377                 caret = {
15378                      tag: 'i',
15379                      cls: 'fa fa-' + this.caret
15380                 };
15381                 
15382             }
15383             
15384             combobox.cn.push({
15385                 tag :'span',
15386                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15387                 cn : [
15388                     caret,
15389                     {
15390                         tag: 'span',
15391                         cls: 'combobox-clear',
15392                         cn  : [
15393                             {
15394                                 tag : 'i',
15395                                 cls: 'icon-remove'
15396                             }
15397                         ]
15398                     }
15399                 ]
15400
15401             })
15402         }
15403         
15404         if(this.multiple){
15405             combobox.cls += ' roo-select2-container-multi';
15406         }
15407         
15408         var align = this.labelAlign || this.parentLabelAlign();
15409         
15410         if (align ==='left' && this.fieldLabel.length) {
15411
15412             cfg.cn = [
15413                 {
15414                    tag : 'i',
15415                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15416                    tooltip : 'This field is required'
15417                 },
15418                 {
15419                     tag: 'label',
15420                     cls : 'control-label col-form-label',
15421                     html : this.fieldLabel
15422
15423                 },
15424                 {
15425                     cls : '', 
15426                     cn: [
15427                         combobox
15428                     ]
15429                 }
15430             ];
15431             
15432             var labelCfg = cfg.cn[1];
15433             var contentCfg = cfg.cn[2];
15434             
15435
15436             if(this.indicatorpos == 'right'){
15437                 cfg.cn = [
15438                     {
15439                         tag: 'label',
15440                         'for' :  id,
15441                         cls : 'control-label col-form-label',
15442                         cn : [
15443                             {
15444                                 tag : 'span',
15445                                 html : this.fieldLabel
15446                             },
15447                             {
15448                                 tag : 'i',
15449                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15450                                 tooltip : 'This field is required'
15451                             }
15452                         ]
15453                     },
15454                     {
15455                         cls : "",
15456                         cn: [
15457                             combobox
15458                         ]
15459                     }
15460
15461                 ];
15462                 
15463                 labelCfg = cfg.cn[0];
15464                 contentCfg = cfg.cn[1];
15465             }
15466             
15467            
15468             
15469             if(this.labelWidth > 12){
15470                 labelCfg.style = "width: " + this.labelWidth + 'px';
15471             }
15472             
15473             if(this.labelWidth < 13 && this.labelmd == 0){
15474                 this.labelmd = this.labelWidth;
15475             }
15476             
15477             if(this.labellg > 0){
15478                 labelCfg.cls += ' col-lg-' + this.labellg;
15479                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15480             }
15481             
15482             if(this.labelmd > 0){
15483                 labelCfg.cls += ' col-md-' + this.labelmd;
15484                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15485             }
15486             
15487             if(this.labelsm > 0){
15488                 labelCfg.cls += ' col-sm-' + this.labelsm;
15489                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15490             }
15491             
15492             if(this.labelxs > 0){
15493                 labelCfg.cls += ' col-xs-' + this.labelxs;
15494                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15495             }
15496                 
15497                 
15498         } else if ( this.fieldLabel.length) {
15499             cfg.cn = [
15500                 {
15501                    tag : 'i',
15502                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15503                    tooltip : 'This field is required'
15504                 },
15505                 {
15506                     tag: 'label',
15507                     cls : 'control-label',
15508                     html : this.fieldLabel
15509
15510                 },
15511                 {
15512                     cls : '', 
15513                     cn: [
15514                         combobox
15515                     ]
15516                 }
15517             ];
15518             
15519             if(this.indicatorpos == 'right'){
15520                 cfg.cn = [
15521                     {
15522                         tag: 'label',
15523                         cls : 'control-label',
15524                         html : this.fieldLabel,
15525                         cn : [
15526                             {
15527                                tag : 'i',
15528                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15529                                tooltip : 'This field is required'
15530                             }
15531                         ]
15532                     },
15533                     {
15534                         cls : '', 
15535                         cn: [
15536                             combobox
15537                         ]
15538                     }
15539                 ];
15540             }
15541         } else {
15542             cfg.cn = combobox;    
15543         }
15544         
15545         
15546         var settings = this;
15547         
15548         ['xs','sm','md','lg'].map(function(size){
15549             if (settings[size]) {
15550                 cfg.cls += ' col-' + size + '-' + settings[size];
15551             }
15552         });
15553         
15554         return cfg;
15555     },
15556     
15557     initTouchView : function()
15558     {
15559         this.renderTouchView();
15560         
15561         this.touchViewEl.on('scroll', function(){
15562             this.el.dom.scrollTop = 0;
15563         }, this);
15564         
15565         this.originalValue = this.getValue();
15566         
15567         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15568         
15569         this.inputEl().on("click", this.showTouchView, this);
15570         if (this.triggerEl) {
15571             this.triggerEl.on("click", this.showTouchView, this);
15572         }
15573         
15574         
15575         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15576         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15577         
15578         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15579         
15580         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15581         this.store.on('load', this.onTouchViewLoad, this);
15582         this.store.on('loadexception', this.onTouchViewLoadException, this);
15583         
15584         if(this.hiddenName){
15585             
15586             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15587             
15588             this.hiddenField.dom.value =
15589                 this.hiddenValue !== undefined ? this.hiddenValue :
15590                 this.value !== undefined ? this.value : '';
15591         
15592             this.el.dom.removeAttribute('name');
15593             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15594         }
15595         
15596         if(this.multiple){
15597             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15598             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15599         }
15600         
15601         if(this.removable && !this.multiple){
15602             var close = this.closeTriggerEl();
15603             if(close){
15604                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15605                 close.on('click', this.removeBtnClick, this, close);
15606             }
15607         }
15608         /*
15609          * fix the bug in Safari iOS8
15610          */
15611         this.inputEl().on("focus", function(e){
15612             document.activeElement.blur();
15613         }, this);
15614         
15615         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15616         
15617         return;
15618         
15619         
15620     },
15621     
15622     renderTouchView : function()
15623     {
15624         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15625         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15626         
15627         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15628         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15629         
15630         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15631         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15632         this.touchViewBodyEl.setStyle('overflow', 'auto');
15633         
15634         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15635         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15636         
15637         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15638         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15639         
15640     },
15641     
15642     showTouchView : function()
15643     {
15644         if(this.disabled){
15645             return;
15646         }
15647         
15648         this.touchViewHeaderEl.hide();
15649
15650         if(this.modalTitle.length){
15651             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15652             this.touchViewHeaderEl.show();
15653         }
15654
15655         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15656         this.touchViewEl.show();
15657
15658         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15659         
15660         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15661         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15662
15663         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15664
15665         if(this.modalTitle.length){
15666             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15667         }
15668         
15669         this.touchViewBodyEl.setHeight(bodyHeight);
15670
15671         if(this.animate){
15672             var _this = this;
15673             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15674         }else{
15675             this.touchViewEl.addClass('in');
15676         }
15677         
15678         if(this._touchViewMask){
15679             Roo.get(document.body).addClass("x-body-masked");
15680             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15681             this._touchViewMask.setStyle('z-index', 10000);
15682             this._touchViewMask.addClass('show');
15683         }
15684         
15685         this.doTouchViewQuery();
15686         
15687     },
15688     
15689     hideTouchView : function()
15690     {
15691         this.touchViewEl.removeClass('in');
15692
15693         if(this.animate){
15694             var _this = this;
15695             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15696         }else{
15697             this.touchViewEl.setStyle('display', 'none');
15698         }
15699         
15700         if(this._touchViewMask){
15701             this._touchViewMask.removeClass('show');
15702             Roo.get(document.body).removeClass("x-body-masked");
15703         }
15704     },
15705     
15706     setTouchViewValue : function()
15707     {
15708         if(this.multiple){
15709             this.clearItem();
15710         
15711             var _this = this;
15712
15713             Roo.each(this.tickItems, function(o){
15714                 this.addItem(o);
15715             }, this);
15716         }
15717         
15718         this.hideTouchView();
15719     },
15720     
15721     doTouchViewQuery : function()
15722     {
15723         var qe = {
15724             query: '',
15725             forceAll: true,
15726             combo: this,
15727             cancel:false
15728         };
15729         
15730         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15731             return false;
15732         }
15733         
15734         if(!this.alwaysQuery || this.mode == 'local'){
15735             this.onTouchViewLoad();
15736             return;
15737         }
15738         
15739         this.store.load();
15740     },
15741     
15742     onTouchViewBeforeLoad : function(combo,opts)
15743     {
15744         return;
15745     },
15746
15747     // private
15748     onTouchViewLoad : function()
15749     {
15750         if(this.store.getCount() < 1){
15751             this.onTouchViewEmptyResults();
15752             return;
15753         }
15754         
15755         this.clearTouchView();
15756         
15757         var rawValue = this.getRawValue();
15758         
15759         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15760         
15761         this.tickItems = [];
15762         
15763         this.store.data.each(function(d, rowIndex){
15764             var row = this.touchViewListGroup.createChild(template);
15765             
15766             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15767                 row.addClass(d.data.cls);
15768             }
15769             
15770             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15771                 var cfg = {
15772                     data : d.data,
15773                     html : d.data[this.displayField]
15774                 };
15775                 
15776                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15777                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15778                 }
15779             }
15780             row.removeClass('selected');
15781             if(!this.multiple && this.valueField &&
15782                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15783             {
15784                 // radio buttons..
15785                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15786                 row.addClass('selected');
15787             }
15788             
15789             if(this.multiple && this.valueField &&
15790                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15791             {
15792                 
15793                 // checkboxes...
15794                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15795                 this.tickItems.push(d.data);
15796             }
15797             
15798             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15799             
15800         }, this);
15801         
15802         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15803         
15804         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15805
15806         if(this.modalTitle.length){
15807             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15808         }
15809
15810         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15811         
15812         if(this.mobile_restrict_height && listHeight < bodyHeight){
15813             this.touchViewBodyEl.setHeight(listHeight);
15814         }
15815         
15816         var _this = this;
15817         
15818         if(firstChecked && listHeight > bodyHeight){
15819             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15820         }
15821         
15822     },
15823     
15824     onTouchViewLoadException : function()
15825     {
15826         this.hideTouchView();
15827     },
15828     
15829     onTouchViewEmptyResults : function()
15830     {
15831         this.clearTouchView();
15832         
15833         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15834         
15835         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15836         
15837     },
15838     
15839     clearTouchView : function()
15840     {
15841         this.touchViewListGroup.dom.innerHTML = '';
15842     },
15843     
15844     onTouchViewClick : function(e, el, o)
15845     {
15846         e.preventDefault();
15847         
15848         var row = o.row;
15849         var rowIndex = o.rowIndex;
15850         
15851         var r = this.store.getAt(rowIndex);
15852         
15853         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15854             
15855             if(!this.multiple){
15856                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15857                     c.dom.removeAttribute('checked');
15858                 }, this);
15859
15860                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15861
15862                 this.setFromData(r.data);
15863
15864                 var close = this.closeTriggerEl();
15865
15866                 if(close){
15867                     close.show();
15868                 }
15869
15870                 this.hideTouchView();
15871
15872                 this.fireEvent('select', this, r, rowIndex);
15873
15874                 return;
15875             }
15876
15877             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15878                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15879                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15880                 return;
15881             }
15882
15883             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15884             this.addItem(r.data);
15885             this.tickItems.push(r.data);
15886         }
15887     },
15888     
15889     getAutoCreateNativeIOS : function()
15890     {
15891         var cfg = {
15892             cls: 'form-group' //input-group,
15893         };
15894         
15895         var combobox =  {
15896             tag: 'select',
15897             cls : 'roo-ios-select'
15898         };
15899         
15900         if (this.name) {
15901             combobox.name = this.name;
15902         }
15903         
15904         if (this.disabled) {
15905             combobox.disabled = true;
15906         }
15907         
15908         var settings = this;
15909         
15910         ['xs','sm','md','lg'].map(function(size){
15911             if (settings[size]) {
15912                 cfg.cls += ' col-' + size + '-' + settings[size];
15913             }
15914         });
15915         
15916         cfg.cn = combobox;
15917         
15918         return cfg;
15919         
15920     },
15921     
15922     initIOSView : function()
15923     {
15924         this.store.on('load', this.onIOSViewLoad, this);
15925         
15926         return;
15927     },
15928     
15929     onIOSViewLoad : function()
15930     {
15931         if(this.store.getCount() < 1){
15932             return;
15933         }
15934         
15935         this.clearIOSView();
15936         
15937         if(this.allowBlank) {
15938             
15939             var default_text = '-- SELECT --';
15940             
15941             if(this.placeholder.length){
15942                 default_text = this.placeholder;
15943             }
15944             
15945             if(this.emptyTitle.length){
15946                 default_text += ' - ' + this.emptyTitle + ' -';
15947             }
15948             
15949             var opt = this.inputEl().createChild({
15950                 tag: 'option',
15951                 value : 0,
15952                 html : default_text
15953             });
15954             
15955             var o = {};
15956             o[this.valueField] = 0;
15957             o[this.displayField] = default_text;
15958             
15959             this.ios_options.push({
15960                 data : o,
15961                 el : opt
15962             });
15963             
15964         }
15965         
15966         this.store.data.each(function(d, rowIndex){
15967             
15968             var html = '';
15969             
15970             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15971                 html = d.data[this.displayField];
15972             }
15973             
15974             var value = '';
15975             
15976             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15977                 value = d.data[this.valueField];
15978             }
15979             
15980             var option = {
15981                 tag: 'option',
15982                 value : value,
15983                 html : html
15984             };
15985             
15986             if(this.value == d.data[this.valueField]){
15987                 option['selected'] = true;
15988             }
15989             
15990             var opt = this.inputEl().createChild(option);
15991             
15992             this.ios_options.push({
15993                 data : d.data,
15994                 el : opt
15995             });
15996             
15997         }, this);
15998         
15999         this.inputEl().on('change', function(){
16000            this.fireEvent('select', this);
16001         }, this);
16002         
16003     },
16004     
16005     clearIOSView: function()
16006     {
16007         this.inputEl().dom.innerHTML = '';
16008         
16009         this.ios_options = [];
16010     },
16011     
16012     setIOSValue: function(v)
16013     {
16014         this.value = v;
16015         
16016         if(!this.ios_options){
16017             return;
16018         }
16019         
16020         Roo.each(this.ios_options, function(opts){
16021            
16022            opts.el.dom.removeAttribute('selected');
16023            
16024            if(opts.data[this.valueField] != v){
16025                return;
16026            }
16027            
16028            opts.el.dom.setAttribute('selected', true);
16029            
16030         }, this);
16031     }
16032
16033     /** 
16034     * @cfg {Boolean} grow 
16035     * @hide 
16036     */
16037     /** 
16038     * @cfg {Number} growMin 
16039     * @hide 
16040     */
16041     /** 
16042     * @cfg {Number} growMax 
16043     * @hide 
16044     */
16045     /**
16046      * @hide
16047      * @method autoSize
16048      */
16049 });
16050
16051 Roo.apply(Roo.bootstrap.ComboBox,  {
16052     
16053     header : {
16054         tag: 'div',
16055         cls: 'modal-header',
16056         cn: [
16057             {
16058                 tag: 'h4',
16059                 cls: 'modal-title'
16060             }
16061         ]
16062     },
16063     
16064     body : {
16065         tag: 'div',
16066         cls: 'modal-body',
16067         cn: [
16068             {
16069                 tag: 'ul',
16070                 cls: 'list-group'
16071             }
16072         ]
16073     },
16074     
16075     listItemRadio : {
16076         tag: 'li',
16077         cls: 'list-group-item',
16078         cn: [
16079             {
16080                 tag: 'span',
16081                 cls: 'roo-combobox-list-group-item-value'
16082             },
16083             {
16084                 tag: 'div',
16085                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
16086                 cn: [
16087                     {
16088                         tag: 'input',
16089                         type: 'radio'
16090                     },
16091                     {
16092                         tag: 'label'
16093                     }
16094                 ]
16095             }
16096         ]
16097     },
16098     
16099     listItemCheckbox : {
16100         tag: 'li',
16101         cls: 'list-group-item',
16102         cn: [
16103             {
16104                 tag: 'span',
16105                 cls: 'roo-combobox-list-group-item-value'
16106             },
16107             {
16108                 tag: 'div',
16109                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
16110                 cn: [
16111                     {
16112                         tag: 'input',
16113                         type: 'checkbox'
16114                     },
16115                     {
16116                         tag: 'label'
16117                     }
16118                 ]
16119             }
16120         ]
16121     },
16122     
16123     emptyResult : {
16124         tag: 'div',
16125         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
16126     },
16127     
16128     footer : {
16129         tag: 'div',
16130         cls: 'modal-footer',
16131         cn: [
16132             {
16133                 tag: 'div',
16134                 cls: 'row',
16135                 cn: [
16136                     {
16137                         tag: 'div',
16138                         cls: 'col-xs-6 text-left',
16139                         cn: {
16140                             tag: 'button',
16141                             cls: 'btn btn-danger roo-touch-view-cancel',
16142                             html: 'Cancel'
16143                         }
16144                     },
16145                     {
16146                         tag: 'div',
16147                         cls: 'col-xs-6 text-right',
16148                         cn: {
16149                             tag: 'button',
16150                             cls: 'btn btn-success roo-touch-view-ok',
16151                             html: 'OK'
16152                         }
16153                     }
16154                 ]
16155             }
16156         ]
16157         
16158     }
16159 });
16160
16161 Roo.apply(Roo.bootstrap.ComboBox,  {
16162     
16163     touchViewTemplate : {
16164         tag: 'div',
16165         cls: 'modal fade roo-combobox-touch-view',
16166         cn: [
16167             {
16168                 tag: 'div',
16169                 cls: 'modal-dialog',
16170                 style : 'position:fixed', // we have to fix position....
16171                 cn: [
16172                     {
16173                         tag: 'div',
16174                         cls: 'modal-content',
16175                         cn: [
16176                             Roo.bootstrap.ComboBox.header,
16177                             Roo.bootstrap.ComboBox.body,
16178                             Roo.bootstrap.ComboBox.footer
16179                         ]
16180                     }
16181                 ]
16182             }
16183         ]
16184     }
16185 });/*
16186  * Based on:
16187  * Ext JS Library 1.1.1
16188  * Copyright(c) 2006-2007, Ext JS, LLC.
16189  *
16190  * Originally Released Under LGPL - original licence link has changed is not relivant.
16191  *
16192  * Fork - LGPL
16193  * <script type="text/javascript">
16194  */
16195
16196 /**
16197  * @class Roo.View
16198  * @extends Roo.util.Observable
16199  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
16200  * This class also supports single and multi selection modes. <br>
16201  * Create a data model bound view:
16202  <pre><code>
16203  var store = new Roo.data.Store(...);
16204
16205  var view = new Roo.View({
16206     el : "my-element",
16207     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
16208  
16209     singleSelect: true,
16210     selectedClass: "ydataview-selected",
16211     store: store
16212  });
16213
16214  // listen for node click?
16215  view.on("click", function(vw, index, node, e){
16216  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16217  });
16218
16219  // load XML data
16220  dataModel.load("foobar.xml");
16221  </code></pre>
16222  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16223  * <br><br>
16224  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16225  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16226  * 
16227  * Note: old style constructor is still suported (container, template, config)
16228  * 
16229  * @constructor
16230  * Create a new View
16231  * @param {Object} config The config object
16232  * 
16233  */
16234 Roo.View = function(config, depreciated_tpl, depreciated_config){
16235     
16236     this.parent = false;
16237     
16238     if (typeof(depreciated_tpl) == 'undefined') {
16239         // new way.. - universal constructor.
16240         Roo.apply(this, config);
16241         this.el  = Roo.get(this.el);
16242     } else {
16243         // old format..
16244         this.el  = Roo.get(config);
16245         this.tpl = depreciated_tpl;
16246         Roo.apply(this, depreciated_config);
16247     }
16248     this.wrapEl  = this.el.wrap().wrap();
16249     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16250     
16251     
16252     if(typeof(this.tpl) == "string"){
16253         this.tpl = new Roo.Template(this.tpl);
16254     } else {
16255         // support xtype ctors..
16256         this.tpl = new Roo.factory(this.tpl, Roo);
16257     }
16258     
16259     
16260     this.tpl.compile();
16261     
16262     /** @private */
16263     this.addEvents({
16264         /**
16265          * @event beforeclick
16266          * Fires before a click is processed. Returns false to cancel the default action.
16267          * @param {Roo.View} this
16268          * @param {Number} index The index of the target node
16269          * @param {HTMLElement} node The target node
16270          * @param {Roo.EventObject} e The raw event object
16271          */
16272             "beforeclick" : true,
16273         /**
16274          * @event click
16275          * Fires when a template node is clicked.
16276          * @param {Roo.View} this
16277          * @param {Number} index The index of the target node
16278          * @param {HTMLElement} node The target node
16279          * @param {Roo.EventObject} e The raw event object
16280          */
16281             "click" : true,
16282         /**
16283          * @event dblclick
16284          * Fires when a template node is double clicked.
16285          * @param {Roo.View} this
16286          * @param {Number} index The index of the target node
16287          * @param {HTMLElement} node The target node
16288          * @param {Roo.EventObject} e The raw event object
16289          */
16290             "dblclick" : true,
16291         /**
16292          * @event contextmenu
16293          * Fires when a template node is right clicked.
16294          * @param {Roo.View} this
16295          * @param {Number} index The index of the target node
16296          * @param {HTMLElement} node The target node
16297          * @param {Roo.EventObject} e The raw event object
16298          */
16299             "contextmenu" : true,
16300         /**
16301          * @event selectionchange
16302          * Fires when the selected nodes change.
16303          * @param {Roo.View} this
16304          * @param {Array} selections Array of the selected nodes
16305          */
16306             "selectionchange" : true,
16307     
16308         /**
16309          * @event beforeselect
16310          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16311          * @param {Roo.View} this
16312          * @param {HTMLElement} node The node to be selected
16313          * @param {Array} selections Array of currently selected nodes
16314          */
16315             "beforeselect" : true,
16316         /**
16317          * @event preparedata
16318          * Fires on every row to render, to allow you to change the data.
16319          * @param {Roo.View} this
16320          * @param {Object} data to be rendered (change this)
16321          */
16322           "preparedata" : true
16323           
16324           
16325         });
16326
16327
16328
16329     this.el.on({
16330         "click": this.onClick,
16331         "dblclick": this.onDblClick,
16332         "contextmenu": this.onContextMenu,
16333         scope:this
16334     });
16335
16336     this.selections = [];
16337     this.nodes = [];
16338     this.cmp = new Roo.CompositeElementLite([]);
16339     if(this.store){
16340         this.store = Roo.factory(this.store, Roo.data);
16341         this.setStore(this.store, true);
16342     }
16343     
16344     if ( this.footer && this.footer.xtype) {
16345            
16346          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16347         
16348         this.footer.dataSource = this.store;
16349         this.footer.container = fctr;
16350         this.footer = Roo.factory(this.footer, Roo);
16351         fctr.insertFirst(this.el);
16352         
16353         // this is a bit insane - as the paging toolbar seems to detach the el..
16354 //        dom.parentNode.parentNode.parentNode
16355          // they get detached?
16356     }
16357     
16358     
16359     Roo.View.superclass.constructor.call(this);
16360     
16361     
16362 };
16363
16364 Roo.extend(Roo.View, Roo.util.Observable, {
16365     
16366      /**
16367      * @cfg {Roo.data.Store} store Data store to load data from.
16368      */
16369     store : false,
16370     
16371     /**
16372      * @cfg {String|Roo.Element} el The container element.
16373      */
16374     el : '',
16375     
16376     /**
16377      * @cfg {String|Roo.Template} tpl The template used by this View 
16378      */
16379     tpl : false,
16380     /**
16381      * @cfg {String} dataName the named area of the template to use as the data area
16382      *                          Works with domtemplates roo-name="name"
16383      */
16384     dataName: false,
16385     /**
16386      * @cfg {String} selectedClass The css class to add to selected nodes
16387      */
16388     selectedClass : "x-view-selected",
16389      /**
16390      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16391      */
16392     emptyText : "",
16393     
16394     /**
16395      * @cfg {String} text to display on mask (default Loading)
16396      */
16397     mask : false,
16398     /**
16399      * @cfg {Boolean} multiSelect Allow multiple selection
16400      */
16401     multiSelect : false,
16402     /**
16403      * @cfg {Boolean} singleSelect Allow single selection
16404      */
16405     singleSelect:  false,
16406     
16407     /**
16408      * @cfg {Boolean} toggleSelect - selecting 
16409      */
16410     toggleSelect : false,
16411     
16412     /**
16413      * @cfg {Boolean} tickable - selecting 
16414      */
16415     tickable : false,
16416     
16417     /**
16418      * Returns the element this view is bound to.
16419      * @return {Roo.Element}
16420      */
16421     getEl : function(){
16422         return this.wrapEl;
16423     },
16424     
16425     
16426
16427     /**
16428      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16429      */
16430     refresh : function(){
16431         //Roo.log('refresh');
16432         var t = this.tpl;
16433         
16434         // if we are using something like 'domtemplate', then
16435         // the what gets used is:
16436         // t.applySubtemplate(NAME, data, wrapping data..)
16437         // the outer template then get' applied with
16438         //     the store 'extra data'
16439         // and the body get's added to the
16440         //      roo-name="data" node?
16441         //      <span class='roo-tpl-{name}'></span> ?????
16442         
16443         
16444         
16445         this.clearSelections();
16446         this.el.update("");
16447         var html = [];
16448         var records = this.store.getRange();
16449         if(records.length < 1) {
16450             
16451             // is this valid??  = should it render a template??
16452             
16453             this.el.update(this.emptyText);
16454             return;
16455         }
16456         var el = this.el;
16457         if (this.dataName) {
16458             this.el.update(t.apply(this.store.meta)); //????
16459             el = this.el.child('.roo-tpl-' + this.dataName);
16460         }
16461         
16462         for(var i = 0, len = records.length; i < len; i++){
16463             var data = this.prepareData(records[i].data, i, records[i]);
16464             this.fireEvent("preparedata", this, data, i, records[i]);
16465             
16466             var d = Roo.apply({}, data);
16467             
16468             if(this.tickable){
16469                 Roo.apply(d, {'roo-id' : Roo.id()});
16470                 
16471                 var _this = this;
16472             
16473                 Roo.each(this.parent.item, function(item){
16474                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16475                         return;
16476                     }
16477                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16478                 });
16479             }
16480             
16481             html[html.length] = Roo.util.Format.trim(
16482                 this.dataName ?
16483                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16484                     t.apply(d)
16485             );
16486         }
16487         
16488         
16489         
16490         el.update(html.join(""));
16491         this.nodes = el.dom.childNodes;
16492         this.updateIndexes(0);
16493     },
16494     
16495
16496     /**
16497      * Function to override to reformat the data that is sent to
16498      * the template for each node.
16499      * DEPRICATED - use the preparedata event handler.
16500      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16501      * a JSON object for an UpdateManager bound view).
16502      */
16503     prepareData : function(data, index, record)
16504     {
16505         this.fireEvent("preparedata", this, data, index, record);
16506         return data;
16507     },
16508
16509     onUpdate : function(ds, record){
16510         // Roo.log('on update');   
16511         this.clearSelections();
16512         var index = this.store.indexOf(record);
16513         var n = this.nodes[index];
16514         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16515         n.parentNode.removeChild(n);
16516         this.updateIndexes(index, index);
16517     },
16518
16519     
16520     
16521 // --------- FIXME     
16522     onAdd : function(ds, records, index)
16523     {
16524         //Roo.log(['on Add', ds, records, index] );        
16525         this.clearSelections();
16526         if(this.nodes.length == 0){
16527             this.refresh();
16528             return;
16529         }
16530         var n = this.nodes[index];
16531         for(var i = 0, len = records.length; i < len; i++){
16532             var d = this.prepareData(records[i].data, i, records[i]);
16533             if(n){
16534                 this.tpl.insertBefore(n, d);
16535             }else{
16536                 
16537                 this.tpl.append(this.el, d);
16538             }
16539         }
16540         this.updateIndexes(index);
16541     },
16542
16543     onRemove : function(ds, record, index){
16544        // Roo.log('onRemove');
16545         this.clearSelections();
16546         var el = this.dataName  ?
16547             this.el.child('.roo-tpl-' + this.dataName) :
16548             this.el; 
16549         
16550         el.dom.removeChild(this.nodes[index]);
16551         this.updateIndexes(index);
16552     },
16553
16554     /**
16555      * Refresh an individual node.
16556      * @param {Number} index
16557      */
16558     refreshNode : function(index){
16559         this.onUpdate(this.store, this.store.getAt(index));
16560     },
16561
16562     updateIndexes : function(startIndex, endIndex){
16563         var ns = this.nodes;
16564         startIndex = startIndex || 0;
16565         endIndex = endIndex || ns.length - 1;
16566         for(var i = startIndex; i <= endIndex; i++){
16567             ns[i].nodeIndex = i;
16568         }
16569     },
16570
16571     /**
16572      * Changes the data store this view uses and refresh the view.
16573      * @param {Store} store
16574      */
16575     setStore : function(store, initial){
16576         if(!initial && this.store){
16577             this.store.un("datachanged", this.refresh);
16578             this.store.un("add", this.onAdd);
16579             this.store.un("remove", this.onRemove);
16580             this.store.un("update", this.onUpdate);
16581             this.store.un("clear", this.refresh);
16582             this.store.un("beforeload", this.onBeforeLoad);
16583             this.store.un("load", this.onLoad);
16584             this.store.un("loadexception", this.onLoad);
16585         }
16586         if(store){
16587           
16588             store.on("datachanged", this.refresh, this);
16589             store.on("add", this.onAdd, this);
16590             store.on("remove", this.onRemove, this);
16591             store.on("update", this.onUpdate, this);
16592             store.on("clear", this.refresh, this);
16593             store.on("beforeload", this.onBeforeLoad, this);
16594             store.on("load", this.onLoad, this);
16595             store.on("loadexception", this.onLoad, this);
16596         }
16597         
16598         if(store){
16599             this.refresh();
16600         }
16601     },
16602     /**
16603      * onbeforeLoad - masks the loading area.
16604      *
16605      */
16606     onBeforeLoad : function(store,opts)
16607     {
16608          //Roo.log('onBeforeLoad');   
16609         if (!opts.add) {
16610             this.el.update("");
16611         }
16612         this.el.mask(this.mask ? this.mask : "Loading" ); 
16613     },
16614     onLoad : function ()
16615     {
16616         this.el.unmask();
16617     },
16618     
16619
16620     /**
16621      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16622      * @param {HTMLElement} node
16623      * @return {HTMLElement} The template node
16624      */
16625     findItemFromChild : function(node){
16626         var el = this.dataName  ?
16627             this.el.child('.roo-tpl-' + this.dataName,true) :
16628             this.el.dom; 
16629         
16630         if(!node || node.parentNode == el){
16631                     return node;
16632             }
16633             var p = node.parentNode;
16634             while(p && p != el){
16635             if(p.parentNode == el){
16636                 return p;
16637             }
16638             p = p.parentNode;
16639         }
16640             return null;
16641     },
16642
16643     /** @ignore */
16644     onClick : function(e){
16645         var item = this.findItemFromChild(e.getTarget());
16646         if(item){
16647             var index = this.indexOf(item);
16648             if(this.onItemClick(item, index, e) !== false){
16649                 this.fireEvent("click", this, index, item, e);
16650             }
16651         }else{
16652             this.clearSelections();
16653         }
16654     },
16655
16656     /** @ignore */
16657     onContextMenu : function(e){
16658         var item = this.findItemFromChild(e.getTarget());
16659         if(item){
16660             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16661         }
16662     },
16663
16664     /** @ignore */
16665     onDblClick : function(e){
16666         var item = this.findItemFromChild(e.getTarget());
16667         if(item){
16668             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16669         }
16670     },
16671
16672     onItemClick : function(item, index, e)
16673     {
16674         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16675             return false;
16676         }
16677         if (this.toggleSelect) {
16678             var m = this.isSelected(item) ? 'unselect' : 'select';
16679             //Roo.log(m);
16680             var _t = this;
16681             _t[m](item, true, false);
16682             return true;
16683         }
16684         if(this.multiSelect || this.singleSelect){
16685             if(this.multiSelect && e.shiftKey && this.lastSelection){
16686                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16687             }else{
16688                 this.select(item, this.multiSelect && e.ctrlKey);
16689                 this.lastSelection = item;
16690             }
16691             
16692             if(!this.tickable){
16693                 e.preventDefault();
16694             }
16695             
16696         }
16697         return true;
16698     },
16699
16700     /**
16701      * Get the number of selected nodes.
16702      * @return {Number}
16703      */
16704     getSelectionCount : function(){
16705         return this.selections.length;
16706     },
16707
16708     /**
16709      * Get the currently selected nodes.
16710      * @return {Array} An array of HTMLElements
16711      */
16712     getSelectedNodes : function(){
16713         return this.selections;
16714     },
16715
16716     /**
16717      * Get the indexes of the selected nodes.
16718      * @return {Array}
16719      */
16720     getSelectedIndexes : function(){
16721         var indexes = [], s = this.selections;
16722         for(var i = 0, len = s.length; i < len; i++){
16723             indexes.push(s[i].nodeIndex);
16724         }
16725         return indexes;
16726     },
16727
16728     /**
16729      * Clear all selections
16730      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16731      */
16732     clearSelections : function(suppressEvent){
16733         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16734             this.cmp.elements = this.selections;
16735             this.cmp.removeClass(this.selectedClass);
16736             this.selections = [];
16737             if(!suppressEvent){
16738                 this.fireEvent("selectionchange", this, this.selections);
16739             }
16740         }
16741     },
16742
16743     /**
16744      * Returns true if the passed node is selected
16745      * @param {HTMLElement/Number} node The node or node index
16746      * @return {Boolean}
16747      */
16748     isSelected : function(node){
16749         var s = this.selections;
16750         if(s.length < 1){
16751             return false;
16752         }
16753         node = this.getNode(node);
16754         return s.indexOf(node) !== -1;
16755     },
16756
16757     /**
16758      * Selects nodes.
16759      * @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
16760      * @param {Boolean} keepExisting (optional) true to keep existing selections
16761      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16762      */
16763     select : function(nodeInfo, keepExisting, suppressEvent){
16764         if(nodeInfo instanceof Array){
16765             if(!keepExisting){
16766                 this.clearSelections(true);
16767             }
16768             for(var i = 0, len = nodeInfo.length; i < len; i++){
16769                 this.select(nodeInfo[i], true, true);
16770             }
16771             return;
16772         } 
16773         var node = this.getNode(nodeInfo);
16774         if(!node || this.isSelected(node)){
16775             return; // already selected.
16776         }
16777         if(!keepExisting){
16778             this.clearSelections(true);
16779         }
16780         
16781         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16782             Roo.fly(node).addClass(this.selectedClass);
16783             this.selections.push(node);
16784             if(!suppressEvent){
16785                 this.fireEvent("selectionchange", this, this.selections);
16786             }
16787         }
16788         
16789         
16790     },
16791       /**
16792      * Unselects nodes.
16793      * @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
16794      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16795      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16796      */
16797     unselect : function(nodeInfo, keepExisting, suppressEvent)
16798     {
16799         if(nodeInfo instanceof Array){
16800             Roo.each(this.selections, function(s) {
16801                 this.unselect(s, nodeInfo);
16802             }, this);
16803             return;
16804         }
16805         var node = this.getNode(nodeInfo);
16806         if(!node || !this.isSelected(node)){
16807             //Roo.log("not selected");
16808             return; // not selected.
16809         }
16810         // fireevent???
16811         var ns = [];
16812         Roo.each(this.selections, function(s) {
16813             if (s == node ) {
16814                 Roo.fly(node).removeClass(this.selectedClass);
16815
16816                 return;
16817             }
16818             ns.push(s);
16819         },this);
16820         
16821         this.selections= ns;
16822         this.fireEvent("selectionchange", this, this.selections);
16823     },
16824
16825     /**
16826      * Gets a template node.
16827      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16828      * @return {HTMLElement} The node or null if it wasn't found
16829      */
16830     getNode : function(nodeInfo){
16831         if(typeof nodeInfo == "string"){
16832             return document.getElementById(nodeInfo);
16833         }else if(typeof nodeInfo == "number"){
16834             return this.nodes[nodeInfo];
16835         }
16836         return nodeInfo;
16837     },
16838
16839     /**
16840      * Gets a range template nodes.
16841      * @param {Number} startIndex
16842      * @param {Number} endIndex
16843      * @return {Array} An array of nodes
16844      */
16845     getNodes : function(start, end){
16846         var ns = this.nodes;
16847         start = start || 0;
16848         end = typeof end == "undefined" ? ns.length - 1 : end;
16849         var nodes = [];
16850         if(start <= end){
16851             for(var i = start; i <= end; i++){
16852                 nodes.push(ns[i]);
16853             }
16854         } else{
16855             for(var i = start; i >= end; i--){
16856                 nodes.push(ns[i]);
16857             }
16858         }
16859         return nodes;
16860     },
16861
16862     /**
16863      * Finds the index of the passed node
16864      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16865      * @return {Number} The index of the node or -1
16866      */
16867     indexOf : function(node){
16868         node = this.getNode(node);
16869         if(typeof node.nodeIndex == "number"){
16870             return node.nodeIndex;
16871         }
16872         var ns = this.nodes;
16873         for(var i = 0, len = ns.length; i < len; i++){
16874             if(ns[i] == node){
16875                 return i;
16876             }
16877         }
16878         return -1;
16879     }
16880 });
16881 /*
16882  * - LGPL
16883  *
16884  * based on jquery fullcalendar
16885  * 
16886  */
16887
16888 Roo.bootstrap = Roo.bootstrap || {};
16889 /**
16890  * @class Roo.bootstrap.Calendar
16891  * @extends Roo.bootstrap.Component
16892  * Bootstrap Calendar class
16893  * @cfg {Boolean} loadMask (true|false) default false
16894  * @cfg {Object} header generate the user specific header of the calendar, default false
16895
16896  * @constructor
16897  * Create a new Container
16898  * @param {Object} config The config object
16899  */
16900
16901
16902
16903 Roo.bootstrap.Calendar = function(config){
16904     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16905      this.addEvents({
16906         /**
16907              * @event select
16908              * Fires when a date is selected
16909              * @param {DatePicker} this
16910              * @param {Date} date The selected date
16911              */
16912         'select': true,
16913         /**
16914              * @event monthchange
16915              * Fires when the displayed month changes 
16916              * @param {DatePicker} this
16917              * @param {Date} date The selected month
16918              */
16919         'monthchange': true,
16920         /**
16921              * @event evententer
16922              * Fires when mouse over an event
16923              * @param {Calendar} this
16924              * @param {event} Event
16925              */
16926         'evententer': true,
16927         /**
16928              * @event eventleave
16929              * Fires when the mouse leaves an
16930              * @param {Calendar} this
16931              * @param {event}
16932              */
16933         'eventleave': true,
16934         /**
16935              * @event eventclick
16936              * Fires when the mouse click an
16937              * @param {Calendar} this
16938              * @param {event}
16939              */
16940         'eventclick': true
16941         
16942     });
16943
16944 };
16945
16946 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16947     
16948      /**
16949      * @cfg {Number} startDay
16950      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16951      */
16952     startDay : 0,
16953     
16954     loadMask : false,
16955     
16956     header : false,
16957       
16958     getAutoCreate : function(){
16959         
16960         
16961         var fc_button = function(name, corner, style, content ) {
16962             return Roo.apply({},{
16963                 tag : 'span',
16964                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16965                          (corner.length ?
16966                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16967                             ''
16968                         ),
16969                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16970                 unselectable: 'on'
16971             });
16972         };
16973         
16974         var header = {};
16975         
16976         if(!this.header){
16977             header = {
16978                 tag : 'table',
16979                 cls : 'fc-header',
16980                 style : 'width:100%',
16981                 cn : [
16982                     {
16983                         tag: 'tr',
16984                         cn : [
16985                             {
16986                                 tag : 'td',
16987                                 cls : 'fc-header-left',
16988                                 cn : [
16989                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16990                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16991                                     { tag: 'span', cls: 'fc-header-space' },
16992                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16993
16994
16995                                 ]
16996                             },
16997
16998                             {
16999                                 tag : 'td',
17000                                 cls : 'fc-header-center',
17001                                 cn : [
17002                                     {
17003                                         tag: 'span',
17004                                         cls: 'fc-header-title',
17005                                         cn : {
17006                                             tag: 'H2',
17007                                             html : 'month / year'
17008                                         }
17009                                     }
17010
17011                                 ]
17012                             },
17013                             {
17014                                 tag : 'td',
17015                                 cls : 'fc-header-right',
17016                                 cn : [
17017                               /*      fc_button('month', 'left', '', 'month' ),
17018                                     fc_button('week', '', '', 'week' ),
17019                                     fc_button('day', 'right', '', 'day' )
17020                                 */    
17021
17022                                 ]
17023                             }
17024
17025                         ]
17026                     }
17027                 ]
17028             };
17029         }
17030         
17031         header = this.header;
17032         
17033        
17034         var cal_heads = function() {
17035             var ret = [];
17036             // fixme - handle this.
17037             
17038             for (var i =0; i < Date.dayNames.length; i++) {
17039                 var d = Date.dayNames[i];
17040                 ret.push({
17041                     tag: 'th',
17042                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
17043                     html : d.substring(0,3)
17044                 });
17045                 
17046             }
17047             ret[0].cls += ' fc-first';
17048             ret[6].cls += ' fc-last';
17049             return ret;
17050         };
17051         var cal_cell = function(n) {
17052             return  {
17053                 tag: 'td',
17054                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
17055                 cn : [
17056                     {
17057                         cn : [
17058                             {
17059                                 cls: 'fc-day-number',
17060                                 html: 'D'
17061                             },
17062                             {
17063                                 cls: 'fc-day-content',
17064                              
17065                                 cn : [
17066                                      {
17067                                         style: 'position: relative;' // height: 17px;
17068                                     }
17069                                 ]
17070                             }
17071                             
17072                             
17073                         ]
17074                     }
17075                 ]
17076                 
17077             }
17078         };
17079         var cal_rows = function() {
17080             
17081             var ret = [];
17082             for (var r = 0; r < 6; r++) {
17083                 var row= {
17084                     tag : 'tr',
17085                     cls : 'fc-week',
17086                     cn : []
17087                 };
17088                 
17089                 for (var i =0; i < Date.dayNames.length; i++) {
17090                     var d = Date.dayNames[i];
17091                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
17092
17093                 }
17094                 row.cn[0].cls+=' fc-first';
17095                 row.cn[0].cn[0].style = 'min-height:90px';
17096                 row.cn[6].cls+=' fc-last';
17097                 ret.push(row);
17098                 
17099             }
17100             ret[0].cls += ' fc-first';
17101             ret[4].cls += ' fc-prev-last';
17102             ret[5].cls += ' fc-last';
17103             return ret;
17104             
17105         };
17106         
17107         var cal_table = {
17108             tag: 'table',
17109             cls: 'fc-border-separate',
17110             style : 'width:100%',
17111             cellspacing  : 0,
17112             cn : [
17113                 { 
17114                     tag: 'thead',
17115                     cn : [
17116                         { 
17117                             tag: 'tr',
17118                             cls : 'fc-first fc-last',
17119                             cn : cal_heads()
17120                         }
17121                     ]
17122                 },
17123                 { 
17124                     tag: 'tbody',
17125                     cn : cal_rows()
17126                 }
17127                   
17128             ]
17129         };
17130          
17131          var cfg = {
17132             cls : 'fc fc-ltr',
17133             cn : [
17134                 header,
17135                 {
17136                     cls : 'fc-content',
17137                     style : "position: relative;",
17138                     cn : [
17139                         {
17140                             cls : 'fc-view fc-view-month fc-grid',
17141                             style : 'position: relative',
17142                             unselectable : 'on',
17143                             cn : [
17144                                 {
17145                                     cls : 'fc-event-container',
17146                                     style : 'position:absolute;z-index:8;top:0;left:0;'
17147                                 },
17148                                 cal_table
17149                             ]
17150                         }
17151                     ]
17152     
17153                 }
17154            ] 
17155             
17156         };
17157         
17158          
17159         
17160         return cfg;
17161     },
17162     
17163     
17164     initEvents : function()
17165     {
17166         if(!this.store){
17167             throw "can not find store for calendar";
17168         }
17169         
17170         var mark = {
17171             tag: "div",
17172             cls:"x-dlg-mask",
17173             style: "text-align:center",
17174             cn: [
17175                 {
17176                     tag: "div",
17177                     style: "background-color:white;width:50%;margin:250 auto",
17178                     cn: [
17179                         {
17180                             tag: "img",
17181                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
17182                         },
17183                         {
17184                             tag: "span",
17185                             html: "Loading"
17186                         }
17187                         
17188                     ]
17189                 }
17190             ]
17191         };
17192         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17193         
17194         var size = this.el.select('.fc-content', true).first().getSize();
17195         this.maskEl.setSize(size.width, size.height);
17196         this.maskEl.enableDisplayMode("block");
17197         if(!this.loadMask){
17198             this.maskEl.hide();
17199         }
17200         
17201         this.store = Roo.factory(this.store, Roo.data);
17202         this.store.on('load', this.onLoad, this);
17203         this.store.on('beforeload', this.onBeforeLoad, this);
17204         
17205         this.resize();
17206         
17207         this.cells = this.el.select('.fc-day',true);
17208         //Roo.log(this.cells);
17209         this.textNodes = this.el.query('.fc-day-number');
17210         this.cells.addClassOnOver('fc-state-hover');
17211         
17212         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17213         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17214         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17215         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17216         
17217         this.on('monthchange', this.onMonthChange, this);
17218         
17219         this.update(new Date().clearTime());
17220     },
17221     
17222     resize : function() {
17223         var sz  = this.el.getSize();
17224         
17225         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17226         this.el.select('.fc-day-content div',true).setHeight(34);
17227     },
17228     
17229     
17230     // private
17231     showPrevMonth : function(e){
17232         this.update(this.activeDate.add("mo", -1));
17233     },
17234     showToday : function(e){
17235         this.update(new Date().clearTime());
17236     },
17237     // private
17238     showNextMonth : function(e){
17239         this.update(this.activeDate.add("mo", 1));
17240     },
17241
17242     // private
17243     showPrevYear : function(){
17244         this.update(this.activeDate.add("y", -1));
17245     },
17246
17247     // private
17248     showNextYear : function(){
17249         this.update(this.activeDate.add("y", 1));
17250     },
17251
17252     
17253    // private
17254     update : function(date)
17255     {
17256         var vd = this.activeDate;
17257         this.activeDate = date;
17258 //        if(vd && this.el){
17259 //            var t = date.getTime();
17260 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17261 //                Roo.log('using add remove');
17262 //                
17263 //                this.fireEvent('monthchange', this, date);
17264 //                
17265 //                this.cells.removeClass("fc-state-highlight");
17266 //                this.cells.each(function(c){
17267 //                   if(c.dateValue == t){
17268 //                       c.addClass("fc-state-highlight");
17269 //                       setTimeout(function(){
17270 //                            try{c.dom.firstChild.focus();}catch(e){}
17271 //                       }, 50);
17272 //                       return false;
17273 //                   }
17274 //                   return true;
17275 //                });
17276 //                return;
17277 //            }
17278 //        }
17279         
17280         var days = date.getDaysInMonth();
17281         
17282         var firstOfMonth = date.getFirstDateOfMonth();
17283         var startingPos = firstOfMonth.getDay()-this.startDay;
17284         
17285         if(startingPos < this.startDay){
17286             startingPos += 7;
17287         }
17288         
17289         var pm = date.add(Date.MONTH, -1);
17290         var prevStart = pm.getDaysInMonth()-startingPos;
17291 //        
17292         this.cells = this.el.select('.fc-day',true);
17293         this.textNodes = this.el.query('.fc-day-number');
17294         this.cells.addClassOnOver('fc-state-hover');
17295         
17296         var cells = this.cells.elements;
17297         var textEls = this.textNodes;
17298         
17299         Roo.each(cells, function(cell){
17300             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17301         });
17302         
17303         days += startingPos;
17304
17305         // convert everything to numbers so it's fast
17306         var day = 86400000;
17307         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17308         //Roo.log(d);
17309         //Roo.log(pm);
17310         //Roo.log(prevStart);
17311         
17312         var today = new Date().clearTime().getTime();
17313         var sel = date.clearTime().getTime();
17314         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17315         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17316         var ddMatch = this.disabledDatesRE;
17317         var ddText = this.disabledDatesText;
17318         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17319         var ddaysText = this.disabledDaysText;
17320         var format = this.format;
17321         
17322         var setCellClass = function(cal, cell){
17323             cell.row = 0;
17324             cell.events = [];
17325             cell.more = [];
17326             //Roo.log('set Cell Class');
17327             cell.title = "";
17328             var t = d.getTime();
17329             
17330             //Roo.log(d);
17331             
17332             cell.dateValue = t;
17333             if(t == today){
17334                 cell.className += " fc-today";
17335                 cell.className += " fc-state-highlight";
17336                 cell.title = cal.todayText;
17337             }
17338             if(t == sel){
17339                 // disable highlight in other month..
17340                 //cell.className += " fc-state-highlight";
17341                 
17342             }
17343             // disabling
17344             if(t < min) {
17345                 cell.className = " fc-state-disabled";
17346                 cell.title = cal.minText;
17347                 return;
17348             }
17349             if(t > max) {
17350                 cell.className = " fc-state-disabled";
17351                 cell.title = cal.maxText;
17352                 return;
17353             }
17354             if(ddays){
17355                 if(ddays.indexOf(d.getDay()) != -1){
17356                     cell.title = ddaysText;
17357                     cell.className = " fc-state-disabled";
17358                 }
17359             }
17360             if(ddMatch && format){
17361                 var fvalue = d.dateFormat(format);
17362                 if(ddMatch.test(fvalue)){
17363                     cell.title = ddText.replace("%0", fvalue);
17364                     cell.className = " fc-state-disabled";
17365                 }
17366             }
17367             
17368             if (!cell.initialClassName) {
17369                 cell.initialClassName = cell.dom.className;
17370             }
17371             
17372             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17373         };
17374
17375         var i = 0;
17376         
17377         for(; i < startingPos; i++) {
17378             textEls[i].innerHTML = (++prevStart);
17379             d.setDate(d.getDate()+1);
17380             
17381             cells[i].className = "fc-past fc-other-month";
17382             setCellClass(this, cells[i]);
17383         }
17384         
17385         var intDay = 0;
17386         
17387         for(; i < days; i++){
17388             intDay = i - startingPos + 1;
17389             textEls[i].innerHTML = (intDay);
17390             d.setDate(d.getDate()+1);
17391             
17392             cells[i].className = ''; // "x-date-active";
17393             setCellClass(this, cells[i]);
17394         }
17395         var extraDays = 0;
17396         
17397         for(; i < 42; i++) {
17398             textEls[i].innerHTML = (++extraDays);
17399             d.setDate(d.getDate()+1);
17400             
17401             cells[i].className = "fc-future fc-other-month";
17402             setCellClass(this, cells[i]);
17403         }
17404         
17405         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17406         
17407         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17408         
17409         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17410         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17411         
17412         if(totalRows != 6){
17413             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17414             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17415         }
17416         
17417         this.fireEvent('monthchange', this, date);
17418         
17419         
17420         /*
17421         if(!this.internalRender){
17422             var main = this.el.dom.firstChild;
17423             var w = main.offsetWidth;
17424             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17425             Roo.fly(main).setWidth(w);
17426             this.internalRender = true;
17427             // opera does not respect the auto grow header center column
17428             // then, after it gets a width opera refuses to recalculate
17429             // without a second pass
17430             if(Roo.isOpera && !this.secondPass){
17431                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17432                 this.secondPass = true;
17433                 this.update.defer(10, this, [date]);
17434             }
17435         }
17436         */
17437         
17438     },
17439     
17440     findCell : function(dt) {
17441         dt = dt.clearTime().getTime();
17442         var ret = false;
17443         this.cells.each(function(c){
17444             //Roo.log("check " +c.dateValue + '?=' + dt);
17445             if(c.dateValue == dt){
17446                 ret = c;
17447                 return false;
17448             }
17449             return true;
17450         });
17451         
17452         return ret;
17453     },
17454     
17455     findCells : function(ev) {
17456         var s = ev.start.clone().clearTime().getTime();
17457        // Roo.log(s);
17458         var e= ev.end.clone().clearTime().getTime();
17459        // Roo.log(e);
17460         var ret = [];
17461         this.cells.each(function(c){
17462              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17463             
17464             if(c.dateValue > e){
17465                 return ;
17466             }
17467             if(c.dateValue < s){
17468                 return ;
17469             }
17470             ret.push(c);
17471         });
17472         
17473         return ret;    
17474     },
17475     
17476 //    findBestRow: function(cells)
17477 //    {
17478 //        var ret = 0;
17479 //        
17480 //        for (var i =0 ; i < cells.length;i++) {
17481 //            ret  = Math.max(cells[i].rows || 0,ret);
17482 //        }
17483 //        return ret;
17484 //        
17485 //    },
17486     
17487     
17488     addItem : function(ev)
17489     {
17490         // look for vertical location slot in
17491         var cells = this.findCells(ev);
17492         
17493 //        ev.row = this.findBestRow(cells);
17494         
17495         // work out the location.
17496         
17497         var crow = false;
17498         var rows = [];
17499         for(var i =0; i < cells.length; i++) {
17500             
17501             cells[i].row = cells[0].row;
17502             
17503             if(i == 0){
17504                 cells[i].row = cells[i].row + 1;
17505             }
17506             
17507             if (!crow) {
17508                 crow = {
17509                     start : cells[i],
17510                     end :  cells[i]
17511                 };
17512                 continue;
17513             }
17514             if (crow.start.getY() == cells[i].getY()) {
17515                 // on same row.
17516                 crow.end = cells[i];
17517                 continue;
17518             }
17519             // different row.
17520             rows.push(crow);
17521             crow = {
17522                 start: cells[i],
17523                 end : cells[i]
17524             };
17525             
17526         }
17527         
17528         rows.push(crow);
17529         ev.els = [];
17530         ev.rows = rows;
17531         ev.cells = cells;
17532         
17533         cells[0].events.push(ev);
17534         
17535         this.calevents.push(ev);
17536     },
17537     
17538     clearEvents: function() {
17539         
17540         if(!this.calevents){
17541             return;
17542         }
17543         
17544         Roo.each(this.cells.elements, function(c){
17545             c.row = 0;
17546             c.events = [];
17547             c.more = [];
17548         });
17549         
17550         Roo.each(this.calevents, function(e) {
17551             Roo.each(e.els, function(el) {
17552                 el.un('mouseenter' ,this.onEventEnter, this);
17553                 el.un('mouseleave' ,this.onEventLeave, this);
17554                 el.remove();
17555             },this);
17556         },this);
17557         
17558         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17559             e.remove();
17560         });
17561         
17562     },
17563     
17564     renderEvents: function()
17565     {   
17566         var _this = this;
17567         
17568         this.cells.each(function(c) {
17569             
17570             if(c.row < 5){
17571                 return;
17572             }
17573             
17574             var ev = c.events;
17575             
17576             var r = 4;
17577             if(c.row != c.events.length){
17578                 r = 4 - (4 - (c.row - c.events.length));
17579             }
17580             
17581             c.events = ev.slice(0, r);
17582             c.more = ev.slice(r);
17583             
17584             if(c.more.length && c.more.length == 1){
17585                 c.events.push(c.more.pop());
17586             }
17587             
17588             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17589             
17590         });
17591             
17592         this.cells.each(function(c) {
17593             
17594             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17595             
17596             
17597             for (var e = 0; e < c.events.length; e++){
17598                 var ev = c.events[e];
17599                 var rows = ev.rows;
17600                 
17601                 for(var i = 0; i < rows.length; i++) {
17602                 
17603                     // how many rows should it span..
17604
17605                     var  cfg = {
17606                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17607                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17608
17609                         unselectable : "on",
17610                         cn : [
17611                             {
17612                                 cls: 'fc-event-inner',
17613                                 cn : [
17614     //                                {
17615     //                                  tag:'span',
17616     //                                  cls: 'fc-event-time',
17617     //                                  html : cells.length > 1 ? '' : ev.time
17618     //                                },
17619                                     {
17620                                       tag:'span',
17621                                       cls: 'fc-event-title',
17622                                       html : String.format('{0}', ev.title)
17623                                     }
17624
17625
17626                                 ]
17627                             },
17628                             {
17629                                 cls: 'ui-resizable-handle ui-resizable-e',
17630                                 html : '&nbsp;&nbsp;&nbsp'
17631                             }
17632
17633                         ]
17634                     };
17635
17636                     if (i == 0) {
17637                         cfg.cls += ' fc-event-start';
17638                     }
17639                     if ((i+1) == rows.length) {
17640                         cfg.cls += ' fc-event-end';
17641                     }
17642
17643                     var ctr = _this.el.select('.fc-event-container',true).first();
17644                     var cg = ctr.createChild(cfg);
17645
17646                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17647                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17648
17649                     var r = (c.more.length) ? 1 : 0;
17650                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17651                     cg.setWidth(ebox.right - sbox.x -2);
17652
17653                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17654                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17655                     cg.on('click', _this.onEventClick, _this, ev);
17656
17657                     ev.els.push(cg);
17658                     
17659                 }
17660                 
17661             }
17662             
17663             
17664             if(c.more.length){
17665                 var  cfg = {
17666                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17667                     style : 'position: absolute',
17668                     unselectable : "on",
17669                     cn : [
17670                         {
17671                             cls: 'fc-event-inner',
17672                             cn : [
17673                                 {
17674                                   tag:'span',
17675                                   cls: 'fc-event-title',
17676                                   html : 'More'
17677                                 }
17678
17679
17680                             ]
17681                         },
17682                         {
17683                             cls: 'ui-resizable-handle ui-resizable-e',
17684                             html : '&nbsp;&nbsp;&nbsp'
17685                         }
17686
17687                     ]
17688                 };
17689
17690                 var ctr = _this.el.select('.fc-event-container',true).first();
17691                 var cg = ctr.createChild(cfg);
17692
17693                 var sbox = c.select('.fc-day-content',true).first().getBox();
17694                 var ebox = c.select('.fc-day-content',true).first().getBox();
17695                 //Roo.log(cg);
17696                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17697                 cg.setWidth(ebox.right - sbox.x -2);
17698
17699                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17700                 
17701             }
17702             
17703         });
17704         
17705         
17706         
17707     },
17708     
17709     onEventEnter: function (e, el,event,d) {
17710         this.fireEvent('evententer', this, el, event);
17711     },
17712     
17713     onEventLeave: function (e, el,event,d) {
17714         this.fireEvent('eventleave', this, el, event);
17715     },
17716     
17717     onEventClick: function (e, el,event,d) {
17718         this.fireEvent('eventclick', this, el, event);
17719     },
17720     
17721     onMonthChange: function () {
17722         this.store.load();
17723     },
17724     
17725     onMoreEventClick: function(e, el, more)
17726     {
17727         var _this = this;
17728         
17729         this.calpopover.placement = 'right';
17730         this.calpopover.setTitle('More');
17731         
17732         this.calpopover.setContent('');
17733         
17734         var ctr = this.calpopover.el.select('.popover-content', true).first();
17735         
17736         Roo.each(more, function(m){
17737             var cfg = {
17738                 cls : 'fc-event-hori fc-event-draggable',
17739                 html : m.title
17740             };
17741             var cg = ctr.createChild(cfg);
17742             
17743             cg.on('click', _this.onEventClick, _this, m);
17744         });
17745         
17746         this.calpopover.show(el);
17747         
17748         
17749     },
17750     
17751     onLoad: function () 
17752     {   
17753         this.calevents = [];
17754         var cal = this;
17755         
17756         if(this.store.getCount() > 0){
17757             this.store.data.each(function(d){
17758                cal.addItem({
17759                     id : d.data.id,
17760                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17761                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17762                     time : d.data.start_time,
17763                     title : d.data.title,
17764                     description : d.data.description,
17765                     venue : d.data.venue
17766                 });
17767             });
17768         }
17769         
17770         this.renderEvents();
17771         
17772         if(this.calevents.length && this.loadMask){
17773             this.maskEl.hide();
17774         }
17775     },
17776     
17777     onBeforeLoad: function()
17778     {
17779         this.clearEvents();
17780         if(this.loadMask){
17781             this.maskEl.show();
17782         }
17783     }
17784 });
17785
17786  
17787  /*
17788  * - LGPL
17789  *
17790  * element
17791  * 
17792  */
17793
17794 /**
17795  * @class Roo.bootstrap.Popover
17796  * @extends Roo.bootstrap.Component
17797  * Bootstrap Popover class
17798  * @cfg {String} html contents of the popover   (or false to use children..)
17799  * @cfg {String} title of popover (or false to hide)
17800  * @cfg {String} placement how it is placed
17801  * @cfg {String} trigger click || hover (or false to trigger manually)
17802  * @cfg {String} over what (parent or false to trigger manually.)
17803  * @cfg {Number} delay - delay before showing
17804  
17805  * @constructor
17806  * Create a new Popover
17807  * @param {Object} config The config object
17808  */
17809
17810 Roo.bootstrap.Popover = function(config){
17811     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17812     
17813     this.addEvents({
17814         // raw events
17815          /**
17816          * @event show
17817          * After the popover show
17818          * 
17819          * @param {Roo.bootstrap.Popover} this
17820          */
17821         "show" : true,
17822         /**
17823          * @event hide
17824          * After the popover hide
17825          * 
17826          * @param {Roo.bootstrap.Popover} this
17827          */
17828         "hide" : true
17829     });
17830 };
17831
17832 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17833     
17834     title: 'Fill in a title',
17835     html: false,
17836     
17837     placement : 'right',
17838     trigger : 'hover', // hover
17839     
17840     delay : 0,
17841     
17842     over: 'parent',
17843     
17844     can_build_overlaid : false,
17845     
17846     getChildContainer : function()
17847     {
17848         return this.el.select('.popover-content',true).first();
17849     },
17850     
17851     getAutoCreate : function(){
17852          
17853         var cfg = {
17854            cls : 'popover roo-dynamic',
17855            style: 'display:block',
17856            cn : [
17857                 {
17858                     cls : 'arrow'
17859                 },
17860                 {
17861                     cls : 'popover-inner',
17862                     cn : [
17863                         {
17864                             tag: 'h3',
17865                             cls: 'popover-title popover-header',
17866                             html : this.title
17867                         },
17868                         {
17869                             cls : 'popover-content popover-body',
17870                             html : this.html
17871                         }
17872                     ]
17873                     
17874                 }
17875            ]
17876         };
17877         
17878         return cfg;
17879     },
17880     setTitle: function(str)
17881     {
17882         this.title = str;
17883         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17884     },
17885     setContent: function(str)
17886     {
17887         this.html = str;
17888         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17889     },
17890     // as it get's added to the bottom of the page.
17891     onRender : function(ct, position)
17892     {
17893         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17894         if(!this.el){
17895             var cfg = Roo.apply({},  this.getAutoCreate());
17896             cfg.id = Roo.id();
17897             
17898             if (this.cls) {
17899                 cfg.cls += ' ' + this.cls;
17900             }
17901             if (this.style) {
17902                 cfg.style = this.style;
17903             }
17904             //Roo.log("adding to ");
17905             this.el = Roo.get(document.body).createChild(cfg, position);
17906 //            Roo.log(this.el);
17907         }
17908         this.initEvents();
17909     },
17910     
17911     initEvents : function()
17912     {
17913         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17914         this.el.enableDisplayMode('block');
17915         this.el.hide();
17916         if (this.over === false) {
17917             return; 
17918         }
17919         if (this.triggers === false) {
17920             return;
17921         }
17922         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17923         var triggers = this.trigger ? this.trigger.split(' ') : [];
17924         Roo.each(triggers, function(trigger) {
17925         
17926             if (trigger == 'click') {
17927                 on_el.on('click', this.toggle, this);
17928             } else if (trigger != 'manual') {
17929                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17930                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17931       
17932                 on_el.on(eventIn  ,this.enter, this);
17933                 on_el.on(eventOut, this.leave, this);
17934             }
17935         }, this);
17936         
17937     },
17938     
17939     
17940     // private
17941     timeout : null,
17942     hoverState : null,
17943     
17944     toggle : function () {
17945         this.hoverState == 'in' ? this.leave() : this.enter();
17946     },
17947     
17948     enter : function () {
17949         
17950         clearTimeout(this.timeout);
17951     
17952         this.hoverState = 'in';
17953     
17954         if (!this.delay || !this.delay.show) {
17955             this.show();
17956             return;
17957         }
17958         var _t = this;
17959         this.timeout = setTimeout(function () {
17960             if (_t.hoverState == 'in') {
17961                 _t.show();
17962             }
17963         }, this.delay.show)
17964     },
17965     
17966     leave : function() {
17967         clearTimeout(this.timeout);
17968     
17969         this.hoverState = 'out';
17970     
17971         if (!this.delay || !this.delay.hide) {
17972             this.hide();
17973             return;
17974         }
17975         var _t = this;
17976         this.timeout = setTimeout(function () {
17977             if (_t.hoverState == 'out') {
17978                 _t.hide();
17979             }
17980         }, this.delay.hide)
17981     },
17982     
17983     show : function (on_el)
17984     {
17985         if (!on_el) {
17986             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17987         }
17988         
17989         // set content.
17990         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17991         if (this.html !== false) {
17992             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17993         }
17994         this.el.removeClass([
17995             'fade','top','bottom', 'left', 'right','in',
17996             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
17997         ]);
17998         if (!this.title.length) {
17999             this.el.select('.popover-title',true).hide();
18000         }
18001         
18002         var placement = typeof this.placement == 'function' ?
18003             this.placement.call(this, this.el, on_el) :
18004             this.placement;
18005             
18006         var autoToken = /\s?auto?\s?/i;
18007         var autoPlace = autoToken.test(placement);
18008         if (autoPlace) {
18009             placement = placement.replace(autoToken, '') || 'top';
18010         }
18011         
18012         //this.el.detach()
18013         //this.el.setXY([0,0]);
18014         this.el.show();
18015         this.el.dom.style.display='block';
18016         this.el.addClass(placement);
18017         
18018         //this.el.appendTo(on_el);
18019         
18020         var p = this.getPosition();
18021         var box = this.el.getBox();
18022         
18023         if (autoPlace) {
18024             // fixme..
18025         }
18026         var align = Roo.bootstrap.Popover.alignment[placement];
18027         
18028 //        Roo.log(align);
18029         this.el.alignTo(on_el, align[0],align[1]);
18030         //var arrow = this.el.select('.arrow',true).first();
18031         //arrow.set(align[2], 
18032         
18033         this.el.addClass('in');
18034         
18035         
18036         if (this.el.hasClass('fade')) {
18037             // fade it?
18038         }
18039         
18040         this.hoverState = 'in';
18041         
18042         this.fireEvent('show', this);
18043         
18044     },
18045     hide : function()
18046     {
18047         this.el.setXY([0,0]);
18048         this.el.removeClass('in');
18049         this.el.hide();
18050         this.hoverState = null;
18051         
18052         this.fireEvent('hide', this);
18053     }
18054     
18055 });
18056
18057 Roo.bootstrap.Popover.alignment = {
18058     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
18059     'right' : ['l-r', [10,0], 'left bs-popover-left'],
18060     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
18061     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
18062 };
18063
18064  /*
18065  * - LGPL
18066  *
18067  * Progress
18068  * 
18069  */
18070
18071 /**
18072  * @class Roo.bootstrap.Progress
18073  * @extends Roo.bootstrap.Component
18074  * Bootstrap Progress class
18075  * @cfg {Boolean} striped striped of the progress bar
18076  * @cfg {Boolean} active animated of the progress bar
18077  * 
18078  * 
18079  * @constructor
18080  * Create a new Progress
18081  * @param {Object} config The config object
18082  */
18083
18084 Roo.bootstrap.Progress = function(config){
18085     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
18086 };
18087
18088 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
18089     
18090     striped : false,
18091     active: false,
18092     
18093     getAutoCreate : function(){
18094         var cfg = {
18095             tag: 'div',
18096             cls: 'progress'
18097         };
18098         
18099         
18100         if(this.striped){
18101             cfg.cls += ' progress-striped';
18102         }
18103       
18104         if(this.active){
18105             cfg.cls += ' active';
18106         }
18107         
18108         
18109         return cfg;
18110     }
18111    
18112 });
18113
18114  
18115
18116  /*
18117  * - LGPL
18118  *
18119  * ProgressBar
18120  * 
18121  */
18122
18123 /**
18124  * @class Roo.bootstrap.ProgressBar
18125  * @extends Roo.bootstrap.Component
18126  * Bootstrap ProgressBar class
18127  * @cfg {Number} aria_valuenow aria-value now
18128  * @cfg {Number} aria_valuemin aria-value min
18129  * @cfg {Number} aria_valuemax aria-value max
18130  * @cfg {String} label label for the progress bar
18131  * @cfg {String} panel (success | info | warning | danger )
18132  * @cfg {String} role role of the progress bar
18133  * @cfg {String} sr_only text
18134  * 
18135  * 
18136  * @constructor
18137  * Create a new ProgressBar
18138  * @param {Object} config The config object
18139  */
18140
18141 Roo.bootstrap.ProgressBar = function(config){
18142     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
18143 };
18144
18145 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
18146     
18147     aria_valuenow : 0,
18148     aria_valuemin : 0,
18149     aria_valuemax : 100,
18150     label : false,
18151     panel : false,
18152     role : false,
18153     sr_only: false,
18154     
18155     getAutoCreate : function()
18156     {
18157         
18158         var cfg = {
18159             tag: 'div',
18160             cls: 'progress-bar',
18161             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18162         };
18163         
18164         if(this.sr_only){
18165             cfg.cn = {
18166                 tag: 'span',
18167                 cls: 'sr-only',
18168                 html: this.sr_only
18169             }
18170         }
18171         
18172         if(this.role){
18173             cfg.role = this.role;
18174         }
18175         
18176         if(this.aria_valuenow){
18177             cfg['aria-valuenow'] = this.aria_valuenow;
18178         }
18179         
18180         if(this.aria_valuemin){
18181             cfg['aria-valuemin'] = this.aria_valuemin;
18182         }
18183         
18184         if(this.aria_valuemax){
18185             cfg['aria-valuemax'] = this.aria_valuemax;
18186         }
18187         
18188         if(this.label && !this.sr_only){
18189             cfg.html = this.label;
18190         }
18191         
18192         if(this.panel){
18193             cfg.cls += ' progress-bar-' + this.panel;
18194         }
18195         
18196         return cfg;
18197     },
18198     
18199     update : function(aria_valuenow)
18200     {
18201         this.aria_valuenow = aria_valuenow;
18202         
18203         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18204     }
18205    
18206 });
18207
18208  
18209
18210  /*
18211  * - LGPL
18212  *
18213  * column
18214  * 
18215  */
18216
18217 /**
18218  * @class Roo.bootstrap.TabGroup
18219  * @extends Roo.bootstrap.Column
18220  * Bootstrap Column class
18221  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18222  * @cfg {Boolean} carousel true to make the group behave like a carousel
18223  * @cfg {Boolean} bullets show bullets for the panels
18224  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18225  * @cfg {Number} timer auto slide timer .. default 0 millisecond
18226  * @cfg {Boolean} showarrow (true|false) show arrow default true
18227  * 
18228  * @constructor
18229  * Create a new TabGroup
18230  * @param {Object} config The config object
18231  */
18232
18233 Roo.bootstrap.TabGroup = function(config){
18234     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18235     if (!this.navId) {
18236         this.navId = Roo.id();
18237     }
18238     this.tabs = [];
18239     Roo.bootstrap.TabGroup.register(this);
18240     
18241 };
18242
18243 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
18244     
18245     carousel : false,
18246     transition : false,
18247     bullets : 0,
18248     timer : 0,
18249     autoslide : false,
18250     slideFn : false,
18251     slideOnTouch : false,
18252     showarrow : true,
18253     
18254     getAutoCreate : function()
18255     {
18256         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18257         
18258         cfg.cls += ' tab-content';
18259         
18260         if (this.carousel) {
18261             cfg.cls += ' carousel slide';
18262             
18263             cfg.cn = [{
18264                cls : 'carousel-inner',
18265                cn : []
18266             }];
18267         
18268             if(this.bullets  && !Roo.isTouch){
18269                 
18270                 var bullets = {
18271                     cls : 'carousel-bullets',
18272                     cn : []
18273                 };
18274                
18275                 if(this.bullets_cls){
18276                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18277                 }
18278                 
18279                 bullets.cn.push({
18280                     cls : 'clear'
18281                 });
18282                 
18283                 cfg.cn[0].cn.push(bullets);
18284             }
18285             
18286             if(this.showarrow){
18287                 cfg.cn[0].cn.push({
18288                     tag : 'div',
18289                     class : 'carousel-arrow',
18290                     cn : [
18291                         {
18292                             tag : 'div',
18293                             class : 'carousel-prev',
18294                             cn : [
18295                                 {
18296                                     tag : 'i',
18297                                     class : 'fa fa-chevron-left'
18298                                 }
18299                             ]
18300                         },
18301                         {
18302                             tag : 'div',
18303                             class : 'carousel-next',
18304                             cn : [
18305                                 {
18306                                     tag : 'i',
18307                                     class : 'fa fa-chevron-right'
18308                                 }
18309                             ]
18310                         }
18311                     ]
18312                 });
18313             }
18314             
18315         }
18316         
18317         return cfg;
18318     },
18319     
18320     initEvents:  function()
18321     {
18322 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18323 //            this.el.on("touchstart", this.onTouchStart, this);
18324 //        }
18325         
18326         if(this.autoslide){
18327             var _this = this;
18328             
18329             this.slideFn = window.setInterval(function() {
18330                 _this.showPanelNext();
18331             }, this.timer);
18332         }
18333         
18334         if(this.showarrow){
18335             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18336             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18337         }
18338         
18339         
18340     },
18341     
18342 //    onTouchStart : function(e, el, o)
18343 //    {
18344 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18345 //            return;
18346 //        }
18347 //        
18348 //        this.showPanelNext();
18349 //    },
18350     
18351     
18352     getChildContainer : function()
18353     {
18354         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18355     },
18356     
18357     /**
18358     * register a Navigation item
18359     * @param {Roo.bootstrap.NavItem} the navitem to add
18360     */
18361     register : function(item)
18362     {
18363         this.tabs.push( item);
18364         item.navId = this.navId; // not really needed..
18365         this.addBullet();
18366     
18367     },
18368     
18369     getActivePanel : function()
18370     {
18371         var r = false;
18372         Roo.each(this.tabs, function(t) {
18373             if (t.active) {
18374                 r = t;
18375                 return false;
18376             }
18377             return null;
18378         });
18379         return r;
18380         
18381     },
18382     getPanelByName : function(n)
18383     {
18384         var r = false;
18385         Roo.each(this.tabs, function(t) {
18386             if (t.tabId == n) {
18387                 r = t;
18388                 return false;
18389             }
18390             return null;
18391         });
18392         return r;
18393     },
18394     indexOfPanel : function(p)
18395     {
18396         var r = false;
18397         Roo.each(this.tabs, function(t,i) {
18398             if (t.tabId == p.tabId) {
18399                 r = i;
18400                 return false;
18401             }
18402             return null;
18403         });
18404         return r;
18405     },
18406     /**
18407      * show a specific panel
18408      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18409      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18410      */
18411     showPanel : function (pan)
18412     {
18413         if(this.transition || typeof(pan) == 'undefined'){
18414             Roo.log("waiting for the transitionend");
18415             return false;
18416         }
18417         
18418         if (typeof(pan) == 'number') {
18419             pan = this.tabs[pan];
18420         }
18421         
18422         if (typeof(pan) == 'string') {
18423             pan = this.getPanelByName(pan);
18424         }
18425         
18426         var cur = this.getActivePanel();
18427         
18428         if(!pan || !cur){
18429             Roo.log('pan or acitve pan is undefined');
18430             return false;
18431         }
18432         
18433         if (pan.tabId == this.getActivePanel().tabId) {
18434             return true;
18435         }
18436         
18437         if (false === cur.fireEvent('beforedeactivate')) {
18438             return false;
18439         }
18440         
18441         if(this.bullets > 0 && !Roo.isTouch){
18442             this.setActiveBullet(this.indexOfPanel(pan));
18443         }
18444         
18445         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18446             
18447             //class="carousel-item carousel-item-next carousel-item-left"
18448             
18449             this.transition = true;
18450             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18451             var lr = dir == 'next' ? 'left' : 'right';
18452             pan.el.addClass(dir); // or prev
18453             pan.el.addClass('carousel-item-' + dir); // or prev
18454             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18455             cur.el.addClass(lr); // or right
18456             pan.el.addClass(lr);
18457             cur.el.addClass('carousel-item-' +lr); // or right
18458             pan.el.addClass('carousel-item-' +lr);
18459             
18460             
18461             var _this = this;
18462             cur.el.on('transitionend', function() {
18463                 Roo.log("trans end?");
18464                 
18465                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
18466                 pan.setActive(true);
18467                 
18468                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
18469                 cur.setActive(false);
18470                 
18471                 _this.transition = false;
18472                 
18473             }, this, { single:  true } );
18474             
18475             return true;
18476         }
18477         
18478         cur.setActive(false);
18479         pan.setActive(true);
18480         
18481         return true;
18482         
18483     },
18484     showPanelNext : function()
18485     {
18486         var i = this.indexOfPanel(this.getActivePanel());
18487         
18488         if (i >= this.tabs.length - 1 && !this.autoslide) {
18489             return;
18490         }
18491         
18492         if (i >= this.tabs.length - 1 && this.autoslide) {
18493             i = -1;
18494         }
18495         
18496         this.showPanel(this.tabs[i+1]);
18497     },
18498     
18499     showPanelPrev : function()
18500     {
18501         var i = this.indexOfPanel(this.getActivePanel());
18502         
18503         if (i  < 1 && !this.autoslide) {
18504             return;
18505         }
18506         
18507         if (i < 1 && this.autoslide) {
18508             i = this.tabs.length;
18509         }
18510         
18511         this.showPanel(this.tabs[i-1]);
18512     },
18513     
18514     
18515     addBullet: function()
18516     {
18517         if(!this.bullets || Roo.isTouch){
18518             return;
18519         }
18520         var ctr = this.el.select('.carousel-bullets',true).first();
18521         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18522         var bullet = ctr.createChild({
18523             cls : 'bullet bullet-' + i
18524         },ctr.dom.lastChild);
18525         
18526         
18527         var _this = this;
18528         
18529         bullet.on('click', (function(e, el, o, ii, t){
18530
18531             e.preventDefault();
18532
18533             this.showPanel(ii);
18534
18535             if(this.autoslide && this.slideFn){
18536                 clearInterval(this.slideFn);
18537                 this.slideFn = window.setInterval(function() {
18538                     _this.showPanelNext();
18539                 }, this.timer);
18540             }
18541
18542         }).createDelegate(this, [i, bullet], true));
18543                 
18544         
18545     },
18546      
18547     setActiveBullet : function(i)
18548     {
18549         if(Roo.isTouch){
18550             return;
18551         }
18552         
18553         Roo.each(this.el.select('.bullet', true).elements, function(el){
18554             el.removeClass('selected');
18555         });
18556
18557         var bullet = this.el.select('.bullet-' + i, true).first();
18558         
18559         if(!bullet){
18560             return;
18561         }
18562         
18563         bullet.addClass('selected');
18564     }
18565     
18566     
18567   
18568 });
18569
18570  
18571
18572  
18573  
18574 Roo.apply(Roo.bootstrap.TabGroup, {
18575     
18576     groups: {},
18577      /**
18578     * register a Navigation Group
18579     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18580     */
18581     register : function(navgrp)
18582     {
18583         this.groups[navgrp.navId] = navgrp;
18584         
18585     },
18586     /**
18587     * fetch a Navigation Group based on the navigation ID
18588     * if one does not exist , it will get created.
18589     * @param {string} the navgroup to add
18590     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18591     */
18592     get: function(navId) {
18593         if (typeof(this.groups[navId]) == 'undefined') {
18594             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18595         }
18596         return this.groups[navId] ;
18597     }
18598     
18599     
18600     
18601 });
18602
18603  /*
18604  * - LGPL
18605  *
18606  * TabPanel
18607  * 
18608  */
18609
18610 /**
18611  * @class Roo.bootstrap.TabPanel
18612  * @extends Roo.bootstrap.Component
18613  * Bootstrap TabPanel class
18614  * @cfg {Boolean} active panel active
18615  * @cfg {String} html panel content
18616  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18617  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18618  * @cfg {String} href click to link..
18619  * 
18620  * 
18621  * @constructor
18622  * Create a new TabPanel
18623  * @param {Object} config The config object
18624  */
18625
18626 Roo.bootstrap.TabPanel = function(config){
18627     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18628     this.addEvents({
18629         /**
18630              * @event changed
18631              * Fires when the active status changes
18632              * @param {Roo.bootstrap.TabPanel} this
18633              * @param {Boolean} state the new state
18634             
18635          */
18636         'changed': true,
18637         /**
18638              * @event beforedeactivate
18639              * Fires before a tab is de-activated - can be used to do validation on a form.
18640              * @param {Roo.bootstrap.TabPanel} this
18641              * @return {Boolean} false if there is an error
18642             
18643          */
18644         'beforedeactivate': true
18645      });
18646     
18647     this.tabId = this.tabId || Roo.id();
18648   
18649 };
18650
18651 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18652     
18653     active: false,
18654     html: false,
18655     tabId: false,
18656     navId : false,
18657     href : '',
18658     
18659     getAutoCreate : function(){
18660         
18661         
18662         var cfg = {
18663             tag: 'div',
18664             // item is needed for carousel - not sure if it has any effect otherwise
18665             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18666             html: this.html || ''
18667         };
18668         
18669         if(this.active){
18670             cfg.cls += ' active';
18671         }
18672         
18673         if(this.tabId){
18674             cfg.tabId = this.tabId;
18675         }
18676         
18677         
18678         
18679         return cfg;
18680     },
18681     
18682     initEvents:  function()
18683     {
18684         var p = this.parent();
18685         
18686         this.navId = this.navId || p.navId;
18687         
18688         if (typeof(this.navId) != 'undefined') {
18689             // not really needed.. but just in case.. parent should be a NavGroup.
18690             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18691             
18692             tg.register(this);
18693             
18694             var i = tg.tabs.length - 1;
18695             
18696             if(this.active && tg.bullets > 0 && i < tg.bullets){
18697                 tg.setActiveBullet(i);
18698             }
18699         }
18700         
18701         this.el.on('click', this.onClick, this);
18702         
18703         if(Roo.isTouch){
18704             this.el.on("touchstart", this.onTouchStart, this);
18705             this.el.on("touchmove", this.onTouchMove, this);
18706             this.el.on("touchend", this.onTouchEnd, this);
18707         }
18708         
18709     },
18710     
18711     onRender : function(ct, position)
18712     {
18713         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18714     },
18715     
18716     setActive : function(state)
18717     {
18718         Roo.log("panel - set active " + this.tabId + "=" + state);
18719         
18720         this.active = state;
18721         if (!state) {
18722             this.el.removeClass('active');
18723             
18724         } else  if (!this.el.hasClass('active')) {
18725             this.el.addClass('active');
18726         }
18727         
18728         this.fireEvent('changed', this, state);
18729     },
18730     
18731     onClick : function(e)
18732     {
18733         e.preventDefault();
18734         
18735         if(!this.href.length){
18736             return;
18737         }
18738         
18739         window.location.href = this.href;
18740     },
18741     
18742     startX : 0,
18743     startY : 0,
18744     endX : 0,
18745     endY : 0,
18746     swiping : false,
18747     
18748     onTouchStart : function(e)
18749     {
18750         this.swiping = false;
18751         
18752         this.startX = e.browserEvent.touches[0].clientX;
18753         this.startY = e.browserEvent.touches[0].clientY;
18754     },
18755     
18756     onTouchMove : function(e)
18757     {
18758         this.swiping = true;
18759         
18760         this.endX = e.browserEvent.touches[0].clientX;
18761         this.endY = e.browserEvent.touches[0].clientY;
18762     },
18763     
18764     onTouchEnd : function(e)
18765     {
18766         if(!this.swiping){
18767             this.onClick(e);
18768             return;
18769         }
18770         
18771         var tabGroup = this.parent();
18772         
18773         if(this.endX > this.startX){ // swiping right
18774             tabGroup.showPanelPrev();
18775             return;
18776         }
18777         
18778         if(this.startX > this.endX){ // swiping left
18779             tabGroup.showPanelNext();
18780             return;
18781         }
18782     }
18783     
18784     
18785 });
18786  
18787
18788  
18789
18790  /*
18791  * - LGPL
18792  *
18793  * DateField
18794  * 
18795  */
18796
18797 /**
18798  * @class Roo.bootstrap.DateField
18799  * @extends Roo.bootstrap.Input
18800  * Bootstrap DateField class
18801  * @cfg {Number} weekStart default 0
18802  * @cfg {String} viewMode default empty, (months|years)
18803  * @cfg {String} minViewMode default empty, (months|years)
18804  * @cfg {Number} startDate default -Infinity
18805  * @cfg {Number} endDate default Infinity
18806  * @cfg {Boolean} todayHighlight default false
18807  * @cfg {Boolean} todayBtn default false
18808  * @cfg {Boolean} calendarWeeks default false
18809  * @cfg {Object} daysOfWeekDisabled default empty
18810  * @cfg {Boolean} singleMode default false (true | false)
18811  * 
18812  * @cfg {Boolean} keyboardNavigation default true
18813  * @cfg {String} language default en
18814  * 
18815  * @constructor
18816  * Create a new DateField
18817  * @param {Object} config The config object
18818  */
18819
18820 Roo.bootstrap.DateField = function(config){
18821     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18822      this.addEvents({
18823             /**
18824              * @event show
18825              * Fires when this field show.
18826              * @param {Roo.bootstrap.DateField} this
18827              * @param {Mixed} date The date value
18828              */
18829             show : true,
18830             /**
18831              * @event show
18832              * Fires when this field hide.
18833              * @param {Roo.bootstrap.DateField} this
18834              * @param {Mixed} date The date value
18835              */
18836             hide : true,
18837             /**
18838              * @event select
18839              * Fires when select a date.
18840              * @param {Roo.bootstrap.DateField} this
18841              * @param {Mixed} date The date value
18842              */
18843             select : true,
18844             /**
18845              * @event beforeselect
18846              * Fires when before select a date.
18847              * @param {Roo.bootstrap.DateField} this
18848              * @param {Mixed} date The date value
18849              */
18850             beforeselect : true
18851         });
18852 };
18853
18854 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18855     
18856     /**
18857      * @cfg {String} format
18858      * The default date format string which can be overriden for localization support.  The format must be
18859      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18860      */
18861     format : "m/d/y",
18862     /**
18863      * @cfg {String} altFormats
18864      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18865      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18866      */
18867     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18868     
18869     weekStart : 0,
18870     
18871     viewMode : '',
18872     
18873     minViewMode : '',
18874     
18875     todayHighlight : false,
18876     
18877     todayBtn: false,
18878     
18879     language: 'en',
18880     
18881     keyboardNavigation: true,
18882     
18883     calendarWeeks: false,
18884     
18885     startDate: -Infinity,
18886     
18887     endDate: Infinity,
18888     
18889     daysOfWeekDisabled: [],
18890     
18891     _events: [],
18892     
18893     singleMode : false,
18894     
18895     UTCDate: function()
18896     {
18897         return new Date(Date.UTC.apply(Date, arguments));
18898     },
18899     
18900     UTCToday: function()
18901     {
18902         var today = new Date();
18903         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18904     },
18905     
18906     getDate: function() {
18907             var d = this.getUTCDate();
18908             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18909     },
18910     
18911     getUTCDate: function() {
18912             return this.date;
18913     },
18914     
18915     setDate: function(d) {
18916             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18917     },
18918     
18919     setUTCDate: function(d) {
18920             this.date = d;
18921             this.setValue(this.formatDate(this.date));
18922     },
18923         
18924     onRender: function(ct, position)
18925     {
18926         
18927         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18928         
18929         this.language = this.language || 'en';
18930         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18931         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18932         
18933         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18934         this.format = this.format || 'm/d/y';
18935         this.isInline = false;
18936         this.isInput = true;
18937         this.component = this.el.select('.add-on', true).first() || false;
18938         this.component = (this.component && this.component.length === 0) ? false : this.component;
18939         this.hasInput = this.component && this.inputEl().length;
18940         
18941         if (typeof(this.minViewMode === 'string')) {
18942             switch (this.minViewMode) {
18943                 case 'months':
18944                     this.minViewMode = 1;
18945                     break;
18946                 case 'years':
18947                     this.minViewMode = 2;
18948                     break;
18949                 default:
18950                     this.minViewMode = 0;
18951                     break;
18952             }
18953         }
18954         
18955         if (typeof(this.viewMode === 'string')) {
18956             switch (this.viewMode) {
18957                 case 'months':
18958                     this.viewMode = 1;
18959                     break;
18960                 case 'years':
18961                     this.viewMode = 2;
18962                     break;
18963                 default:
18964                     this.viewMode = 0;
18965                     break;
18966             }
18967         }
18968                 
18969         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18970         
18971 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18972         
18973         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18974         
18975         this.picker().on('mousedown', this.onMousedown, this);
18976         this.picker().on('click', this.onClick, this);
18977         
18978         this.picker().addClass('datepicker-dropdown');
18979         
18980         this.startViewMode = this.viewMode;
18981         
18982         if(this.singleMode){
18983             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18984                 v.setVisibilityMode(Roo.Element.DISPLAY);
18985                 v.hide();
18986             });
18987             
18988             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18989                 v.setStyle('width', '189px');
18990             });
18991         }
18992         
18993         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18994             if(!this.calendarWeeks){
18995                 v.remove();
18996                 return;
18997             }
18998             
18999             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19000             v.attr('colspan', function(i, val){
19001                 return parseInt(val) + 1;
19002             });
19003         });
19004                         
19005         
19006         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
19007         
19008         this.setStartDate(this.startDate);
19009         this.setEndDate(this.endDate);
19010         
19011         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
19012         
19013         this.fillDow();
19014         this.fillMonths();
19015         this.update();
19016         this.showMode();
19017         
19018         if(this.isInline) {
19019             this.showPopup();
19020         }
19021     },
19022     
19023     picker : function()
19024     {
19025         return this.pickerEl;
19026 //        return this.el.select('.datepicker', true).first();
19027     },
19028     
19029     fillDow: function()
19030     {
19031         var dowCnt = this.weekStart;
19032         
19033         var dow = {
19034             tag: 'tr',
19035             cn: [
19036                 
19037             ]
19038         };
19039         
19040         if(this.calendarWeeks){
19041             dow.cn.push({
19042                 tag: 'th',
19043                 cls: 'cw',
19044                 html: '&nbsp;'
19045             })
19046         }
19047         
19048         while (dowCnt < this.weekStart + 7) {
19049             dow.cn.push({
19050                 tag: 'th',
19051                 cls: 'dow',
19052                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
19053             });
19054         }
19055         
19056         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
19057     },
19058     
19059     fillMonths: function()
19060     {    
19061         var i = 0;
19062         var months = this.picker().select('>.datepicker-months td', true).first();
19063         
19064         months.dom.innerHTML = '';
19065         
19066         while (i < 12) {
19067             var month = {
19068                 tag: 'span',
19069                 cls: 'month',
19070                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
19071             };
19072             
19073             months.createChild(month);
19074         }
19075         
19076     },
19077     
19078     update: function()
19079     {
19080         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;
19081         
19082         if (this.date < this.startDate) {
19083             this.viewDate = new Date(this.startDate);
19084         } else if (this.date > this.endDate) {
19085             this.viewDate = new Date(this.endDate);
19086         } else {
19087             this.viewDate = new Date(this.date);
19088         }
19089         
19090         this.fill();
19091     },
19092     
19093     fill: function() 
19094     {
19095         var d = new Date(this.viewDate),
19096                 year = d.getUTCFullYear(),
19097                 month = d.getUTCMonth(),
19098                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
19099                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
19100                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
19101                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
19102                 currentDate = this.date && this.date.valueOf(),
19103                 today = this.UTCToday();
19104         
19105         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
19106         
19107 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19108         
19109 //        this.picker.select('>tfoot th.today').
19110 //                                              .text(dates[this.language].today)
19111 //                                              .toggle(this.todayBtn !== false);
19112     
19113         this.updateNavArrows();
19114         this.fillMonths();
19115                                                 
19116         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
19117         
19118         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
19119          
19120         prevMonth.setUTCDate(day);
19121         
19122         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
19123         
19124         var nextMonth = new Date(prevMonth);
19125         
19126         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
19127         
19128         nextMonth = nextMonth.valueOf();
19129         
19130         var fillMonths = false;
19131         
19132         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
19133         
19134         while(prevMonth.valueOf() <= nextMonth) {
19135             var clsName = '';
19136             
19137             if (prevMonth.getUTCDay() === this.weekStart) {
19138                 if(fillMonths){
19139                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
19140                 }
19141                     
19142                 fillMonths = {
19143                     tag: 'tr',
19144                     cn: []
19145                 };
19146                 
19147                 if(this.calendarWeeks){
19148                     // ISO 8601: First week contains first thursday.
19149                     // ISO also states week starts on Monday, but we can be more abstract here.
19150                     var
19151                     // Start of current week: based on weekstart/current date
19152                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
19153                     // Thursday of this week
19154                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
19155                     // First Thursday of year, year from thursday
19156                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
19157                     // Calendar week: ms between thursdays, div ms per day, div 7 days
19158                     calWeek =  (th - yth) / 864e5 / 7 + 1;
19159                     
19160                     fillMonths.cn.push({
19161                         tag: 'td',
19162                         cls: 'cw',
19163                         html: calWeek
19164                     });
19165                 }
19166             }
19167             
19168             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
19169                 clsName += ' old';
19170             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19171                 clsName += ' new';
19172             }
19173             if (this.todayHighlight &&
19174                 prevMonth.getUTCFullYear() == today.getFullYear() &&
19175                 prevMonth.getUTCMonth() == today.getMonth() &&
19176                 prevMonth.getUTCDate() == today.getDate()) {
19177                 clsName += ' today';
19178             }
19179             
19180             if (currentDate && prevMonth.valueOf() === currentDate) {
19181                 clsName += ' active';
19182             }
19183             
19184             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19185                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19186                     clsName += ' disabled';
19187             }
19188             
19189             fillMonths.cn.push({
19190                 tag: 'td',
19191                 cls: 'day ' + clsName,
19192                 html: prevMonth.getDate()
19193             });
19194             
19195             prevMonth.setDate(prevMonth.getDate()+1);
19196         }
19197           
19198         var currentYear = this.date && this.date.getUTCFullYear();
19199         var currentMonth = this.date && this.date.getUTCMonth();
19200         
19201         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19202         
19203         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19204             v.removeClass('active');
19205             
19206             if(currentYear === year && k === currentMonth){
19207                 v.addClass('active');
19208             }
19209             
19210             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19211                 v.addClass('disabled');
19212             }
19213             
19214         });
19215         
19216         
19217         year = parseInt(year/10, 10) * 10;
19218         
19219         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19220         
19221         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19222         
19223         year -= 1;
19224         for (var i = -1; i < 11; i++) {
19225             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19226                 tag: 'span',
19227                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19228                 html: year
19229             });
19230             
19231             year += 1;
19232         }
19233     },
19234     
19235     showMode: function(dir) 
19236     {
19237         if (dir) {
19238             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19239         }
19240         
19241         Roo.each(this.picker().select('>div',true).elements, function(v){
19242             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19243             v.hide();
19244         });
19245         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19246     },
19247     
19248     place: function()
19249     {
19250         if(this.isInline) {
19251             return;
19252         }
19253         
19254         this.picker().removeClass(['bottom', 'top']);
19255         
19256         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19257             /*
19258              * place to the top of element!
19259              *
19260              */
19261             
19262             this.picker().addClass('top');
19263             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19264             
19265             return;
19266         }
19267         
19268         this.picker().addClass('bottom');
19269         
19270         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19271     },
19272     
19273     parseDate : function(value)
19274     {
19275         if(!value || value instanceof Date){
19276             return value;
19277         }
19278         var v = Date.parseDate(value, this.format);
19279         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19280             v = Date.parseDate(value, 'Y-m-d');
19281         }
19282         if(!v && this.altFormats){
19283             if(!this.altFormatsArray){
19284                 this.altFormatsArray = this.altFormats.split("|");
19285             }
19286             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19287                 v = Date.parseDate(value, this.altFormatsArray[i]);
19288             }
19289         }
19290         return v;
19291     },
19292     
19293     formatDate : function(date, fmt)
19294     {   
19295         return (!date || !(date instanceof Date)) ?
19296         date : date.dateFormat(fmt || this.format);
19297     },
19298     
19299     onFocus : function()
19300     {
19301         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19302         this.showPopup();
19303     },
19304     
19305     onBlur : function()
19306     {
19307         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19308         
19309         var d = this.inputEl().getValue();
19310         
19311         this.setValue(d);
19312                 
19313         this.hidePopup();
19314     },
19315     
19316     showPopup : function()
19317     {
19318         this.picker().show();
19319         this.update();
19320         this.place();
19321         
19322         this.fireEvent('showpopup', this, this.date);
19323     },
19324     
19325     hidePopup : function()
19326     {
19327         if(this.isInline) {
19328             return;
19329         }
19330         this.picker().hide();
19331         this.viewMode = this.startViewMode;
19332         this.showMode();
19333         
19334         this.fireEvent('hidepopup', this, this.date);
19335         
19336     },
19337     
19338     onMousedown: function(e)
19339     {
19340         e.stopPropagation();
19341         e.preventDefault();
19342     },
19343     
19344     keyup: function(e)
19345     {
19346         Roo.bootstrap.DateField.superclass.keyup.call(this);
19347         this.update();
19348     },
19349
19350     setValue: function(v)
19351     {
19352         if(this.fireEvent('beforeselect', this, v) !== false){
19353             var d = new Date(this.parseDate(v) ).clearTime();
19354         
19355             if(isNaN(d.getTime())){
19356                 this.date = this.viewDate = '';
19357                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19358                 return;
19359             }
19360
19361             v = this.formatDate(d);
19362
19363             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19364
19365             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19366
19367             this.update();
19368
19369             this.fireEvent('select', this, this.date);
19370         }
19371     },
19372     
19373     getValue: function()
19374     {
19375         return this.formatDate(this.date);
19376     },
19377     
19378     fireKey: function(e)
19379     {
19380         if (!this.picker().isVisible()){
19381             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19382                 this.showPopup();
19383             }
19384             return;
19385         }
19386         
19387         var dateChanged = false,
19388         dir, day, month,
19389         newDate, newViewDate;
19390         
19391         switch(e.keyCode){
19392             case 27: // escape
19393                 this.hidePopup();
19394                 e.preventDefault();
19395                 break;
19396             case 37: // left
19397             case 39: // right
19398                 if (!this.keyboardNavigation) {
19399                     break;
19400                 }
19401                 dir = e.keyCode == 37 ? -1 : 1;
19402                 
19403                 if (e.ctrlKey){
19404                     newDate = this.moveYear(this.date, dir);
19405                     newViewDate = this.moveYear(this.viewDate, dir);
19406                 } else if (e.shiftKey){
19407                     newDate = this.moveMonth(this.date, dir);
19408                     newViewDate = this.moveMonth(this.viewDate, dir);
19409                 } else {
19410                     newDate = new Date(this.date);
19411                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19412                     newViewDate = new Date(this.viewDate);
19413                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19414                 }
19415                 if (this.dateWithinRange(newDate)){
19416                     this.date = newDate;
19417                     this.viewDate = newViewDate;
19418                     this.setValue(this.formatDate(this.date));
19419 //                    this.update();
19420                     e.preventDefault();
19421                     dateChanged = true;
19422                 }
19423                 break;
19424             case 38: // up
19425             case 40: // down
19426                 if (!this.keyboardNavigation) {
19427                     break;
19428                 }
19429                 dir = e.keyCode == 38 ? -1 : 1;
19430                 if (e.ctrlKey){
19431                     newDate = this.moveYear(this.date, dir);
19432                     newViewDate = this.moveYear(this.viewDate, dir);
19433                 } else if (e.shiftKey){
19434                     newDate = this.moveMonth(this.date, dir);
19435                     newViewDate = this.moveMonth(this.viewDate, dir);
19436                 } else {
19437                     newDate = new Date(this.date);
19438                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19439                     newViewDate = new Date(this.viewDate);
19440                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19441                 }
19442                 if (this.dateWithinRange(newDate)){
19443                     this.date = newDate;
19444                     this.viewDate = newViewDate;
19445                     this.setValue(this.formatDate(this.date));
19446 //                    this.update();
19447                     e.preventDefault();
19448                     dateChanged = true;
19449                 }
19450                 break;
19451             case 13: // enter
19452                 this.setValue(this.formatDate(this.date));
19453                 this.hidePopup();
19454                 e.preventDefault();
19455                 break;
19456             case 9: // tab
19457                 this.setValue(this.formatDate(this.date));
19458                 this.hidePopup();
19459                 break;
19460             case 16: // shift
19461             case 17: // ctrl
19462             case 18: // alt
19463                 break;
19464             default :
19465                 this.hidePopup();
19466                 
19467         }
19468     },
19469     
19470     
19471     onClick: function(e) 
19472     {
19473         e.stopPropagation();
19474         e.preventDefault();
19475         
19476         var target = e.getTarget();
19477         
19478         if(target.nodeName.toLowerCase() === 'i'){
19479             target = Roo.get(target).dom.parentNode;
19480         }
19481         
19482         var nodeName = target.nodeName;
19483         var className = target.className;
19484         var html = target.innerHTML;
19485         //Roo.log(nodeName);
19486         
19487         switch(nodeName.toLowerCase()) {
19488             case 'th':
19489                 switch(className) {
19490                     case 'switch':
19491                         this.showMode(1);
19492                         break;
19493                     case 'prev':
19494                     case 'next':
19495                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19496                         switch(this.viewMode){
19497                                 case 0:
19498                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19499                                         break;
19500                                 case 1:
19501                                 case 2:
19502                                         this.viewDate = this.moveYear(this.viewDate, dir);
19503                                         break;
19504                         }
19505                         this.fill();
19506                         break;
19507                     case 'today':
19508                         var date = new Date();
19509                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19510 //                        this.fill()
19511                         this.setValue(this.formatDate(this.date));
19512                         
19513                         this.hidePopup();
19514                         break;
19515                 }
19516                 break;
19517             case 'span':
19518                 if (className.indexOf('disabled') < 0) {
19519                     this.viewDate.setUTCDate(1);
19520                     if (className.indexOf('month') > -1) {
19521                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19522                     } else {
19523                         var year = parseInt(html, 10) || 0;
19524                         this.viewDate.setUTCFullYear(year);
19525                         
19526                     }
19527                     
19528                     if(this.singleMode){
19529                         this.setValue(this.formatDate(this.viewDate));
19530                         this.hidePopup();
19531                         return;
19532                     }
19533                     
19534                     this.showMode(-1);
19535                     this.fill();
19536                 }
19537                 break;
19538                 
19539             case 'td':
19540                 //Roo.log(className);
19541                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19542                     var day = parseInt(html, 10) || 1;
19543                     var year = this.viewDate.getUTCFullYear(),
19544                         month = this.viewDate.getUTCMonth();
19545
19546                     if (className.indexOf('old') > -1) {
19547                         if(month === 0 ){
19548                             month = 11;
19549                             year -= 1;
19550                         }else{
19551                             month -= 1;
19552                         }
19553                     } else if (className.indexOf('new') > -1) {
19554                         if (month == 11) {
19555                             month = 0;
19556                             year += 1;
19557                         } else {
19558                             month += 1;
19559                         }
19560                     }
19561                     //Roo.log([year,month,day]);
19562                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19563                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19564 //                    this.fill();
19565                     //Roo.log(this.formatDate(this.date));
19566                     this.setValue(this.formatDate(this.date));
19567                     this.hidePopup();
19568                 }
19569                 break;
19570         }
19571     },
19572     
19573     setStartDate: function(startDate)
19574     {
19575         this.startDate = startDate || -Infinity;
19576         if (this.startDate !== -Infinity) {
19577             this.startDate = this.parseDate(this.startDate);
19578         }
19579         this.update();
19580         this.updateNavArrows();
19581     },
19582
19583     setEndDate: function(endDate)
19584     {
19585         this.endDate = endDate || Infinity;
19586         if (this.endDate !== Infinity) {
19587             this.endDate = this.parseDate(this.endDate);
19588         }
19589         this.update();
19590         this.updateNavArrows();
19591     },
19592     
19593     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19594     {
19595         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19596         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19597             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19598         }
19599         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19600             return parseInt(d, 10);
19601         });
19602         this.update();
19603         this.updateNavArrows();
19604     },
19605     
19606     updateNavArrows: function() 
19607     {
19608         if(this.singleMode){
19609             return;
19610         }
19611         
19612         var d = new Date(this.viewDate),
19613         year = d.getUTCFullYear(),
19614         month = d.getUTCMonth();
19615         
19616         Roo.each(this.picker().select('.prev', true).elements, function(v){
19617             v.show();
19618             switch (this.viewMode) {
19619                 case 0:
19620
19621                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19622                         v.hide();
19623                     }
19624                     break;
19625                 case 1:
19626                 case 2:
19627                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19628                         v.hide();
19629                     }
19630                     break;
19631             }
19632         });
19633         
19634         Roo.each(this.picker().select('.next', true).elements, function(v){
19635             v.show();
19636             switch (this.viewMode) {
19637                 case 0:
19638
19639                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19640                         v.hide();
19641                     }
19642                     break;
19643                 case 1:
19644                 case 2:
19645                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19646                         v.hide();
19647                     }
19648                     break;
19649             }
19650         })
19651     },
19652     
19653     moveMonth: function(date, dir)
19654     {
19655         if (!dir) {
19656             return date;
19657         }
19658         var new_date = new Date(date.valueOf()),
19659         day = new_date.getUTCDate(),
19660         month = new_date.getUTCMonth(),
19661         mag = Math.abs(dir),
19662         new_month, test;
19663         dir = dir > 0 ? 1 : -1;
19664         if (mag == 1){
19665             test = dir == -1
19666             // If going back one month, make sure month is not current month
19667             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19668             ? function(){
19669                 return new_date.getUTCMonth() == month;
19670             }
19671             // If going forward one month, make sure month is as expected
19672             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19673             : function(){
19674                 return new_date.getUTCMonth() != new_month;
19675             };
19676             new_month = month + dir;
19677             new_date.setUTCMonth(new_month);
19678             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19679             if (new_month < 0 || new_month > 11) {
19680                 new_month = (new_month + 12) % 12;
19681             }
19682         } else {
19683             // For magnitudes >1, move one month at a time...
19684             for (var i=0; i<mag; i++) {
19685                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19686                 new_date = this.moveMonth(new_date, dir);
19687             }
19688             // ...then reset the day, keeping it in the new month
19689             new_month = new_date.getUTCMonth();
19690             new_date.setUTCDate(day);
19691             test = function(){
19692                 return new_month != new_date.getUTCMonth();
19693             };
19694         }
19695         // Common date-resetting loop -- if date is beyond end of month, make it
19696         // end of month
19697         while (test()){
19698             new_date.setUTCDate(--day);
19699             new_date.setUTCMonth(new_month);
19700         }
19701         return new_date;
19702     },
19703
19704     moveYear: function(date, dir)
19705     {
19706         return this.moveMonth(date, dir*12);
19707     },
19708
19709     dateWithinRange: function(date)
19710     {
19711         return date >= this.startDate && date <= this.endDate;
19712     },
19713
19714     
19715     remove: function() 
19716     {
19717         this.picker().remove();
19718     },
19719     
19720     validateValue : function(value)
19721     {
19722         if(this.getVisibilityEl().hasClass('hidden')){
19723             return true;
19724         }
19725         
19726         if(value.length < 1)  {
19727             if(this.allowBlank){
19728                 return true;
19729             }
19730             return false;
19731         }
19732         
19733         if(value.length < this.minLength){
19734             return false;
19735         }
19736         if(value.length > this.maxLength){
19737             return false;
19738         }
19739         if(this.vtype){
19740             var vt = Roo.form.VTypes;
19741             if(!vt[this.vtype](value, this)){
19742                 return false;
19743             }
19744         }
19745         if(typeof this.validator == "function"){
19746             var msg = this.validator(value);
19747             if(msg !== true){
19748                 return false;
19749             }
19750         }
19751         
19752         if(this.regex && !this.regex.test(value)){
19753             return false;
19754         }
19755         
19756         if(typeof(this.parseDate(value)) == 'undefined'){
19757             return false;
19758         }
19759         
19760         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19761             return false;
19762         }      
19763         
19764         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19765             return false;
19766         } 
19767         
19768         
19769         return true;
19770     },
19771     
19772     reset : function()
19773     {
19774         this.date = this.viewDate = '';
19775         
19776         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19777     }
19778    
19779 });
19780
19781 Roo.apply(Roo.bootstrap.DateField,  {
19782     
19783     head : {
19784         tag: 'thead',
19785         cn: [
19786         {
19787             tag: 'tr',
19788             cn: [
19789             {
19790                 tag: 'th',
19791                 cls: 'prev',
19792                 html: '<i class="fa fa-arrow-left"/>'
19793             },
19794             {
19795                 tag: 'th',
19796                 cls: 'switch',
19797                 colspan: '5'
19798             },
19799             {
19800                 tag: 'th',
19801                 cls: 'next',
19802                 html: '<i class="fa fa-arrow-right"/>'
19803             }
19804
19805             ]
19806         }
19807         ]
19808     },
19809     
19810     content : {
19811         tag: 'tbody',
19812         cn: [
19813         {
19814             tag: 'tr',
19815             cn: [
19816             {
19817                 tag: 'td',
19818                 colspan: '7'
19819             }
19820             ]
19821         }
19822         ]
19823     },
19824     
19825     footer : {
19826         tag: 'tfoot',
19827         cn: [
19828         {
19829             tag: 'tr',
19830             cn: [
19831             {
19832                 tag: 'th',
19833                 colspan: '7',
19834                 cls: 'today'
19835             }
19836                     
19837             ]
19838         }
19839         ]
19840     },
19841     
19842     dates:{
19843         en: {
19844             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19845             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19846             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19847             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19848             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19849             today: "Today"
19850         }
19851     },
19852     
19853     modes: [
19854     {
19855         clsName: 'days',
19856         navFnc: 'Month',
19857         navStep: 1
19858     },
19859     {
19860         clsName: 'months',
19861         navFnc: 'FullYear',
19862         navStep: 1
19863     },
19864     {
19865         clsName: 'years',
19866         navFnc: 'FullYear',
19867         navStep: 10
19868     }]
19869 });
19870
19871 Roo.apply(Roo.bootstrap.DateField,  {
19872   
19873     template : {
19874         tag: 'div',
19875         cls: 'datepicker dropdown-menu roo-dynamic',
19876         cn: [
19877         {
19878             tag: 'div',
19879             cls: 'datepicker-days',
19880             cn: [
19881             {
19882                 tag: 'table',
19883                 cls: 'table-condensed',
19884                 cn:[
19885                 Roo.bootstrap.DateField.head,
19886                 {
19887                     tag: 'tbody'
19888                 },
19889                 Roo.bootstrap.DateField.footer
19890                 ]
19891             }
19892             ]
19893         },
19894         {
19895             tag: 'div',
19896             cls: 'datepicker-months',
19897             cn: [
19898             {
19899                 tag: 'table',
19900                 cls: 'table-condensed',
19901                 cn:[
19902                 Roo.bootstrap.DateField.head,
19903                 Roo.bootstrap.DateField.content,
19904                 Roo.bootstrap.DateField.footer
19905                 ]
19906             }
19907             ]
19908         },
19909         {
19910             tag: 'div',
19911             cls: 'datepicker-years',
19912             cn: [
19913             {
19914                 tag: 'table',
19915                 cls: 'table-condensed',
19916                 cn:[
19917                 Roo.bootstrap.DateField.head,
19918                 Roo.bootstrap.DateField.content,
19919                 Roo.bootstrap.DateField.footer
19920                 ]
19921             }
19922             ]
19923         }
19924         ]
19925     }
19926 });
19927
19928  
19929
19930  /*
19931  * - LGPL
19932  *
19933  * TimeField
19934  * 
19935  */
19936
19937 /**
19938  * @class Roo.bootstrap.TimeField
19939  * @extends Roo.bootstrap.Input
19940  * Bootstrap DateField class
19941  * 
19942  * 
19943  * @constructor
19944  * Create a new TimeField
19945  * @param {Object} config The config object
19946  */
19947
19948 Roo.bootstrap.TimeField = function(config){
19949     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19950     this.addEvents({
19951             /**
19952              * @event show
19953              * Fires when this field show.
19954              * @param {Roo.bootstrap.DateField} thisthis
19955              * @param {Mixed} date The date value
19956              */
19957             show : true,
19958             /**
19959              * @event show
19960              * Fires when this field hide.
19961              * @param {Roo.bootstrap.DateField} this
19962              * @param {Mixed} date The date value
19963              */
19964             hide : true,
19965             /**
19966              * @event select
19967              * Fires when select a date.
19968              * @param {Roo.bootstrap.DateField} this
19969              * @param {Mixed} date The date value
19970              */
19971             select : true
19972         });
19973 };
19974
19975 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19976     
19977     /**
19978      * @cfg {String} format
19979      * The default time format string which can be overriden for localization support.  The format must be
19980      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19981      */
19982     format : "H:i",
19983        
19984     onRender: function(ct, position)
19985     {
19986         
19987         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19988                 
19989         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19990         
19991         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19992         
19993         this.pop = this.picker().select('>.datepicker-time',true).first();
19994         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19995         
19996         this.picker().on('mousedown', this.onMousedown, this);
19997         this.picker().on('click', this.onClick, this);
19998         
19999         this.picker().addClass('datepicker-dropdown');
20000     
20001         this.fillTime();
20002         this.update();
20003             
20004         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
20005         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
20006         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
20007         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
20008         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
20009         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
20010
20011     },
20012     
20013     fireKey: function(e){
20014         if (!this.picker().isVisible()){
20015             if (e.keyCode == 27) { // allow escape to hide and re-show picker
20016                 this.show();
20017             }
20018             return;
20019         }
20020
20021         e.preventDefault();
20022         
20023         switch(e.keyCode){
20024             case 27: // escape
20025                 this.hide();
20026                 break;
20027             case 37: // left
20028             case 39: // right
20029                 this.onTogglePeriod();
20030                 break;
20031             case 38: // up
20032                 this.onIncrementMinutes();
20033                 break;
20034             case 40: // down
20035                 this.onDecrementMinutes();
20036                 break;
20037             case 13: // enter
20038             case 9: // tab
20039                 this.setTime();
20040                 break;
20041         }
20042     },
20043     
20044     onClick: function(e) {
20045         e.stopPropagation();
20046         e.preventDefault();
20047     },
20048     
20049     picker : function()
20050     {
20051         return this.el.select('.datepicker', true).first();
20052     },
20053     
20054     fillTime: function()
20055     {    
20056         var time = this.pop.select('tbody', true).first();
20057         
20058         time.dom.innerHTML = '';
20059         
20060         time.createChild({
20061             tag: 'tr',
20062             cn: [
20063                 {
20064                     tag: 'td',
20065                     cn: [
20066                         {
20067                             tag: 'a',
20068                             href: '#',
20069                             cls: 'btn',
20070                             cn: [
20071                                 {
20072                                     tag: 'span',
20073                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
20074                                 }
20075                             ]
20076                         } 
20077                     ]
20078                 },
20079                 {
20080                     tag: 'td',
20081                     cls: 'separator'
20082                 },
20083                 {
20084                     tag: 'td',
20085                     cn: [
20086                         {
20087                             tag: 'a',
20088                             href: '#',
20089                             cls: 'btn',
20090                             cn: [
20091                                 {
20092                                     tag: 'span',
20093                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
20094                                 }
20095                             ]
20096                         }
20097                     ]
20098                 },
20099                 {
20100                     tag: 'td',
20101                     cls: 'separator'
20102                 }
20103             ]
20104         });
20105         
20106         time.createChild({
20107             tag: 'tr',
20108             cn: [
20109                 {
20110                     tag: 'td',
20111                     cn: [
20112                         {
20113                             tag: 'span',
20114                             cls: 'timepicker-hour',
20115                             html: '00'
20116                         }  
20117                     ]
20118                 },
20119                 {
20120                     tag: 'td',
20121                     cls: 'separator',
20122                     html: ':'
20123                 },
20124                 {
20125                     tag: 'td',
20126                     cn: [
20127                         {
20128                             tag: 'span',
20129                             cls: 'timepicker-minute',
20130                             html: '00'
20131                         }  
20132                     ]
20133                 },
20134                 {
20135                     tag: 'td',
20136                     cls: 'separator'
20137                 },
20138                 {
20139                     tag: 'td',
20140                     cn: [
20141                         {
20142                             tag: 'button',
20143                             type: 'button',
20144                             cls: 'btn btn-primary period',
20145                             html: 'AM'
20146                             
20147                         }
20148                     ]
20149                 }
20150             ]
20151         });
20152         
20153         time.createChild({
20154             tag: 'tr',
20155             cn: [
20156                 {
20157                     tag: 'td',
20158                     cn: [
20159                         {
20160                             tag: 'a',
20161                             href: '#',
20162                             cls: 'btn',
20163                             cn: [
20164                                 {
20165                                     tag: 'span',
20166                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
20167                                 }
20168                             ]
20169                         }
20170                     ]
20171                 },
20172                 {
20173                     tag: 'td',
20174                     cls: 'separator'
20175                 },
20176                 {
20177                     tag: 'td',
20178                     cn: [
20179                         {
20180                             tag: 'a',
20181                             href: '#',
20182                             cls: 'btn',
20183                             cn: [
20184                                 {
20185                                     tag: 'span',
20186                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
20187                                 }
20188                             ]
20189                         }
20190                     ]
20191                 },
20192                 {
20193                     tag: 'td',
20194                     cls: 'separator'
20195                 }
20196             ]
20197         });
20198         
20199     },
20200     
20201     update: function()
20202     {
20203         
20204         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20205         
20206         this.fill();
20207     },
20208     
20209     fill: function() 
20210     {
20211         var hours = this.time.getHours();
20212         var minutes = this.time.getMinutes();
20213         var period = 'AM';
20214         
20215         if(hours > 11){
20216             period = 'PM';
20217         }
20218         
20219         if(hours == 0){
20220             hours = 12;
20221         }
20222         
20223         
20224         if(hours > 12){
20225             hours = hours - 12;
20226         }
20227         
20228         if(hours < 10){
20229             hours = '0' + hours;
20230         }
20231         
20232         if(minutes < 10){
20233             minutes = '0' + minutes;
20234         }
20235         
20236         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20237         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20238         this.pop.select('button', true).first().dom.innerHTML = period;
20239         
20240     },
20241     
20242     place: function()
20243     {   
20244         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20245         
20246         var cls = ['bottom'];
20247         
20248         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20249             cls.pop();
20250             cls.push('top');
20251         }
20252         
20253         cls.push('right');
20254         
20255         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20256             cls.pop();
20257             cls.push('left');
20258         }
20259         
20260         this.picker().addClass(cls.join('-'));
20261         
20262         var _this = this;
20263         
20264         Roo.each(cls, function(c){
20265             if(c == 'bottom'){
20266                 _this.picker().setTop(_this.inputEl().getHeight());
20267                 return;
20268             }
20269             if(c == 'top'){
20270                 _this.picker().setTop(0 - _this.picker().getHeight());
20271                 return;
20272             }
20273             
20274             if(c == 'left'){
20275                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20276                 return;
20277             }
20278             if(c == 'right'){
20279                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20280                 return;
20281             }
20282         });
20283         
20284     },
20285   
20286     onFocus : function()
20287     {
20288         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20289         this.show();
20290     },
20291     
20292     onBlur : function()
20293     {
20294         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20295         this.hide();
20296     },
20297     
20298     show : function()
20299     {
20300         this.picker().show();
20301         this.pop.show();
20302         this.update();
20303         this.place();
20304         
20305         this.fireEvent('show', this, this.date);
20306     },
20307     
20308     hide : function()
20309     {
20310         this.picker().hide();
20311         this.pop.hide();
20312         
20313         this.fireEvent('hide', this, this.date);
20314     },
20315     
20316     setTime : function()
20317     {
20318         this.hide();
20319         this.setValue(this.time.format(this.format));
20320         
20321         this.fireEvent('select', this, this.date);
20322         
20323         
20324     },
20325     
20326     onMousedown: function(e){
20327         e.stopPropagation();
20328         e.preventDefault();
20329     },
20330     
20331     onIncrementHours: function()
20332     {
20333         Roo.log('onIncrementHours');
20334         this.time = this.time.add(Date.HOUR, 1);
20335         this.update();
20336         
20337     },
20338     
20339     onDecrementHours: function()
20340     {
20341         Roo.log('onDecrementHours');
20342         this.time = this.time.add(Date.HOUR, -1);
20343         this.update();
20344     },
20345     
20346     onIncrementMinutes: function()
20347     {
20348         Roo.log('onIncrementMinutes');
20349         this.time = this.time.add(Date.MINUTE, 1);
20350         this.update();
20351     },
20352     
20353     onDecrementMinutes: function()
20354     {
20355         Roo.log('onDecrementMinutes');
20356         this.time = this.time.add(Date.MINUTE, -1);
20357         this.update();
20358     },
20359     
20360     onTogglePeriod: function()
20361     {
20362         Roo.log('onTogglePeriod');
20363         this.time = this.time.add(Date.HOUR, 12);
20364         this.update();
20365     }
20366     
20367    
20368 });
20369
20370 Roo.apply(Roo.bootstrap.TimeField,  {
20371     
20372     content : {
20373         tag: 'tbody',
20374         cn: [
20375             {
20376                 tag: 'tr',
20377                 cn: [
20378                 {
20379                     tag: 'td',
20380                     colspan: '7'
20381                 }
20382                 ]
20383             }
20384         ]
20385     },
20386     
20387     footer : {
20388         tag: 'tfoot',
20389         cn: [
20390             {
20391                 tag: 'tr',
20392                 cn: [
20393                 {
20394                     tag: 'th',
20395                     colspan: '7',
20396                     cls: '',
20397                     cn: [
20398                         {
20399                             tag: 'button',
20400                             cls: 'btn btn-info ok',
20401                             html: 'OK'
20402                         }
20403                     ]
20404                 }
20405
20406                 ]
20407             }
20408         ]
20409     }
20410 });
20411
20412 Roo.apply(Roo.bootstrap.TimeField,  {
20413   
20414     template : {
20415         tag: 'div',
20416         cls: 'datepicker dropdown-menu',
20417         cn: [
20418             {
20419                 tag: 'div',
20420                 cls: 'datepicker-time',
20421                 cn: [
20422                 {
20423                     tag: 'table',
20424                     cls: 'table-condensed',
20425                     cn:[
20426                     Roo.bootstrap.TimeField.content,
20427                     Roo.bootstrap.TimeField.footer
20428                     ]
20429                 }
20430                 ]
20431             }
20432         ]
20433     }
20434 });
20435
20436  
20437
20438  /*
20439  * - LGPL
20440  *
20441  * MonthField
20442  * 
20443  */
20444
20445 /**
20446  * @class Roo.bootstrap.MonthField
20447  * @extends Roo.bootstrap.Input
20448  * Bootstrap MonthField class
20449  * 
20450  * @cfg {String} language default en
20451  * 
20452  * @constructor
20453  * Create a new MonthField
20454  * @param {Object} config The config object
20455  */
20456
20457 Roo.bootstrap.MonthField = function(config){
20458     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20459     
20460     this.addEvents({
20461         /**
20462          * @event show
20463          * Fires when this field show.
20464          * @param {Roo.bootstrap.MonthField} this
20465          * @param {Mixed} date The date value
20466          */
20467         show : true,
20468         /**
20469          * @event show
20470          * Fires when this field hide.
20471          * @param {Roo.bootstrap.MonthField} this
20472          * @param {Mixed} date The date value
20473          */
20474         hide : true,
20475         /**
20476          * @event select
20477          * Fires when select a date.
20478          * @param {Roo.bootstrap.MonthField} this
20479          * @param {String} oldvalue The old value
20480          * @param {String} newvalue The new value
20481          */
20482         select : true
20483     });
20484 };
20485
20486 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20487     
20488     onRender: function(ct, position)
20489     {
20490         
20491         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20492         
20493         this.language = this.language || 'en';
20494         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20495         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20496         
20497         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20498         this.isInline = false;
20499         this.isInput = true;
20500         this.component = this.el.select('.add-on', true).first() || false;
20501         this.component = (this.component && this.component.length === 0) ? false : this.component;
20502         this.hasInput = this.component && this.inputEL().length;
20503         
20504         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20505         
20506         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20507         
20508         this.picker().on('mousedown', this.onMousedown, this);
20509         this.picker().on('click', this.onClick, this);
20510         
20511         this.picker().addClass('datepicker-dropdown');
20512         
20513         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20514             v.setStyle('width', '189px');
20515         });
20516         
20517         this.fillMonths();
20518         
20519         this.update();
20520         
20521         if(this.isInline) {
20522             this.show();
20523         }
20524         
20525     },
20526     
20527     setValue: function(v, suppressEvent)
20528     {   
20529         var o = this.getValue();
20530         
20531         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20532         
20533         this.update();
20534
20535         if(suppressEvent !== true){
20536             this.fireEvent('select', this, o, v);
20537         }
20538         
20539     },
20540     
20541     getValue: function()
20542     {
20543         return this.value;
20544     },
20545     
20546     onClick: function(e) 
20547     {
20548         e.stopPropagation();
20549         e.preventDefault();
20550         
20551         var target = e.getTarget();
20552         
20553         if(target.nodeName.toLowerCase() === 'i'){
20554             target = Roo.get(target).dom.parentNode;
20555         }
20556         
20557         var nodeName = target.nodeName;
20558         var className = target.className;
20559         var html = target.innerHTML;
20560         
20561         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20562             return;
20563         }
20564         
20565         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20566         
20567         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20568         
20569         this.hide();
20570                         
20571     },
20572     
20573     picker : function()
20574     {
20575         return this.pickerEl;
20576     },
20577     
20578     fillMonths: function()
20579     {    
20580         var i = 0;
20581         var months = this.picker().select('>.datepicker-months td', true).first();
20582         
20583         months.dom.innerHTML = '';
20584         
20585         while (i < 12) {
20586             var month = {
20587                 tag: 'span',
20588                 cls: 'month',
20589                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20590             };
20591             
20592             months.createChild(month);
20593         }
20594         
20595     },
20596     
20597     update: function()
20598     {
20599         var _this = this;
20600         
20601         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20602             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20603         }
20604         
20605         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20606             e.removeClass('active');
20607             
20608             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20609                 e.addClass('active');
20610             }
20611         })
20612     },
20613     
20614     place: function()
20615     {
20616         if(this.isInline) {
20617             return;
20618         }
20619         
20620         this.picker().removeClass(['bottom', 'top']);
20621         
20622         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20623             /*
20624              * place to the top of element!
20625              *
20626              */
20627             
20628             this.picker().addClass('top');
20629             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20630             
20631             return;
20632         }
20633         
20634         this.picker().addClass('bottom');
20635         
20636         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20637     },
20638     
20639     onFocus : function()
20640     {
20641         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20642         this.show();
20643     },
20644     
20645     onBlur : function()
20646     {
20647         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20648         
20649         var d = this.inputEl().getValue();
20650         
20651         this.setValue(d);
20652                 
20653         this.hide();
20654     },
20655     
20656     show : function()
20657     {
20658         this.picker().show();
20659         this.picker().select('>.datepicker-months', true).first().show();
20660         this.update();
20661         this.place();
20662         
20663         this.fireEvent('show', this, this.date);
20664     },
20665     
20666     hide : function()
20667     {
20668         if(this.isInline) {
20669             return;
20670         }
20671         this.picker().hide();
20672         this.fireEvent('hide', this, this.date);
20673         
20674     },
20675     
20676     onMousedown: function(e)
20677     {
20678         e.stopPropagation();
20679         e.preventDefault();
20680     },
20681     
20682     keyup: function(e)
20683     {
20684         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20685         this.update();
20686     },
20687
20688     fireKey: function(e)
20689     {
20690         if (!this.picker().isVisible()){
20691             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20692                 this.show();
20693             }
20694             return;
20695         }
20696         
20697         var dir;
20698         
20699         switch(e.keyCode){
20700             case 27: // escape
20701                 this.hide();
20702                 e.preventDefault();
20703                 break;
20704             case 37: // left
20705             case 39: // right
20706                 dir = e.keyCode == 37 ? -1 : 1;
20707                 
20708                 this.vIndex = this.vIndex + dir;
20709                 
20710                 if(this.vIndex < 0){
20711                     this.vIndex = 0;
20712                 }
20713                 
20714                 if(this.vIndex > 11){
20715                     this.vIndex = 11;
20716                 }
20717                 
20718                 if(isNaN(this.vIndex)){
20719                     this.vIndex = 0;
20720                 }
20721                 
20722                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20723                 
20724                 break;
20725             case 38: // up
20726             case 40: // down
20727                 
20728                 dir = e.keyCode == 38 ? -1 : 1;
20729                 
20730                 this.vIndex = this.vIndex + dir * 4;
20731                 
20732                 if(this.vIndex < 0){
20733                     this.vIndex = 0;
20734                 }
20735                 
20736                 if(this.vIndex > 11){
20737                     this.vIndex = 11;
20738                 }
20739                 
20740                 if(isNaN(this.vIndex)){
20741                     this.vIndex = 0;
20742                 }
20743                 
20744                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20745                 break;
20746                 
20747             case 13: // enter
20748                 
20749                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20750                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20751                 }
20752                 
20753                 this.hide();
20754                 e.preventDefault();
20755                 break;
20756             case 9: // tab
20757                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20758                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20759                 }
20760                 this.hide();
20761                 break;
20762             case 16: // shift
20763             case 17: // ctrl
20764             case 18: // alt
20765                 break;
20766             default :
20767                 this.hide();
20768                 
20769         }
20770     },
20771     
20772     remove: function() 
20773     {
20774         this.picker().remove();
20775     }
20776    
20777 });
20778
20779 Roo.apply(Roo.bootstrap.MonthField,  {
20780     
20781     content : {
20782         tag: 'tbody',
20783         cn: [
20784         {
20785             tag: 'tr',
20786             cn: [
20787             {
20788                 tag: 'td',
20789                 colspan: '7'
20790             }
20791             ]
20792         }
20793         ]
20794     },
20795     
20796     dates:{
20797         en: {
20798             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20799             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20800         }
20801     }
20802 });
20803
20804 Roo.apply(Roo.bootstrap.MonthField,  {
20805   
20806     template : {
20807         tag: 'div',
20808         cls: 'datepicker dropdown-menu roo-dynamic',
20809         cn: [
20810             {
20811                 tag: 'div',
20812                 cls: 'datepicker-months',
20813                 cn: [
20814                 {
20815                     tag: 'table',
20816                     cls: 'table-condensed',
20817                     cn:[
20818                         Roo.bootstrap.DateField.content
20819                     ]
20820                 }
20821                 ]
20822             }
20823         ]
20824     }
20825 });
20826
20827  
20828
20829  
20830  /*
20831  * - LGPL
20832  *
20833  * CheckBox
20834  * 
20835  */
20836
20837 /**
20838  * @class Roo.bootstrap.CheckBox
20839  * @extends Roo.bootstrap.Input
20840  * Bootstrap CheckBox class
20841  * 
20842  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20843  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20844  * @cfg {String} boxLabel The text that appears beside the checkbox
20845  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20846  * @cfg {Boolean} checked initnal the element
20847  * @cfg {Boolean} inline inline the element (default false)
20848  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20849  * @cfg {String} tooltip label tooltip
20850  * 
20851  * @constructor
20852  * Create a new CheckBox
20853  * @param {Object} config The config object
20854  */
20855
20856 Roo.bootstrap.CheckBox = function(config){
20857     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20858    
20859     this.addEvents({
20860         /**
20861         * @event check
20862         * Fires when the element is checked or unchecked.
20863         * @param {Roo.bootstrap.CheckBox} this This input
20864         * @param {Boolean} checked The new checked value
20865         */
20866        check : true,
20867        /**
20868         * @event click
20869         * Fires when the element is click.
20870         * @param {Roo.bootstrap.CheckBox} this This input
20871         */
20872        click : true
20873     });
20874     
20875 };
20876
20877 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20878   
20879     inputType: 'checkbox',
20880     inputValue: 1,
20881     valueOff: 0,
20882     boxLabel: false,
20883     checked: false,
20884     weight : false,
20885     inline: false,
20886     tooltip : '',
20887     
20888     getAutoCreate : function()
20889     {
20890         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20891         
20892         var id = Roo.id();
20893         
20894         var cfg = {};
20895         
20896         cfg.cls = 'form-group ' + this.inputType; //input-group
20897         
20898         if(this.inline){
20899             cfg.cls += ' ' + this.inputType + '-inline';
20900         }
20901         
20902         var input =  {
20903             tag: 'input',
20904             id : id,
20905             type : this.inputType,
20906             value : this.inputValue,
20907             cls : 'roo-' + this.inputType, //'form-box',
20908             placeholder : this.placeholder || ''
20909             
20910         };
20911         
20912         if(this.inputType != 'radio'){
20913             var hidden =  {
20914                 tag: 'input',
20915                 type : 'hidden',
20916                 cls : 'roo-hidden-value',
20917                 value : this.checked ? this.inputValue : this.valueOff
20918             };
20919         }
20920         
20921             
20922         if (this.weight) { // Validity check?
20923             cfg.cls += " " + this.inputType + "-" + this.weight;
20924         }
20925         
20926         if (this.disabled) {
20927             input.disabled=true;
20928         }
20929         
20930         if(this.checked){
20931             input.checked = this.checked;
20932         }
20933         
20934         if (this.name) {
20935             
20936             input.name = this.name;
20937             
20938             if(this.inputType != 'radio'){
20939                 hidden.name = this.name;
20940                 input.name = '_hidden_' + this.name;
20941             }
20942         }
20943         
20944         if (this.size) {
20945             input.cls += ' input-' + this.size;
20946         }
20947         
20948         var settings=this;
20949         
20950         ['xs','sm','md','lg'].map(function(size){
20951             if (settings[size]) {
20952                 cfg.cls += ' col-' + size + '-' + settings[size];
20953             }
20954         });
20955         
20956         var inputblock = input;
20957          
20958         if (this.before || this.after) {
20959             
20960             inputblock = {
20961                 cls : 'input-group',
20962                 cn :  [] 
20963             };
20964             
20965             if (this.before) {
20966                 inputblock.cn.push({
20967                     tag :'span',
20968                     cls : 'input-group-addon',
20969                     html : this.before
20970                 });
20971             }
20972             
20973             inputblock.cn.push(input);
20974             
20975             if(this.inputType != 'radio'){
20976                 inputblock.cn.push(hidden);
20977             }
20978             
20979             if (this.after) {
20980                 inputblock.cn.push({
20981                     tag :'span',
20982                     cls : 'input-group-addon',
20983                     html : this.after
20984                 });
20985             }
20986             
20987         }
20988         
20989         if (align ==='left' && this.fieldLabel.length) {
20990 //                Roo.log("left and has label");
20991             cfg.cn = [
20992                 {
20993                     tag: 'label',
20994                     'for' :  id,
20995                     cls : 'control-label',
20996                     html : this.fieldLabel
20997                 },
20998                 {
20999                     cls : "", 
21000                     cn: [
21001                         inputblock
21002                     ]
21003                 }
21004             ];
21005             
21006             if(this.labelWidth > 12){
21007                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
21008             }
21009             
21010             if(this.labelWidth < 13 && this.labelmd == 0){
21011                 this.labelmd = this.labelWidth;
21012             }
21013             
21014             if(this.labellg > 0){
21015                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
21016                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
21017             }
21018             
21019             if(this.labelmd > 0){
21020                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
21021                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
21022             }
21023             
21024             if(this.labelsm > 0){
21025                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
21026                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
21027             }
21028             
21029             if(this.labelxs > 0){
21030                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
21031                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
21032             }
21033             
21034         } else if ( this.fieldLabel.length) {
21035 //                Roo.log(" label");
21036                 cfg.cn = [
21037                    
21038                     {
21039                         tag: this.boxLabel ? 'span' : 'label',
21040                         'for': id,
21041                         cls: 'control-label box-input-label',
21042                         //cls : 'input-group-addon',
21043                         html : this.fieldLabel
21044                     },
21045                     
21046                     inputblock
21047                     
21048                 ];
21049
21050         } else {
21051             
21052 //                Roo.log(" no label && no align");
21053                 cfg.cn = [  inputblock ] ;
21054                 
21055                 
21056         }
21057         
21058         if(this.boxLabel){
21059              var boxLabelCfg = {
21060                 tag: 'label',
21061                 //'for': id, // box label is handled by onclick - so no for...
21062                 cls: 'box-label',
21063                 html: this.boxLabel
21064             };
21065             
21066             if(this.tooltip){
21067                 boxLabelCfg.tooltip = this.tooltip;
21068             }
21069              
21070             cfg.cn.push(boxLabelCfg);
21071         }
21072         
21073         if(this.inputType != 'radio'){
21074             cfg.cn.push(hidden);
21075         }
21076         
21077         return cfg;
21078         
21079     },
21080     
21081     /**
21082      * return the real input element.
21083      */
21084     inputEl: function ()
21085     {
21086         return this.el.select('input.roo-' + this.inputType,true).first();
21087     },
21088     hiddenEl: function ()
21089     {
21090         return this.el.select('input.roo-hidden-value',true).first();
21091     },
21092     
21093     labelEl: function()
21094     {
21095         return this.el.select('label.control-label',true).first();
21096     },
21097     /* depricated... */
21098     
21099     label: function()
21100     {
21101         return this.labelEl();
21102     },
21103     
21104     boxLabelEl: function()
21105     {
21106         return this.el.select('label.box-label',true).first();
21107     },
21108     
21109     initEvents : function()
21110     {
21111 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
21112         
21113         this.inputEl().on('click', this.onClick,  this);
21114         
21115         if (this.boxLabel) { 
21116             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
21117         }
21118         
21119         this.startValue = this.getValue();
21120         
21121         if(this.groupId){
21122             Roo.bootstrap.CheckBox.register(this);
21123         }
21124     },
21125     
21126     onClick : function(e)
21127     {   
21128         if(this.fireEvent('click', this, e) !== false){
21129             this.setChecked(!this.checked);
21130         }
21131         
21132     },
21133     
21134     setChecked : function(state,suppressEvent)
21135     {
21136         this.startValue = this.getValue();
21137
21138         if(this.inputType == 'radio'){
21139             
21140             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21141                 e.dom.checked = false;
21142             });
21143             
21144             this.inputEl().dom.checked = true;
21145             
21146             this.inputEl().dom.value = this.inputValue;
21147             
21148             if(suppressEvent !== true){
21149                 this.fireEvent('check', this, true);
21150             }
21151             
21152             this.validate();
21153             
21154             return;
21155         }
21156         
21157         this.checked = state;
21158         
21159         this.inputEl().dom.checked = state;
21160         
21161         
21162         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
21163         
21164         if(suppressEvent !== true){
21165             this.fireEvent('check', this, state);
21166         }
21167         
21168         this.validate();
21169     },
21170     
21171     getValue : function()
21172     {
21173         if(this.inputType == 'radio'){
21174             return this.getGroupValue();
21175         }
21176         
21177         return this.hiddenEl().dom.value;
21178         
21179     },
21180     
21181     getGroupValue : function()
21182     {
21183         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21184             return '';
21185         }
21186         
21187         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21188     },
21189     
21190     setValue : function(v,suppressEvent)
21191     {
21192         if(this.inputType == 'radio'){
21193             this.setGroupValue(v, suppressEvent);
21194             return;
21195         }
21196         
21197         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21198         
21199         this.validate();
21200     },
21201     
21202     setGroupValue : function(v, suppressEvent)
21203     {
21204         this.startValue = this.getValue();
21205         
21206         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21207             e.dom.checked = false;
21208             
21209             if(e.dom.value == v){
21210                 e.dom.checked = true;
21211             }
21212         });
21213         
21214         if(suppressEvent !== true){
21215             this.fireEvent('check', this, true);
21216         }
21217
21218         this.validate();
21219         
21220         return;
21221     },
21222     
21223     validate : function()
21224     {
21225         if(this.getVisibilityEl().hasClass('hidden')){
21226             return true;
21227         }
21228         
21229         if(
21230                 this.disabled || 
21231                 (this.inputType == 'radio' && this.validateRadio()) ||
21232                 (this.inputType == 'checkbox' && this.validateCheckbox())
21233         ){
21234             this.markValid();
21235             return true;
21236         }
21237         
21238         this.markInvalid();
21239         return false;
21240     },
21241     
21242     validateRadio : function()
21243     {
21244         if(this.getVisibilityEl().hasClass('hidden')){
21245             return true;
21246         }
21247         
21248         if(this.allowBlank){
21249             return true;
21250         }
21251         
21252         var valid = false;
21253         
21254         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21255             if(!e.dom.checked){
21256                 return;
21257             }
21258             
21259             valid = true;
21260             
21261             return false;
21262         });
21263         
21264         return valid;
21265     },
21266     
21267     validateCheckbox : function()
21268     {
21269         if(!this.groupId){
21270             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21271             //return (this.getValue() == this.inputValue) ? true : false;
21272         }
21273         
21274         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21275         
21276         if(!group){
21277             return false;
21278         }
21279         
21280         var r = false;
21281         
21282         for(var i in group){
21283             if(group[i].el.isVisible(true)){
21284                 r = false;
21285                 break;
21286             }
21287             
21288             r = true;
21289         }
21290         
21291         for(var i in group){
21292             if(r){
21293                 break;
21294             }
21295             
21296             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21297         }
21298         
21299         return r;
21300     },
21301     
21302     /**
21303      * Mark this field as valid
21304      */
21305     markValid : function()
21306     {
21307         var _this = this;
21308         
21309         this.fireEvent('valid', this);
21310         
21311         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21312         
21313         if(this.groupId){
21314             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21315         }
21316         
21317         if(label){
21318             label.markValid();
21319         }
21320
21321         if(this.inputType == 'radio'){
21322             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21323                 var fg = e.findParent('.form-group', false, true);
21324                 if (Roo.bootstrap.version == 3) {
21325                     fg.removeClass([_this.invalidClass, _this.validClass]);
21326                     fg.addClass(_this.validClass);
21327                 } else {
21328                     fg.removeClass(['is-valid', 'is-invalid']);
21329                     fg.addClass('is-valid');
21330                 }
21331             });
21332             
21333             return;
21334         }
21335
21336         if(!this.groupId){
21337             var fg = this.el.findParent('.form-group', false, true);
21338             if (Roo.bootstrap.version == 3) {
21339                 fg.removeClass([this.invalidClass, this.validClass]);
21340                 fg.addClass(this.validClass);
21341             } else {
21342                 fg.removeClass(['is-valid', 'is-invalid']);
21343                 fg.addClass('is-valid');
21344             }
21345             return;
21346         }
21347         
21348         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21349         
21350         if(!group){
21351             return;
21352         }
21353         
21354         for(var i in group){
21355             var fg = group[i].el.findParent('.form-group', false, true);
21356             if (Roo.bootstrap.version == 3) {
21357                 fg.removeClass([this.invalidClass, this.validClass]);
21358                 fg.addClass(this.validClass);
21359             } else {
21360                 fg.removeClass(['is-valid', 'is-invalid']);
21361                 fg.addClass('is-valid');
21362             }
21363         }
21364     },
21365     
21366      /**
21367      * Mark this field as invalid
21368      * @param {String} msg The validation message
21369      */
21370     markInvalid : function(msg)
21371     {
21372         if(this.allowBlank){
21373             return;
21374         }
21375         
21376         var _this = this;
21377         
21378         this.fireEvent('invalid', this, msg);
21379         
21380         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21381         
21382         if(this.groupId){
21383             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21384         }
21385         
21386         if(label){
21387             label.markInvalid();
21388         }
21389             
21390         if(this.inputType == 'radio'){
21391             
21392             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21393                 var fg = e.findParent('.form-group', false, true);
21394                 if (Roo.bootstrap.version == 3) {
21395                     fg.removeClass([_this.invalidClass, _this.validClass]);
21396                     fg.addClass(_this.invalidClass);
21397                 } else {
21398                     fg.removeClass(['is-invalid', 'is-valid']);
21399                     fg.addClass('is-invalid');
21400                 }
21401             });
21402             
21403             return;
21404         }
21405         
21406         if(!this.groupId){
21407             var fg = this.el.findParent('.form-group', false, true);
21408             if (Roo.bootstrap.version == 3) {
21409                 fg.removeClass([_this.invalidClass, _this.validClass]);
21410                 fg.addClass(_this.invalidClass);
21411             } else {
21412                 fg.removeClass(['is-invalid', 'is-valid']);
21413                 fg.addClass('is-invalid');
21414             }
21415             return;
21416         }
21417         
21418         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21419         
21420         if(!group){
21421             return;
21422         }
21423         
21424         for(var i in group){
21425             var fg = group[i].el.findParent('.form-group', false, true);
21426             if (Roo.bootstrap.version == 3) {
21427                 fg.removeClass([_this.invalidClass, _this.validClass]);
21428                 fg.addClass(_this.invalidClass);
21429             } else {
21430                 fg.removeClass(['is-invalid', 'is-valid']);
21431                 fg.addClass('is-invalid');
21432             }
21433         }
21434         
21435     },
21436     
21437     clearInvalid : function()
21438     {
21439         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21440         
21441         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21442         
21443         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21444         
21445         if (label && label.iconEl) {
21446             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
21447             label.iconEl.removeClass(['is-invalid', 'is-valid']);
21448         }
21449     },
21450     
21451     disable : function()
21452     {
21453         if(this.inputType != 'radio'){
21454             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21455             return;
21456         }
21457         
21458         var _this = this;
21459         
21460         if(this.rendered){
21461             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21462                 _this.getActionEl().addClass(this.disabledClass);
21463                 e.dom.disabled = true;
21464             });
21465         }
21466         
21467         this.disabled = true;
21468         this.fireEvent("disable", this);
21469         return this;
21470     },
21471
21472     enable : function()
21473     {
21474         if(this.inputType != 'radio'){
21475             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21476             return;
21477         }
21478         
21479         var _this = this;
21480         
21481         if(this.rendered){
21482             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21483                 _this.getActionEl().removeClass(this.disabledClass);
21484                 e.dom.disabled = false;
21485             });
21486         }
21487         
21488         this.disabled = false;
21489         this.fireEvent("enable", this);
21490         return this;
21491     },
21492     
21493     setBoxLabel : function(v)
21494     {
21495         this.boxLabel = v;
21496         
21497         if(this.rendered){
21498             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21499         }
21500     }
21501
21502 });
21503
21504 Roo.apply(Roo.bootstrap.CheckBox, {
21505     
21506     groups: {},
21507     
21508      /**
21509     * register a CheckBox Group
21510     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21511     */
21512     register : function(checkbox)
21513     {
21514         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21515             this.groups[checkbox.groupId] = {};
21516         }
21517         
21518         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21519             return;
21520         }
21521         
21522         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21523         
21524     },
21525     /**
21526     * fetch a CheckBox Group based on the group ID
21527     * @param {string} the group ID
21528     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21529     */
21530     get: function(groupId) {
21531         if (typeof(this.groups[groupId]) == 'undefined') {
21532             return false;
21533         }
21534         
21535         return this.groups[groupId] ;
21536     }
21537     
21538     
21539 });
21540 /*
21541  * - LGPL
21542  *
21543  * RadioItem
21544  * 
21545  */
21546
21547 /**
21548  * @class Roo.bootstrap.Radio
21549  * @extends Roo.bootstrap.Component
21550  * Bootstrap Radio class
21551  * @cfg {String} boxLabel - the label associated
21552  * @cfg {String} value - the value of radio
21553  * 
21554  * @constructor
21555  * Create a new Radio
21556  * @param {Object} config The config object
21557  */
21558 Roo.bootstrap.Radio = function(config){
21559     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21560     
21561 };
21562
21563 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21564     
21565     boxLabel : '',
21566     
21567     value : '',
21568     
21569     getAutoCreate : function()
21570     {
21571         var cfg = {
21572             tag : 'div',
21573             cls : 'form-group radio',
21574             cn : [
21575                 {
21576                     tag : 'label',
21577                     cls : 'box-label',
21578                     html : this.boxLabel
21579                 }
21580             ]
21581         };
21582         
21583         return cfg;
21584     },
21585     
21586     initEvents : function() 
21587     {
21588         this.parent().register(this);
21589         
21590         this.el.on('click', this.onClick, this);
21591         
21592     },
21593     
21594     onClick : function(e)
21595     {
21596         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21597             this.setChecked(true);
21598         }
21599     },
21600     
21601     setChecked : function(state, suppressEvent)
21602     {
21603         this.parent().setValue(this.value, suppressEvent);
21604         
21605     },
21606     
21607     setBoxLabel : function(v)
21608     {
21609         this.boxLabel = v;
21610         
21611         if(this.rendered){
21612             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21613         }
21614     }
21615     
21616 });
21617  
21618
21619  /*
21620  * - LGPL
21621  *
21622  * Input
21623  * 
21624  */
21625
21626 /**
21627  * @class Roo.bootstrap.SecurePass
21628  * @extends Roo.bootstrap.Input
21629  * Bootstrap SecurePass class
21630  *
21631  * 
21632  * @constructor
21633  * Create a new SecurePass
21634  * @param {Object} config The config object
21635  */
21636  
21637 Roo.bootstrap.SecurePass = function (config) {
21638     // these go here, so the translation tool can replace them..
21639     this.errors = {
21640         PwdEmpty: "Please type a password, and then retype it to confirm.",
21641         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21642         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21643         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21644         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21645         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21646         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21647         TooWeak: "Your password is Too Weak."
21648     },
21649     this.meterLabel = "Password strength:";
21650     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21651     this.meterClass = [
21652         "roo-password-meter-tooweak", 
21653         "roo-password-meter-weak", 
21654         "roo-password-meter-medium", 
21655         "roo-password-meter-strong", 
21656         "roo-password-meter-grey"
21657     ];
21658     
21659     this.errors = {};
21660     
21661     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21662 }
21663
21664 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21665     /**
21666      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21667      * {
21668      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21669      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21670      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21671      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21672      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21673      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21674      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21675      * })
21676      */
21677     // private
21678     
21679     meterWidth: 300,
21680     errorMsg :'',    
21681     errors: false,
21682     imageRoot: '/',
21683     /**
21684      * @cfg {String/Object} Label for the strength meter (defaults to
21685      * 'Password strength:')
21686      */
21687     // private
21688     meterLabel: '',
21689     /**
21690      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21691      * ['Weak', 'Medium', 'Strong'])
21692      */
21693     // private    
21694     pwdStrengths: false,    
21695     // private
21696     strength: 0,
21697     // private
21698     _lastPwd: null,
21699     // private
21700     kCapitalLetter: 0,
21701     kSmallLetter: 1,
21702     kDigit: 2,
21703     kPunctuation: 3,
21704     
21705     insecure: false,
21706     // private
21707     initEvents: function ()
21708     {
21709         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21710
21711         if (this.el.is('input[type=password]') && Roo.isSafari) {
21712             this.el.on('keydown', this.SafariOnKeyDown, this);
21713         }
21714
21715         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21716     },
21717     // private
21718     onRender: function (ct, position)
21719     {
21720         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21721         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21722         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21723
21724         this.trigger.createChild({
21725                    cn: [
21726                     {
21727                     //id: 'PwdMeter',
21728                     tag: 'div',
21729                     cls: 'roo-password-meter-grey col-xs-12',
21730                     style: {
21731                         //width: 0,
21732                         //width: this.meterWidth + 'px'                                                
21733                         }
21734                     },
21735                     {                            
21736                          cls: 'roo-password-meter-text'                          
21737                     }
21738                 ]            
21739         });
21740
21741          
21742         if (this.hideTrigger) {
21743             this.trigger.setDisplayed(false);
21744         }
21745         this.setSize(this.width || '', this.height || '');
21746     },
21747     // private
21748     onDestroy: function ()
21749     {
21750         if (this.trigger) {
21751             this.trigger.removeAllListeners();
21752             this.trigger.remove();
21753         }
21754         if (this.wrap) {
21755             this.wrap.remove();
21756         }
21757         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21758     },
21759     // private
21760     checkStrength: function ()
21761     {
21762         var pwd = this.inputEl().getValue();
21763         if (pwd == this._lastPwd) {
21764             return;
21765         }
21766
21767         var strength;
21768         if (this.ClientSideStrongPassword(pwd)) {
21769             strength = 3;
21770         } else if (this.ClientSideMediumPassword(pwd)) {
21771             strength = 2;
21772         } else if (this.ClientSideWeakPassword(pwd)) {
21773             strength = 1;
21774         } else {
21775             strength = 0;
21776         }
21777         
21778         Roo.log('strength1: ' + strength);
21779         
21780         //var pm = this.trigger.child('div/div/div').dom;
21781         var pm = this.trigger.child('div/div');
21782         pm.removeClass(this.meterClass);
21783         pm.addClass(this.meterClass[strength]);
21784                 
21785         
21786         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21787                 
21788         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21789         
21790         this._lastPwd = pwd;
21791     },
21792     reset: function ()
21793     {
21794         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21795         
21796         this._lastPwd = '';
21797         
21798         var pm = this.trigger.child('div/div');
21799         pm.removeClass(this.meterClass);
21800         pm.addClass('roo-password-meter-grey');        
21801         
21802         
21803         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21804         
21805         pt.innerHTML = '';
21806         this.inputEl().dom.type='password';
21807     },
21808     // private
21809     validateValue: function (value)
21810     {
21811         
21812         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21813             return false;
21814         }
21815         if (value.length == 0) {
21816             if (this.allowBlank) {
21817                 this.clearInvalid();
21818                 return true;
21819             }
21820
21821             this.markInvalid(this.errors.PwdEmpty);
21822             this.errorMsg = this.errors.PwdEmpty;
21823             return false;
21824         }
21825         
21826         if(this.insecure){
21827             return true;
21828         }
21829         
21830         if ('[\x21-\x7e]*'.match(value)) {
21831             this.markInvalid(this.errors.PwdBadChar);
21832             this.errorMsg = this.errors.PwdBadChar;
21833             return false;
21834         }
21835         if (value.length < 6) {
21836             this.markInvalid(this.errors.PwdShort);
21837             this.errorMsg = this.errors.PwdShort;
21838             return false;
21839         }
21840         if (value.length > 16) {
21841             this.markInvalid(this.errors.PwdLong);
21842             this.errorMsg = this.errors.PwdLong;
21843             return false;
21844         }
21845         var strength;
21846         if (this.ClientSideStrongPassword(value)) {
21847             strength = 3;
21848         } else if (this.ClientSideMediumPassword(value)) {
21849             strength = 2;
21850         } else if (this.ClientSideWeakPassword(value)) {
21851             strength = 1;
21852         } else {
21853             strength = 0;
21854         }
21855
21856         
21857         if (strength < 2) {
21858             //this.markInvalid(this.errors.TooWeak);
21859             this.errorMsg = this.errors.TooWeak;
21860             //return false;
21861         }
21862         
21863         
21864         console.log('strength2: ' + strength);
21865         
21866         //var pm = this.trigger.child('div/div/div').dom;
21867         
21868         var pm = this.trigger.child('div/div');
21869         pm.removeClass(this.meterClass);
21870         pm.addClass(this.meterClass[strength]);
21871                 
21872         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21873                 
21874         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21875         
21876         this.errorMsg = ''; 
21877         return true;
21878     },
21879     // private
21880     CharacterSetChecks: function (type)
21881     {
21882         this.type = type;
21883         this.fResult = false;
21884     },
21885     // private
21886     isctype: function (character, type)
21887     {
21888         switch (type) {  
21889             case this.kCapitalLetter:
21890                 if (character >= 'A' && character <= 'Z') {
21891                     return true;
21892                 }
21893                 break;
21894             
21895             case this.kSmallLetter:
21896                 if (character >= 'a' && character <= 'z') {
21897                     return true;
21898                 }
21899                 break;
21900             
21901             case this.kDigit:
21902                 if (character >= '0' && character <= '9') {
21903                     return true;
21904                 }
21905                 break;
21906             
21907             case this.kPunctuation:
21908                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21909                     return true;
21910                 }
21911                 break;
21912             
21913             default:
21914                 return false;
21915         }
21916
21917     },
21918     // private
21919     IsLongEnough: function (pwd, size)
21920     {
21921         return !(pwd == null || isNaN(size) || pwd.length < size);
21922     },
21923     // private
21924     SpansEnoughCharacterSets: function (word, nb)
21925     {
21926         if (!this.IsLongEnough(word, nb))
21927         {
21928             return false;
21929         }
21930
21931         var characterSetChecks = new Array(
21932             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21933             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21934         );
21935         
21936         for (var index = 0; index < word.length; ++index) {
21937             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21938                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21939                     characterSetChecks[nCharSet].fResult = true;
21940                     break;
21941                 }
21942             }
21943         }
21944
21945         var nCharSets = 0;
21946         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21947             if (characterSetChecks[nCharSet].fResult) {
21948                 ++nCharSets;
21949             }
21950         }
21951
21952         if (nCharSets < nb) {
21953             return false;
21954         }
21955         return true;
21956     },
21957     // private
21958     ClientSideStrongPassword: function (pwd)
21959     {
21960         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21961     },
21962     // private
21963     ClientSideMediumPassword: function (pwd)
21964     {
21965         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21966     },
21967     // private
21968     ClientSideWeakPassword: function (pwd)
21969     {
21970         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21971     }
21972           
21973 })//<script type="text/javascript">
21974
21975 /*
21976  * Based  Ext JS Library 1.1.1
21977  * Copyright(c) 2006-2007, Ext JS, LLC.
21978  * LGPL
21979  *
21980  */
21981  
21982 /**
21983  * @class Roo.HtmlEditorCore
21984  * @extends Roo.Component
21985  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21986  *
21987  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21988  */
21989
21990 Roo.HtmlEditorCore = function(config){
21991     
21992     
21993     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21994     
21995     
21996     this.addEvents({
21997         /**
21998          * @event initialize
21999          * Fires when the editor is fully initialized (including the iframe)
22000          * @param {Roo.HtmlEditorCore} this
22001          */
22002         initialize: true,
22003         /**
22004          * @event activate
22005          * Fires when the editor is first receives the focus. Any insertion must wait
22006          * until after this event.
22007          * @param {Roo.HtmlEditorCore} this
22008          */
22009         activate: true,
22010          /**
22011          * @event beforesync
22012          * Fires before the textarea is updated with content from the editor iframe. Return false
22013          * to cancel the sync.
22014          * @param {Roo.HtmlEditorCore} this
22015          * @param {String} html
22016          */
22017         beforesync: true,
22018          /**
22019          * @event beforepush
22020          * Fires before the iframe editor is updated with content from the textarea. Return false
22021          * to cancel the push.
22022          * @param {Roo.HtmlEditorCore} this
22023          * @param {String} html
22024          */
22025         beforepush: true,
22026          /**
22027          * @event sync
22028          * Fires when the textarea is updated with content from the editor iframe.
22029          * @param {Roo.HtmlEditorCore} this
22030          * @param {String} html
22031          */
22032         sync: true,
22033          /**
22034          * @event push
22035          * Fires when the iframe editor is updated with content from the textarea.
22036          * @param {Roo.HtmlEditorCore} this
22037          * @param {String} html
22038          */
22039         push: true,
22040         
22041         /**
22042          * @event editorevent
22043          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22044          * @param {Roo.HtmlEditorCore} this
22045          */
22046         editorevent: true
22047         
22048     });
22049     
22050     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
22051     
22052     // defaults : white / black...
22053     this.applyBlacklists();
22054     
22055     
22056     
22057 };
22058
22059
22060 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
22061
22062
22063      /**
22064      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
22065      */
22066     
22067     owner : false,
22068     
22069      /**
22070      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22071      *                        Roo.resizable.
22072      */
22073     resizable : false,
22074      /**
22075      * @cfg {Number} height (in pixels)
22076      */   
22077     height: 300,
22078    /**
22079      * @cfg {Number} width (in pixels)
22080      */   
22081     width: 500,
22082     
22083     /**
22084      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22085      * 
22086      */
22087     stylesheets: false,
22088     
22089     // id of frame..
22090     frameId: false,
22091     
22092     // private properties
22093     validationEvent : false,
22094     deferHeight: true,
22095     initialized : false,
22096     activated : false,
22097     sourceEditMode : false,
22098     onFocus : Roo.emptyFn,
22099     iframePad:3,
22100     hideMode:'offsets',
22101     
22102     clearUp: true,
22103     
22104     // blacklist + whitelisted elements..
22105     black: false,
22106     white: false,
22107      
22108     bodyCls : '',
22109
22110     /**
22111      * Protected method that will not generally be called directly. It
22112      * is called when the editor initializes the iframe with HTML contents. Override this method if you
22113      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
22114      */
22115     getDocMarkup : function(){
22116         // body styles..
22117         var st = '';
22118         
22119         // inherit styels from page...?? 
22120         if (this.stylesheets === false) {
22121             
22122             Roo.get(document.head).select('style').each(function(node) {
22123                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22124             });
22125             
22126             Roo.get(document.head).select('link').each(function(node) { 
22127                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22128             });
22129             
22130         } else if (!this.stylesheets.length) {
22131                 // simple..
22132                 st = '<style type="text/css">' +
22133                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22134                    '</style>';
22135         } else { 
22136             st = '<style type="text/css">' +
22137                     this.stylesheets +
22138                 '</style>';
22139         }
22140         
22141         st +=  '<style type="text/css">' +
22142             'IMG { cursor: pointer } ' +
22143         '</style>';
22144
22145         var cls = 'roo-htmleditor-body';
22146         
22147         if(this.bodyCls.length){
22148             cls += ' ' + this.bodyCls;
22149         }
22150         
22151         return '<html><head>' + st  +
22152             //<style type="text/css">' +
22153             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22154             //'</style>' +
22155             ' </head><body class="' +  cls + '"></body></html>';
22156     },
22157
22158     // private
22159     onRender : function(ct, position)
22160     {
22161         var _t = this;
22162         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
22163         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
22164         
22165         
22166         this.el.dom.style.border = '0 none';
22167         this.el.dom.setAttribute('tabIndex', -1);
22168         this.el.addClass('x-hidden hide');
22169         
22170         
22171         
22172         if(Roo.isIE){ // fix IE 1px bogus margin
22173             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
22174         }
22175        
22176         
22177         this.frameId = Roo.id();
22178         
22179          
22180         
22181         var iframe = this.owner.wrap.createChild({
22182             tag: 'iframe',
22183             cls: 'form-control', // bootstrap..
22184             id: this.frameId,
22185             name: this.frameId,
22186             frameBorder : 'no',
22187             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
22188         }, this.el
22189         );
22190         
22191         
22192         this.iframe = iframe.dom;
22193
22194          this.assignDocWin();
22195         
22196         this.doc.designMode = 'on';
22197        
22198         this.doc.open();
22199         this.doc.write(this.getDocMarkup());
22200         this.doc.close();
22201
22202         
22203         var task = { // must defer to wait for browser to be ready
22204             run : function(){
22205                 //console.log("run task?" + this.doc.readyState);
22206                 this.assignDocWin();
22207                 if(this.doc.body || this.doc.readyState == 'complete'){
22208                     try {
22209                         this.doc.designMode="on";
22210                     } catch (e) {
22211                         return;
22212                     }
22213                     Roo.TaskMgr.stop(task);
22214                     this.initEditor.defer(10, this);
22215                 }
22216             },
22217             interval : 10,
22218             duration: 10000,
22219             scope: this
22220         };
22221         Roo.TaskMgr.start(task);
22222
22223     },
22224
22225     // private
22226     onResize : function(w, h)
22227     {
22228          Roo.log('resize: ' +w + ',' + h );
22229         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22230         if(!this.iframe){
22231             return;
22232         }
22233         if(typeof w == 'number'){
22234             
22235             this.iframe.style.width = w + 'px';
22236         }
22237         if(typeof h == 'number'){
22238             
22239             this.iframe.style.height = h + 'px';
22240             if(this.doc){
22241                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22242             }
22243         }
22244         
22245     },
22246
22247     /**
22248      * Toggles the editor between standard and source edit mode.
22249      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22250      */
22251     toggleSourceEdit : function(sourceEditMode){
22252         
22253         this.sourceEditMode = sourceEditMode === true;
22254         
22255         if(this.sourceEditMode){
22256  
22257             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
22258             
22259         }else{
22260             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22261             //this.iframe.className = '';
22262             this.deferFocus();
22263         }
22264         //this.setSize(this.owner.wrap.getSize());
22265         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22266     },
22267
22268     
22269   
22270
22271     /**
22272      * Protected method that will not generally be called directly. If you need/want
22273      * custom HTML cleanup, this is the method you should override.
22274      * @param {String} html The HTML to be cleaned
22275      * return {String} The cleaned HTML
22276      */
22277     cleanHtml : function(html){
22278         html = String(html);
22279         if(html.length > 5){
22280             if(Roo.isSafari){ // strip safari nonsense
22281                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22282             }
22283         }
22284         if(html == '&nbsp;'){
22285             html = '';
22286         }
22287         return html;
22288     },
22289
22290     /**
22291      * HTML Editor -> Textarea
22292      * Protected method that will not generally be called directly. Syncs the contents
22293      * of the editor iframe with the textarea.
22294      */
22295     syncValue : function(){
22296         if(this.initialized){
22297             var bd = (this.doc.body || this.doc.documentElement);
22298             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22299             var html = bd.innerHTML;
22300             if(Roo.isSafari){
22301                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22302                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22303                 if(m && m[1]){
22304                     html = '<div style="'+m[0]+'">' + html + '</div>';
22305                 }
22306             }
22307             html = this.cleanHtml(html);
22308             // fix up the special chars.. normaly like back quotes in word...
22309             // however we do not want to do this with chinese..
22310             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22311                 var cc = b.charCodeAt();
22312                 if (
22313                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22314                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22315                     (cc >= 0xf900 && cc < 0xfb00 )
22316                 ) {
22317                         return b;
22318                 }
22319                 return "&#"+cc+";" 
22320             });
22321             if(this.owner.fireEvent('beforesync', this, html) !== false){
22322                 this.el.dom.value = html;
22323                 this.owner.fireEvent('sync', this, html);
22324             }
22325         }
22326     },
22327
22328     /**
22329      * Protected method that will not generally be called directly. Pushes the value of the textarea
22330      * into the iframe editor.
22331      */
22332     pushValue : function(){
22333         if(this.initialized){
22334             var v = this.el.dom.value.trim();
22335             
22336 //            if(v.length < 1){
22337 //                v = '&#160;';
22338 //            }
22339             
22340             if(this.owner.fireEvent('beforepush', this, v) !== false){
22341                 var d = (this.doc.body || this.doc.documentElement);
22342                 d.innerHTML = v;
22343                 this.cleanUpPaste();
22344                 this.el.dom.value = d.innerHTML;
22345                 this.owner.fireEvent('push', this, v);
22346             }
22347         }
22348     },
22349
22350     // private
22351     deferFocus : function(){
22352         this.focus.defer(10, this);
22353     },
22354
22355     // doc'ed in Field
22356     focus : function(){
22357         if(this.win && !this.sourceEditMode){
22358             this.win.focus();
22359         }else{
22360             this.el.focus();
22361         }
22362     },
22363     
22364     assignDocWin: function()
22365     {
22366         var iframe = this.iframe;
22367         
22368          if(Roo.isIE){
22369             this.doc = iframe.contentWindow.document;
22370             this.win = iframe.contentWindow;
22371         } else {
22372 //            if (!Roo.get(this.frameId)) {
22373 //                return;
22374 //            }
22375 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22376 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22377             
22378             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22379                 return;
22380             }
22381             
22382             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22383             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22384         }
22385     },
22386     
22387     // private
22388     initEditor : function(){
22389         //console.log("INIT EDITOR");
22390         this.assignDocWin();
22391         
22392         
22393         
22394         this.doc.designMode="on";
22395         this.doc.open();
22396         this.doc.write(this.getDocMarkup());
22397         this.doc.close();
22398         
22399         var dbody = (this.doc.body || this.doc.documentElement);
22400         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22401         // this copies styles from the containing element into thsi one..
22402         // not sure why we need all of this..
22403         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22404         
22405         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22406         //ss['background-attachment'] = 'fixed'; // w3c
22407         dbody.bgProperties = 'fixed'; // ie
22408         //Roo.DomHelper.applyStyles(dbody, ss);
22409         Roo.EventManager.on(this.doc, {
22410             //'mousedown': this.onEditorEvent,
22411             'mouseup': this.onEditorEvent,
22412             'dblclick': this.onEditorEvent,
22413             'click': this.onEditorEvent,
22414             'keyup': this.onEditorEvent,
22415             buffer:100,
22416             scope: this
22417         });
22418         if(Roo.isGecko){
22419             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22420         }
22421         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22422             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22423         }
22424         this.initialized = true;
22425
22426         this.owner.fireEvent('initialize', this);
22427         this.pushValue();
22428     },
22429
22430     // private
22431     onDestroy : function(){
22432         
22433         
22434         
22435         if(this.rendered){
22436             
22437             //for (var i =0; i < this.toolbars.length;i++) {
22438             //    // fixme - ask toolbars for heights?
22439             //    this.toolbars[i].onDestroy();
22440            // }
22441             
22442             //this.wrap.dom.innerHTML = '';
22443             //this.wrap.remove();
22444         }
22445     },
22446
22447     // private
22448     onFirstFocus : function(){
22449         
22450         this.assignDocWin();
22451         
22452         
22453         this.activated = true;
22454          
22455     
22456         if(Roo.isGecko){ // prevent silly gecko errors
22457             this.win.focus();
22458             var s = this.win.getSelection();
22459             if(!s.focusNode || s.focusNode.nodeType != 3){
22460                 var r = s.getRangeAt(0);
22461                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22462                 r.collapse(true);
22463                 this.deferFocus();
22464             }
22465             try{
22466                 this.execCmd('useCSS', true);
22467                 this.execCmd('styleWithCSS', false);
22468             }catch(e){}
22469         }
22470         this.owner.fireEvent('activate', this);
22471     },
22472
22473     // private
22474     adjustFont: function(btn){
22475         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22476         //if(Roo.isSafari){ // safari
22477         //    adjust *= 2;
22478        // }
22479         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22480         if(Roo.isSafari){ // safari
22481             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22482             v =  (v < 10) ? 10 : v;
22483             v =  (v > 48) ? 48 : v;
22484             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22485             
22486         }
22487         
22488         
22489         v = Math.max(1, v+adjust);
22490         
22491         this.execCmd('FontSize', v  );
22492     },
22493
22494     onEditorEvent : function(e)
22495     {
22496         this.owner.fireEvent('editorevent', this, e);
22497       //  this.updateToolbar();
22498         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22499     },
22500
22501     insertTag : function(tg)
22502     {
22503         // could be a bit smarter... -> wrap the current selected tRoo..
22504         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22505             
22506             range = this.createRange(this.getSelection());
22507             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22508             wrappingNode.appendChild(range.extractContents());
22509             range.insertNode(wrappingNode);
22510
22511             return;
22512             
22513             
22514             
22515         }
22516         this.execCmd("formatblock",   tg);
22517         
22518     },
22519     
22520     insertText : function(txt)
22521     {
22522         
22523         
22524         var range = this.createRange();
22525         range.deleteContents();
22526                //alert(Sender.getAttribute('label'));
22527                
22528         range.insertNode(this.doc.createTextNode(txt));
22529     } ,
22530     
22531      
22532
22533     /**
22534      * Executes a Midas editor command on the editor document and performs necessary focus and
22535      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22536      * @param {String} cmd The Midas command
22537      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22538      */
22539     relayCmd : function(cmd, value){
22540         this.win.focus();
22541         this.execCmd(cmd, value);
22542         this.owner.fireEvent('editorevent', this);
22543         //this.updateToolbar();
22544         this.owner.deferFocus();
22545     },
22546
22547     /**
22548      * Executes a Midas editor command directly on the editor document.
22549      * For visual commands, you should use {@link #relayCmd} instead.
22550      * <b>This should only be called after the editor is initialized.</b>
22551      * @param {String} cmd The Midas command
22552      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22553      */
22554     execCmd : function(cmd, value){
22555         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22556         this.syncValue();
22557     },
22558  
22559  
22560    
22561     /**
22562      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22563      * to insert tRoo.
22564      * @param {String} text | dom node.. 
22565      */
22566     insertAtCursor : function(text)
22567     {
22568         
22569         if(!this.activated){
22570             return;
22571         }
22572         /*
22573         if(Roo.isIE){
22574             this.win.focus();
22575             var r = this.doc.selection.createRange();
22576             if(r){
22577                 r.collapse(true);
22578                 r.pasteHTML(text);
22579                 this.syncValue();
22580                 this.deferFocus();
22581             
22582             }
22583             return;
22584         }
22585         */
22586         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22587             this.win.focus();
22588             
22589             
22590             // from jquery ui (MIT licenced)
22591             var range, node;
22592             var win = this.win;
22593             
22594             if (win.getSelection && win.getSelection().getRangeAt) {
22595                 range = win.getSelection().getRangeAt(0);
22596                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22597                 range.insertNode(node);
22598             } else if (win.document.selection && win.document.selection.createRange) {
22599                 // no firefox support
22600                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22601                 win.document.selection.createRange().pasteHTML(txt);
22602             } else {
22603                 // no firefox support
22604                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22605                 this.execCmd('InsertHTML', txt);
22606             } 
22607             
22608             this.syncValue();
22609             
22610             this.deferFocus();
22611         }
22612     },
22613  // private
22614     mozKeyPress : function(e){
22615         if(e.ctrlKey){
22616             var c = e.getCharCode(), cmd;
22617           
22618             if(c > 0){
22619                 c = String.fromCharCode(c).toLowerCase();
22620                 switch(c){
22621                     case 'b':
22622                         cmd = 'bold';
22623                         break;
22624                     case 'i':
22625                         cmd = 'italic';
22626                         break;
22627                     
22628                     case 'u':
22629                         cmd = 'underline';
22630                         break;
22631                     
22632                     case 'v':
22633                         this.cleanUpPaste.defer(100, this);
22634                         return;
22635                         
22636                 }
22637                 if(cmd){
22638                     this.win.focus();
22639                     this.execCmd(cmd);
22640                     this.deferFocus();
22641                     e.preventDefault();
22642                 }
22643                 
22644             }
22645         }
22646     },
22647
22648     // private
22649     fixKeys : function(){ // load time branching for fastest keydown performance
22650         if(Roo.isIE){
22651             return function(e){
22652                 var k = e.getKey(), r;
22653                 if(k == e.TAB){
22654                     e.stopEvent();
22655                     r = this.doc.selection.createRange();
22656                     if(r){
22657                         r.collapse(true);
22658                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22659                         this.deferFocus();
22660                     }
22661                     return;
22662                 }
22663                 
22664                 if(k == e.ENTER){
22665                     r = this.doc.selection.createRange();
22666                     if(r){
22667                         var target = r.parentElement();
22668                         if(!target || target.tagName.toLowerCase() != 'li'){
22669                             e.stopEvent();
22670                             r.pasteHTML('<br />');
22671                             r.collapse(false);
22672                             r.select();
22673                         }
22674                     }
22675                 }
22676                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22677                     this.cleanUpPaste.defer(100, this);
22678                     return;
22679                 }
22680                 
22681                 
22682             };
22683         }else if(Roo.isOpera){
22684             return function(e){
22685                 var k = e.getKey();
22686                 if(k == e.TAB){
22687                     e.stopEvent();
22688                     this.win.focus();
22689                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22690                     this.deferFocus();
22691                 }
22692                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22693                     this.cleanUpPaste.defer(100, this);
22694                     return;
22695                 }
22696                 
22697             };
22698         }else if(Roo.isSafari){
22699             return function(e){
22700                 var k = e.getKey();
22701                 
22702                 if(k == e.TAB){
22703                     e.stopEvent();
22704                     this.execCmd('InsertText','\t');
22705                     this.deferFocus();
22706                     return;
22707                 }
22708                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22709                     this.cleanUpPaste.defer(100, this);
22710                     return;
22711                 }
22712                 
22713              };
22714         }
22715     }(),
22716     
22717     getAllAncestors: function()
22718     {
22719         var p = this.getSelectedNode();
22720         var a = [];
22721         if (!p) {
22722             a.push(p); // push blank onto stack..
22723             p = this.getParentElement();
22724         }
22725         
22726         
22727         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22728             a.push(p);
22729             p = p.parentNode;
22730         }
22731         a.push(this.doc.body);
22732         return a;
22733     },
22734     lastSel : false,
22735     lastSelNode : false,
22736     
22737     
22738     getSelection : function() 
22739     {
22740         this.assignDocWin();
22741         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22742     },
22743     
22744     getSelectedNode: function() 
22745     {
22746         // this may only work on Gecko!!!
22747         
22748         // should we cache this!!!!
22749         
22750         
22751         
22752          
22753         var range = this.createRange(this.getSelection()).cloneRange();
22754         
22755         if (Roo.isIE) {
22756             var parent = range.parentElement();
22757             while (true) {
22758                 var testRange = range.duplicate();
22759                 testRange.moveToElementText(parent);
22760                 if (testRange.inRange(range)) {
22761                     break;
22762                 }
22763                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22764                     break;
22765                 }
22766                 parent = parent.parentElement;
22767             }
22768             return parent;
22769         }
22770         
22771         // is ancestor a text element.
22772         var ac =  range.commonAncestorContainer;
22773         if (ac.nodeType == 3) {
22774             ac = ac.parentNode;
22775         }
22776         
22777         var ar = ac.childNodes;
22778          
22779         var nodes = [];
22780         var other_nodes = [];
22781         var has_other_nodes = false;
22782         for (var i=0;i<ar.length;i++) {
22783             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22784                 continue;
22785             }
22786             // fullly contained node.
22787             
22788             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22789                 nodes.push(ar[i]);
22790                 continue;
22791             }
22792             
22793             // probably selected..
22794             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22795                 other_nodes.push(ar[i]);
22796                 continue;
22797             }
22798             // outer..
22799             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22800                 continue;
22801             }
22802             
22803             
22804             has_other_nodes = true;
22805         }
22806         if (!nodes.length && other_nodes.length) {
22807             nodes= other_nodes;
22808         }
22809         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22810             return false;
22811         }
22812         
22813         return nodes[0];
22814     },
22815     createRange: function(sel)
22816     {
22817         // this has strange effects when using with 
22818         // top toolbar - not sure if it's a great idea.
22819         //this.editor.contentWindow.focus();
22820         if (typeof sel != "undefined") {
22821             try {
22822                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22823             } catch(e) {
22824                 return this.doc.createRange();
22825             }
22826         } else {
22827             return this.doc.createRange();
22828         }
22829     },
22830     getParentElement: function()
22831     {
22832         
22833         this.assignDocWin();
22834         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22835         
22836         var range = this.createRange(sel);
22837          
22838         try {
22839             var p = range.commonAncestorContainer;
22840             while (p.nodeType == 3) { // text node
22841                 p = p.parentNode;
22842             }
22843             return p;
22844         } catch (e) {
22845             return null;
22846         }
22847     
22848     },
22849     /***
22850      *
22851      * Range intersection.. the hard stuff...
22852      *  '-1' = before
22853      *  '0' = hits..
22854      *  '1' = after.
22855      *         [ -- selected range --- ]
22856      *   [fail]                        [fail]
22857      *
22858      *    basically..
22859      *      if end is before start or  hits it. fail.
22860      *      if start is after end or hits it fail.
22861      *
22862      *   if either hits (but other is outside. - then it's not 
22863      *   
22864      *    
22865      **/
22866     
22867     
22868     // @see http://www.thismuchiknow.co.uk/?p=64.
22869     rangeIntersectsNode : function(range, node)
22870     {
22871         var nodeRange = node.ownerDocument.createRange();
22872         try {
22873             nodeRange.selectNode(node);
22874         } catch (e) {
22875             nodeRange.selectNodeContents(node);
22876         }
22877     
22878         var rangeStartRange = range.cloneRange();
22879         rangeStartRange.collapse(true);
22880     
22881         var rangeEndRange = range.cloneRange();
22882         rangeEndRange.collapse(false);
22883     
22884         var nodeStartRange = nodeRange.cloneRange();
22885         nodeStartRange.collapse(true);
22886     
22887         var nodeEndRange = nodeRange.cloneRange();
22888         nodeEndRange.collapse(false);
22889     
22890         return rangeStartRange.compareBoundaryPoints(
22891                  Range.START_TO_START, nodeEndRange) == -1 &&
22892                rangeEndRange.compareBoundaryPoints(
22893                  Range.START_TO_START, nodeStartRange) == 1;
22894         
22895          
22896     },
22897     rangeCompareNode : function(range, node)
22898     {
22899         var nodeRange = node.ownerDocument.createRange();
22900         try {
22901             nodeRange.selectNode(node);
22902         } catch (e) {
22903             nodeRange.selectNodeContents(node);
22904         }
22905         
22906         
22907         range.collapse(true);
22908     
22909         nodeRange.collapse(true);
22910      
22911         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22912         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22913          
22914         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22915         
22916         var nodeIsBefore   =  ss == 1;
22917         var nodeIsAfter    = ee == -1;
22918         
22919         if (nodeIsBefore && nodeIsAfter) {
22920             return 0; // outer
22921         }
22922         if (!nodeIsBefore && nodeIsAfter) {
22923             return 1; //right trailed.
22924         }
22925         
22926         if (nodeIsBefore && !nodeIsAfter) {
22927             return 2;  // left trailed.
22928         }
22929         // fully contined.
22930         return 3;
22931     },
22932
22933     // private? - in a new class?
22934     cleanUpPaste :  function()
22935     {
22936         // cleans up the whole document..
22937         Roo.log('cleanuppaste');
22938         
22939         this.cleanUpChildren(this.doc.body);
22940         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22941         if (clean != this.doc.body.innerHTML) {
22942             this.doc.body.innerHTML = clean;
22943         }
22944         
22945     },
22946     
22947     cleanWordChars : function(input) {// change the chars to hex code
22948         var he = Roo.HtmlEditorCore;
22949         
22950         var output = input;
22951         Roo.each(he.swapCodes, function(sw) { 
22952             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22953             
22954             output = output.replace(swapper, sw[1]);
22955         });
22956         
22957         return output;
22958     },
22959     
22960     
22961     cleanUpChildren : function (n)
22962     {
22963         if (!n.childNodes.length) {
22964             return;
22965         }
22966         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22967            this.cleanUpChild(n.childNodes[i]);
22968         }
22969     },
22970     
22971     
22972         
22973     
22974     cleanUpChild : function (node)
22975     {
22976         var ed = this;
22977         //console.log(node);
22978         if (node.nodeName == "#text") {
22979             // clean up silly Windows -- stuff?
22980             return; 
22981         }
22982         if (node.nodeName == "#comment") {
22983             node.parentNode.removeChild(node);
22984             // clean up silly Windows -- stuff?
22985             return; 
22986         }
22987         var lcname = node.tagName.toLowerCase();
22988         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22989         // whitelist of tags..
22990         
22991         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22992             // remove node.
22993             node.parentNode.removeChild(node);
22994             return;
22995             
22996         }
22997         
22998         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22999         
23000         // remove <a name=....> as rendering on yahoo mailer is borked with this.
23001         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
23002         
23003         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
23004         //    remove_keep_children = true;
23005         //}
23006         
23007         if (remove_keep_children) {
23008             this.cleanUpChildren(node);
23009             // inserts everything just before this node...
23010             while (node.childNodes.length) {
23011                 var cn = node.childNodes[0];
23012                 node.removeChild(cn);
23013                 node.parentNode.insertBefore(cn, node);
23014             }
23015             node.parentNode.removeChild(node);
23016             return;
23017         }
23018         
23019         if (!node.attributes || !node.attributes.length) {
23020             this.cleanUpChildren(node);
23021             return;
23022         }
23023         
23024         function cleanAttr(n,v)
23025         {
23026             
23027             if (v.match(/^\./) || v.match(/^\//)) {
23028                 return;
23029             }
23030             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
23031                 return;
23032             }
23033             if (v.match(/^#/)) {
23034                 return;
23035             }
23036 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
23037             node.removeAttribute(n);
23038             
23039         }
23040         
23041         var cwhite = this.cwhite;
23042         var cblack = this.cblack;
23043             
23044         function cleanStyle(n,v)
23045         {
23046             if (v.match(/expression/)) { //XSS?? should we even bother..
23047                 node.removeAttribute(n);
23048                 return;
23049             }
23050             
23051             var parts = v.split(/;/);
23052             var clean = [];
23053             
23054             Roo.each(parts, function(p) {
23055                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
23056                 if (!p.length) {
23057                     return true;
23058                 }
23059                 var l = p.split(':').shift().replace(/\s+/g,'');
23060                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
23061                 
23062                 if ( cwhite.length && cblack.indexOf(l) > -1) {
23063 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23064                     //node.removeAttribute(n);
23065                     return true;
23066                 }
23067                 //Roo.log()
23068                 // only allow 'c whitelisted system attributes'
23069                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
23070 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23071                     //node.removeAttribute(n);
23072                     return true;
23073                 }
23074                 
23075                 
23076                  
23077                 
23078                 clean.push(p);
23079                 return true;
23080             });
23081             if (clean.length) { 
23082                 node.setAttribute(n, clean.join(';'));
23083             } else {
23084                 node.removeAttribute(n);
23085             }
23086             
23087         }
23088         
23089         
23090         for (var i = node.attributes.length-1; i > -1 ; i--) {
23091             var a = node.attributes[i];
23092             //console.log(a);
23093             
23094             if (a.name.toLowerCase().substr(0,2)=='on')  {
23095                 node.removeAttribute(a.name);
23096                 continue;
23097             }
23098             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
23099                 node.removeAttribute(a.name);
23100                 continue;
23101             }
23102             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
23103                 cleanAttr(a.name,a.value); // fixme..
23104                 continue;
23105             }
23106             if (a.name == 'style') {
23107                 cleanStyle(a.name,a.value);
23108                 continue;
23109             }
23110             /// clean up MS crap..
23111             // tecnically this should be a list of valid class'es..
23112             
23113             
23114             if (a.name == 'class') {
23115                 if (a.value.match(/^Mso/)) {
23116                     node.className = '';
23117                 }
23118                 
23119                 if (a.value.match(/^body$/)) {
23120                     node.className = '';
23121                 }
23122                 continue;
23123             }
23124             
23125             // style cleanup!?
23126             // class cleanup?
23127             
23128         }
23129         
23130         
23131         this.cleanUpChildren(node);
23132         
23133         
23134     },
23135     
23136     /**
23137      * Clean up MS wordisms...
23138      */
23139     cleanWord : function(node)
23140     {
23141         
23142         
23143         if (!node) {
23144             this.cleanWord(this.doc.body);
23145             return;
23146         }
23147         if (node.nodeName == "#text") {
23148             // clean up silly Windows -- stuff?
23149             return; 
23150         }
23151         if (node.nodeName == "#comment") {
23152             node.parentNode.removeChild(node);
23153             // clean up silly Windows -- stuff?
23154             return; 
23155         }
23156         
23157         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
23158             node.parentNode.removeChild(node);
23159             return;
23160         }
23161         
23162         // remove - but keep children..
23163         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
23164             while (node.childNodes.length) {
23165                 var cn = node.childNodes[0];
23166                 node.removeChild(cn);
23167                 node.parentNode.insertBefore(cn, node);
23168             }
23169             node.parentNode.removeChild(node);
23170             this.iterateChildren(node, this.cleanWord);
23171             return;
23172         }
23173         // clean styles
23174         if (node.className.length) {
23175             
23176             var cn = node.className.split(/\W+/);
23177             var cna = [];
23178             Roo.each(cn, function(cls) {
23179                 if (cls.match(/Mso[a-zA-Z]+/)) {
23180                     return;
23181                 }
23182                 cna.push(cls);
23183             });
23184             node.className = cna.length ? cna.join(' ') : '';
23185             if (!cna.length) {
23186                 node.removeAttribute("class");
23187             }
23188         }
23189         
23190         if (node.hasAttribute("lang")) {
23191             node.removeAttribute("lang");
23192         }
23193         
23194         if (node.hasAttribute("style")) {
23195             
23196             var styles = node.getAttribute("style").split(";");
23197             var nstyle = [];
23198             Roo.each(styles, function(s) {
23199                 if (!s.match(/:/)) {
23200                     return;
23201                 }
23202                 var kv = s.split(":");
23203                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
23204                     return;
23205                 }
23206                 // what ever is left... we allow.
23207                 nstyle.push(s);
23208             });
23209             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23210             if (!nstyle.length) {
23211                 node.removeAttribute('style');
23212             }
23213         }
23214         this.iterateChildren(node, this.cleanWord);
23215         
23216         
23217         
23218     },
23219     /**
23220      * iterateChildren of a Node, calling fn each time, using this as the scole..
23221      * @param {DomNode} node node to iterate children of.
23222      * @param {Function} fn method of this class to call on each item.
23223      */
23224     iterateChildren : function(node, fn)
23225     {
23226         if (!node.childNodes.length) {
23227                 return;
23228         }
23229         for (var i = node.childNodes.length-1; i > -1 ; i--) {
23230            fn.call(this, node.childNodes[i])
23231         }
23232     },
23233     
23234     
23235     /**
23236      * cleanTableWidths.
23237      *
23238      * Quite often pasting from word etc.. results in tables with column and widths.
23239      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23240      *
23241      */
23242     cleanTableWidths : function(node)
23243     {
23244          
23245          
23246         if (!node) {
23247             this.cleanTableWidths(this.doc.body);
23248             return;
23249         }
23250         
23251         // ignore list...
23252         if (node.nodeName == "#text" || node.nodeName == "#comment") {
23253             return; 
23254         }
23255         Roo.log(node.tagName);
23256         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23257             this.iterateChildren(node, this.cleanTableWidths);
23258             return;
23259         }
23260         if (node.hasAttribute('width')) {
23261             node.removeAttribute('width');
23262         }
23263         
23264          
23265         if (node.hasAttribute("style")) {
23266             // pretty basic...
23267             
23268             var styles = node.getAttribute("style").split(";");
23269             var nstyle = [];
23270             Roo.each(styles, function(s) {
23271                 if (!s.match(/:/)) {
23272                     return;
23273                 }
23274                 var kv = s.split(":");
23275                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23276                     return;
23277                 }
23278                 // what ever is left... we allow.
23279                 nstyle.push(s);
23280             });
23281             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23282             if (!nstyle.length) {
23283                 node.removeAttribute('style');
23284             }
23285         }
23286         
23287         this.iterateChildren(node, this.cleanTableWidths);
23288         
23289         
23290     },
23291     
23292     
23293     
23294     
23295     domToHTML : function(currentElement, depth, nopadtext) {
23296         
23297         depth = depth || 0;
23298         nopadtext = nopadtext || false;
23299     
23300         if (!currentElement) {
23301             return this.domToHTML(this.doc.body);
23302         }
23303         
23304         //Roo.log(currentElement);
23305         var j;
23306         var allText = false;
23307         var nodeName = currentElement.nodeName;
23308         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23309         
23310         if  (nodeName == '#text') {
23311             
23312             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23313         }
23314         
23315         
23316         var ret = '';
23317         if (nodeName != 'BODY') {
23318              
23319             var i = 0;
23320             // Prints the node tagName, such as <A>, <IMG>, etc
23321             if (tagName) {
23322                 var attr = [];
23323                 for(i = 0; i < currentElement.attributes.length;i++) {
23324                     // quoting?
23325                     var aname = currentElement.attributes.item(i).name;
23326                     if (!currentElement.attributes.item(i).value.length) {
23327                         continue;
23328                     }
23329                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23330                 }
23331                 
23332                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23333             } 
23334             else {
23335                 
23336                 // eack
23337             }
23338         } else {
23339             tagName = false;
23340         }
23341         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23342             return ret;
23343         }
23344         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23345             nopadtext = true;
23346         }
23347         
23348         
23349         // Traverse the tree
23350         i = 0;
23351         var currentElementChild = currentElement.childNodes.item(i);
23352         var allText = true;
23353         var innerHTML  = '';
23354         lastnode = '';
23355         while (currentElementChild) {
23356             // Formatting code (indent the tree so it looks nice on the screen)
23357             var nopad = nopadtext;
23358             if (lastnode == 'SPAN') {
23359                 nopad  = true;
23360             }
23361             // text
23362             if  (currentElementChild.nodeName == '#text') {
23363                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23364                 toadd = nopadtext ? toadd : toadd.trim();
23365                 if (!nopad && toadd.length > 80) {
23366                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23367                 }
23368                 innerHTML  += toadd;
23369                 
23370                 i++;
23371                 currentElementChild = currentElement.childNodes.item(i);
23372                 lastNode = '';
23373                 continue;
23374             }
23375             allText = false;
23376             
23377             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23378                 
23379             // Recursively traverse the tree structure of the child node
23380             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23381             lastnode = currentElementChild.nodeName;
23382             i++;
23383             currentElementChild=currentElement.childNodes.item(i);
23384         }
23385         
23386         ret += innerHTML;
23387         
23388         if (!allText) {
23389                 // The remaining code is mostly for formatting the tree
23390             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23391         }
23392         
23393         
23394         if (tagName) {
23395             ret+= "</"+tagName+">";
23396         }
23397         return ret;
23398         
23399     },
23400         
23401     applyBlacklists : function()
23402     {
23403         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23404         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23405         
23406         this.white = [];
23407         this.black = [];
23408         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23409             if (b.indexOf(tag) > -1) {
23410                 return;
23411             }
23412             this.white.push(tag);
23413             
23414         }, this);
23415         
23416         Roo.each(w, function(tag) {
23417             if (b.indexOf(tag) > -1) {
23418                 return;
23419             }
23420             if (this.white.indexOf(tag) > -1) {
23421                 return;
23422             }
23423             this.white.push(tag);
23424             
23425         }, this);
23426         
23427         
23428         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23429             if (w.indexOf(tag) > -1) {
23430                 return;
23431             }
23432             this.black.push(tag);
23433             
23434         }, this);
23435         
23436         Roo.each(b, function(tag) {
23437             if (w.indexOf(tag) > -1) {
23438                 return;
23439             }
23440             if (this.black.indexOf(tag) > -1) {
23441                 return;
23442             }
23443             this.black.push(tag);
23444             
23445         }, this);
23446         
23447         
23448         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23449         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23450         
23451         this.cwhite = [];
23452         this.cblack = [];
23453         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23454             if (b.indexOf(tag) > -1) {
23455                 return;
23456             }
23457             this.cwhite.push(tag);
23458             
23459         }, this);
23460         
23461         Roo.each(w, function(tag) {
23462             if (b.indexOf(tag) > -1) {
23463                 return;
23464             }
23465             if (this.cwhite.indexOf(tag) > -1) {
23466                 return;
23467             }
23468             this.cwhite.push(tag);
23469             
23470         }, this);
23471         
23472         
23473         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23474             if (w.indexOf(tag) > -1) {
23475                 return;
23476             }
23477             this.cblack.push(tag);
23478             
23479         }, this);
23480         
23481         Roo.each(b, function(tag) {
23482             if (w.indexOf(tag) > -1) {
23483                 return;
23484             }
23485             if (this.cblack.indexOf(tag) > -1) {
23486                 return;
23487             }
23488             this.cblack.push(tag);
23489             
23490         }, this);
23491     },
23492     
23493     setStylesheets : function(stylesheets)
23494     {
23495         if(typeof(stylesheets) == 'string'){
23496             Roo.get(this.iframe.contentDocument.head).createChild({
23497                 tag : 'link',
23498                 rel : 'stylesheet',
23499                 type : 'text/css',
23500                 href : stylesheets
23501             });
23502             
23503             return;
23504         }
23505         var _this = this;
23506      
23507         Roo.each(stylesheets, function(s) {
23508             if(!s.length){
23509                 return;
23510             }
23511             
23512             Roo.get(_this.iframe.contentDocument.head).createChild({
23513                 tag : 'link',
23514                 rel : 'stylesheet',
23515                 type : 'text/css',
23516                 href : s
23517             });
23518         });
23519
23520         
23521     },
23522     
23523     removeStylesheets : function()
23524     {
23525         var _this = this;
23526         
23527         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23528             s.remove();
23529         });
23530     },
23531     
23532     setStyle : function(style)
23533     {
23534         Roo.get(this.iframe.contentDocument.head).createChild({
23535             tag : 'style',
23536             type : 'text/css',
23537             html : style
23538         });
23539
23540         return;
23541     }
23542     
23543     // hide stuff that is not compatible
23544     /**
23545      * @event blur
23546      * @hide
23547      */
23548     /**
23549      * @event change
23550      * @hide
23551      */
23552     /**
23553      * @event focus
23554      * @hide
23555      */
23556     /**
23557      * @event specialkey
23558      * @hide
23559      */
23560     /**
23561      * @cfg {String} fieldClass @hide
23562      */
23563     /**
23564      * @cfg {String} focusClass @hide
23565      */
23566     /**
23567      * @cfg {String} autoCreate @hide
23568      */
23569     /**
23570      * @cfg {String} inputType @hide
23571      */
23572     /**
23573      * @cfg {String} invalidClass @hide
23574      */
23575     /**
23576      * @cfg {String} invalidText @hide
23577      */
23578     /**
23579      * @cfg {String} msgFx @hide
23580      */
23581     /**
23582      * @cfg {String} validateOnBlur @hide
23583      */
23584 });
23585
23586 Roo.HtmlEditorCore.white = [
23587         'area', 'br', 'img', 'input', 'hr', 'wbr',
23588         
23589        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23590        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23591        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23592        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23593        'table',   'ul',         'xmp', 
23594        
23595        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23596       'thead',   'tr', 
23597      
23598       'dir', 'menu', 'ol', 'ul', 'dl',
23599        
23600       'embed',  'object'
23601 ];
23602
23603
23604 Roo.HtmlEditorCore.black = [
23605     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23606         'applet', // 
23607         'base',   'basefont', 'bgsound', 'blink',  'body', 
23608         'frame',  'frameset', 'head',    'html',   'ilayer', 
23609         'iframe', 'layer',  'link',     'meta',    'object',   
23610         'script', 'style' ,'title',  'xml' // clean later..
23611 ];
23612 Roo.HtmlEditorCore.clean = [
23613     'script', 'style', 'title', 'xml'
23614 ];
23615 Roo.HtmlEditorCore.remove = [
23616     'font'
23617 ];
23618 // attributes..
23619
23620 Roo.HtmlEditorCore.ablack = [
23621     'on'
23622 ];
23623     
23624 Roo.HtmlEditorCore.aclean = [ 
23625     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23626 ];
23627
23628 // protocols..
23629 Roo.HtmlEditorCore.pwhite= [
23630         'http',  'https',  'mailto'
23631 ];
23632
23633 // white listed style attributes.
23634 Roo.HtmlEditorCore.cwhite= [
23635       //  'text-align', /// default is to allow most things..
23636       
23637          
23638 //        'font-size'//??
23639 ];
23640
23641 // black listed style attributes.
23642 Roo.HtmlEditorCore.cblack= [
23643       //  'font-size' -- this can be set by the project 
23644 ];
23645
23646
23647 Roo.HtmlEditorCore.swapCodes   =[ 
23648     [    8211, "--" ], 
23649     [    8212, "--" ], 
23650     [    8216,  "'" ],  
23651     [    8217, "'" ],  
23652     [    8220, '"' ],  
23653     [    8221, '"' ],  
23654     [    8226, "*" ],  
23655     [    8230, "..." ]
23656 ]; 
23657
23658     /*
23659  * - LGPL
23660  *
23661  * HtmlEditor
23662  * 
23663  */
23664
23665 /**
23666  * @class Roo.bootstrap.HtmlEditor
23667  * @extends Roo.bootstrap.TextArea
23668  * Bootstrap HtmlEditor class
23669
23670  * @constructor
23671  * Create a new HtmlEditor
23672  * @param {Object} config The config object
23673  */
23674
23675 Roo.bootstrap.HtmlEditor = function(config){
23676     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23677     if (!this.toolbars) {
23678         this.toolbars = [];
23679     }
23680     
23681     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23682     this.addEvents({
23683             /**
23684              * @event initialize
23685              * Fires when the editor is fully initialized (including the iframe)
23686              * @param {HtmlEditor} this
23687              */
23688             initialize: true,
23689             /**
23690              * @event activate
23691              * Fires when the editor is first receives the focus. Any insertion must wait
23692              * until after this event.
23693              * @param {HtmlEditor} this
23694              */
23695             activate: true,
23696              /**
23697              * @event beforesync
23698              * Fires before the textarea is updated with content from the editor iframe. Return false
23699              * to cancel the sync.
23700              * @param {HtmlEditor} this
23701              * @param {String} html
23702              */
23703             beforesync: true,
23704              /**
23705              * @event beforepush
23706              * Fires before the iframe editor is updated with content from the textarea. Return false
23707              * to cancel the push.
23708              * @param {HtmlEditor} this
23709              * @param {String} html
23710              */
23711             beforepush: true,
23712              /**
23713              * @event sync
23714              * Fires when the textarea is updated with content from the editor iframe.
23715              * @param {HtmlEditor} this
23716              * @param {String} html
23717              */
23718             sync: true,
23719              /**
23720              * @event push
23721              * Fires when the iframe editor is updated with content from the textarea.
23722              * @param {HtmlEditor} this
23723              * @param {String} html
23724              */
23725             push: true,
23726              /**
23727              * @event editmodechange
23728              * Fires when the editor switches edit modes
23729              * @param {HtmlEditor} this
23730              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23731              */
23732             editmodechange: true,
23733             /**
23734              * @event editorevent
23735              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23736              * @param {HtmlEditor} this
23737              */
23738             editorevent: true,
23739             /**
23740              * @event firstfocus
23741              * Fires when on first focus - needed by toolbars..
23742              * @param {HtmlEditor} this
23743              */
23744             firstfocus: true,
23745             /**
23746              * @event autosave
23747              * Auto save the htmlEditor value as a file into Events
23748              * @param {HtmlEditor} this
23749              */
23750             autosave: true,
23751             /**
23752              * @event savedpreview
23753              * preview the saved version of htmlEditor
23754              * @param {HtmlEditor} this
23755              */
23756             savedpreview: true
23757         });
23758 };
23759
23760
23761 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23762     
23763     
23764       /**
23765      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23766      */
23767     toolbars : false,
23768     
23769      /**
23770     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23771     */
23772     btns : [],
23773    
23774      /**
23775      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23776      *                        Roo.resizable.
23777      */
23778     resizable : false,
23779      /**
23780      * @cfg {Number} height (in pixels)
23781      */   
23782     height: 300,
23783    /**
23784      * @cfg {Number} width (in pixels)
23785      */   
23786     width: false,
23787     
23788     /**
23789      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23790      * 
23791      */
23792     stylesheets: false,
23793     
23794     // id of frame..
23795     frameId: false,
23796     
23797     // private properties
23798     validationEvent : false,
23799     deferHeight: true,
23800     initialized : false,
23801     activated : false,
23802     
23803     onFocus : Roo.emptyFn,
23804     iframePad:3,
23805     hideMode:'offsets',
23806     
23807     tbContainer : false,
23808     
23809     bodyCls : '',
23810     
23811     toolbarContainer :function() {
23812         return this.wrap.select('.x-html-editor-tb',true).first();
23813     },
23814
23815     /**
23816      * Protected method that will not generally be called directly. It
23817      * is called when the editor creates its toolbar. Override this method if you need to
23818      * add custom toolbar buttons.
23819      * @param {HtmlEditor} editor
23820      */
23821     createToolbar : function(){
23822         Roo.log('renewing');
23823         Roo.log("create toolbars");
23824         
23825         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23826         this.toolbars[0].render(this.toolbarContainer());
23827         
23828         return;
23829         
23830 //        if (!editor.toolbars || !editor.toolbars.length) {
23831 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23832 //        }
23833 //        
23834 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23835 //            editor.toolbars[i] = Roo.factory(
23836 //                    typeof(editor.toolbars[i]) == 'string' ?
23837 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23838 //                Roo.bootstrap.HtmlEditor);
23839 //            editor.toolbars[i].init(editor);
23840 //        }
23841     },
23842
23843      
23844     // private
23845     onRender : function(ct, position)
23846     {
23847        // Roo.log("Call onRender: " + this.xtype);
23848         var _t = this;
23849         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23850       
23851         this.wrap = this.inputEl().wrap({
23852             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23853         });
23854         
23855         this.editorcore.onRender(ct, position);
23856          
23857         if (this.resizable) {
23858             this.resizeEl = new Roo.Resizable(this.wrap, {
23859                 pinned : true,
23860                 wrap: true,
23861                 dynamic : true,
23862                 minHeight : this.height,
23863                 height: this.height,
23864                 handles : this.resizable,
23865                 width: this.width,
23866                 listeners : {
23867                     resize : function(r, w, h) {
23868                         _t.onResize(w,h); // -something
23869                     }
23870                 }
23871             });
23872             
23873         }
23874         this.createToolbar(this);
23875        
23876         
23877         if(!this.width && this.resizable){
23878             this.setSize(this.wrap.getSize());
23879         }
23880         if (this.resizeEl) {
23881             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23882             // should trigger onReize..
23883         }
23884         
23885     },
23886
23887     // private
23888     onResize : function(w, h)
23889     {
23890         Roo.log('resize: ' +w + ',' + h );
23891         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23892         var ew = false;
23893         var eh = false;
23894         
23895         if(this.inputEl() ){
23896             if(typeof w == 'number'){
23897                 var aw = w - this.wrap.getFrameWidth('lr');
23898                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23899                 ew = aw;
23900             }
23901             if(typeof h == 'number'){
23902                  var tbh = -11;  // fixme it needs to tool bar size!
23903                 for (var i =0; i < this.toolbars.length;i++) {
23904                     // fixme - ask toolbars for heights?
23905                     tbh += this.toolbars[i].el.getHeight();
23906                     //if (this.toolbars[i].footer) {
23907                     //    tbh += this.toolbars[i].footer.el.getHeight();
23908                     //}
23909                 }
23910               
23911                 
23912                 
23913                 
23914                 
23915                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23916                 ah -= 5; // knock a few pixes off for look..
23917                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23918                 var eh = ah;
23919             }
23920         }
23921         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23922         this.editorcore.onResize(ew,eh);
23923         
23924     },
23925
23926     /**
23927      * Toggles the editor between standard and source edit mode.
23928      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23929      */
23930     toggleSourceEdit : function(sourceEditMode)
23931     {
23932         this.editorcore.toggleSourceEdit(sourceEditMode);
23933         
23934         if(this.editorcore.sourceEditMode){
23935             Roo.log('editor - showing textarea');
23936             
23937 //            Roo.log('in');
23938 //            Roo.log(this.syncValue());
23939             this.syncValue();
23940             this.inputEl().removeClass(['hide', 'x-hidden']);
23941             this.inputEl().dom.removeAttribute('tabIndex');
23942             this.inputEl().focus();
23943         }else{
23944             Roo.log('editor - hiding textarea');
23945 //            Roo.log('out')
23946 //            Roo.log(this.pushValue()); 
23947             this.pushValue();
23948             
23949             this.inputEl().addClass(['hide', 'x-hidden']);
23950             this.inputEl().dom.setAttribute('tabIndex', -1);
23951             //this.deferFocus();
23952         }
23953          
23954         if(this.resizable){
23955             this.setSize(this.wrap.getSize());
23956         }
23957         
23958         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23959     },
23960  
23961     // private (for BoxComponent)
23962     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23963
23964     // private (for BoxComponent)
23965     getResizeEl : function(){
23966         return this.wrap;
23967     },
23968
23969     // private (for BoxComponent)
23970     getPositionEl : function(){
23971         return this.wrap;
23972     },
23973
23974     // private
23975     initEvents : function(){
23976         this.originalValue = this.getValue();
23977     },
23978
23979 //    /**
23980 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23981 //     * @method
23982 //     */
23983 //    markInvalid : Roo.emptyFn,
23984 //    /**
23985 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23986 //     * @method
23987 //     */
23988 //    clearInvalid : Roo.emptyFn,
23989
23990     setValue : function(v){
23991         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23992         this.editorcore.pushValue();
23993     },
23994
23995      
23996     // private
23997     deferFocus : function(){
23998         this.focus.defer(10, this);
23999     },
24000
24001     // doc'ed in Field
24002     focus : function(){
24003         this.editorcore.focus();
24004         
24005     },
24006       
24007
24008     // private
24009     onDestroy : function(){
24010         
24011         
24012         
24013         if(this.rendered){
24014             
24015             for (var i =0; i < this.toolbars.length;i++) {
24016                 // fixme - ask toolbars for heights?
24017                 this.toolbars[i].onDestroy();
24018             }
24019             
24020             this.wrap.dom.innerHTML = '';
24021             this.wrap.remove();
24022         }
24023     },
24024
24025     // private
24026     onFirstFocus : function(){
24027         //Roo.log("onFirstFocus");
24028         this.editorcore.onFirstFocus();
24029          for (var i =0; i < this.toolbars.length;i++) {
24030             this.toolbars[i].onFirstFocus();
24031         }
24032         
24033     },
24034     
24035     // private
24036     syncValue : function()
24037     {   
24038         this.editorcore.syncValue();
24039     },
24040     
24041     pushValue : function()
24042     {   
24043         this.editorcore.pushValue();
24044     }
24045      
24046     
24047     // hide stuff that is not compatible
24048     /**
24049      * @event blur
24050      * @hide
24051      */
24052     /**
24053      * @event change
24054      * @hide
24055      */
24056     /**
24057      * @event focus
24058      * @hide
24059      */
24060     /**
24061      * @event specialkey
24062      * @hide
24063      */
24064     /**
24065      * @cfg {String} fieldClass @hide
24066      */
24067     /**
24068      * @cfg {String} focusClass @hide
24069      */
24070     /**
24071      * @cfg {String} autoCreate @hide
24072      */
24073     /**
24074      * @cfg {String} inputType @hide
24075      */
24076      
24077     /**
24078      * @cfg {String} invalidText @hide
24079      */
24080     /**
24081      * @cfg {String} msgFx @hide
24082      */
24083     /**
24084      * @cfg {String} validateOnBlur @hide
24085      */
24086 });
24087  
24088     
24089    
24090    
24091    
24092       
24093 Roo.namespace('Roo.bootstrap.htmleditor');
24094 /**
24095  * @class Roo.bootstrap.HtmlEditorToolbar1
24096  * Basic Toolbar
24097  * 
24098  * @example
24099  * Usage:
24100  *
24101  new Roo.bootstrap.HtmlEditor({
24102     ....
24103     toolbars : [
24104         new Roo.bootstrap.HtmlEditorToolbar1({
24105             disable : { fonts: 1 , format: 1, ..., ... , ...],
24106             btns : [ .... ]
24107         })
24108     }
24109      
24110  * 
24111  * @cfg {Object} disable List of elements to disable..
24112  * @cfg {Array} btns List of additional buttons.
24113  * 
24114  * 
24115  * NEEDS Extra CSS? 
24116  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24117  */
24118  
24119 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
24120 {
24121     
24122     Roo.apply(this, config);
24123     
24124     // default disabled, based on 'good practice'..
24125     this.disable = this.disable || {};
24126     Roo.applyIf(this.disable, {
24127         fontSize : true,
24128         colors : true,
24129         specialElements : true
24130     });
24131     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
24132     
24133     this.editor = config.editor;
24134     this.editorcore = config.editor.editorcore;
24135     
24136     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
24137     
24138     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24139     // dont call parent... till later.
24140 }
24141 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
24142      
24143     bar : true,
24144     
24145     editor : false,
24146     editorcore : false,
24147     
24148     
24149     formats : [
24150         "p" ,  
24151         "h1","h2","h3","h4","h5","h6", 
24152         "pre", "code", 
24153         "abbr", "acronym", "address", "cite", "samp", "var",
24154         'div','span'
24155     ],
24156     
24157     onRender : function(ct, position)
24158     {
24159        // Roo.log("Call onRender: " + this.xtype);
24160         
24161        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
24162        Roo.log(this.el);
24163        this.el.dom.style.marginBottom = '0';
24164        var _this = this;
24165        var editorcore = this.editorcore;
24166        var editor= this.editor;
24167        
24168        var children = [];
24169        var btn = function(id,cmd , toggle, handler, html){
24170        
24171             var  event = toggle ? 'toggle' : 'click';
24172        
24173             var a = {
24174                 size : 'sm',
24175                 xtype: 'Button',
24176                 xns: Roo.bootstrap,
24177                 //glyphicon : id,
24178                 fa: id,
24179                 cmd : id || cmd,
24180                 enableToggle:toggle !== false,
24181                 html : html || '',
24182                 pressed : toggle ? false : null,
24183                 listeners : {}
24184             };
24185             a.listeners[toggle ? 'toggle' : 'click'] = function() {
24186                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
24187             };
24188             children.push(a);
24189             return a;
24190        }
24191        
24192     //    var cb_box = function...
24193         
24194         var style = {
24195                 xtype: 'Button',
24196                 size : 'sm',
24197                 xns: Roo.bootstrap,
24198                 fa : 'font',
24199                 //html : 'submit'
24200                 menu : {
24201                     xtype: 'Menu',
24202                     xns: Roo.bootstrap,
24203                     items:  []
24204                 }
24205         };
24206         Roo.each(this.formats, function(f) {
24207             style.menu.items.push({
24208                 xtype :'MenuItem',
24209                 xns: Roo.bootstrap,
24210                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24211                 tagname : f,
24212                 listeners : {
24213                     click : function()
24214                     {
24215                         editorcore.insertTag(this.tagname);
24216                         editor.focus();
24217                     }
24218                 }
24219                 
24220             });
24221         });
24222         children.push(style);   
24223         
24224         btn('bold',false,true);
24225         btn('italic',false,true);
24226         btn('align-left', 'justifyleft',true);
24227         btn('align-center', 'justifycenter',true);
24228         btn('align-right' , 'justifyright',true);
24229         btn('link', false, false, function(btn) {
24230             //Roo.log("create link?");
24231             var url = prompt(this.createLinkText, this.defaultLinkValue);
24232             if(url && url != 'http:/'+'/'){
24233                 this.editorcore.relayCmd('createlink', url);
24234             }
24235         }),
24236         btn('list','insertunorderedlist',true);
24237         btn('pencil', false,true, function(btn){
24238                 Roo.log(this);
24239                 this.toggleSourceEdit(btn.pressed);
24240         });
24241         
24242         if (this.editor.btns.length > 0) {
24243             for (var i = 0; i<this.editor.btns.length; i++) {
24244                 children.push(this.editor.btns[i]);
24245             }
24246         }
24247         
24248         /*
24249         var cog = {
24250                 xtype: 'Button',
24251                 size : 'sm',
24252                 xns: Roo.bootstrap,
24253                 glyphicon : 'cog',
24254                 //html : 'submit'
24255                 menu : {
24256                     xtype: 'Menu',
24257                     xns: Roo.bootstrap,
24258                     items:  []
24259                 }
24260         };
24261         
24262         cog.menu.items.push({
24263             xtype :'MenuItem',
24264             xns: Roo.bootstrap,
24265             html : Clean styles,
24266             tagname : f,
24267             listeners : {
24268                 click : function()
24269                 {
24270                     editorcore.insertTag(this.tagname);
24271                     editor.focus();
24272                 }
24273             }
24274             
24275         });
24276        */
24277         
24278          
24279        this.xtype = 'NavSimplebar';
24280         
24281         for(var i=0;i< children.length;i++) {
24282             
24283             this.buttons.add(this.addxtypeChild(children[i]));
24284             
24285         }
24286         
24287         editor.on('editorevent', this.updateToolbar, this);
24288     },
24289     onBtnClick : function(id)
24290     {
24291        this.editorcore.relayCmd(id);
24292        this.editorcore.focus();
24293     },
24294     
24295     /**
24296      * Protected method that will not generally be called directly. It triggers
24297      * a toolbar update by reading the markup state of the current selection in the editor.
24298      */
24299     updateToolbar: function(){
24300
24301         if(!this.editorcore.activated){
24302             this.editor.onFirstFocus(); // is this neeed?
24303             return;
24304         }
24305
24306         var btns = this.buttons; 
24307         var doc = this.editorcore.doc;
24308         btns.get('bold').setActive(doc.queryCommandState('bold'));
24309         btns.get('italic').setActive(doc.queryCommandState('italic'));
24310         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24311         
24312         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24313         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24314         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24315         
24316         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24317         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24318          /*
24319         
24320         var ans = this.editorcore.getAllAncestors();
24321         if (this.formatCombo) {
24322             
24323             
24324             var store = this.formatCombo.store;
24325             this.formatCombo.setValue("");
24326             for (var i =0; i < ans.length;i++) {
24327                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24328                     // select it..
24329                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24330                     break;
24331                 }
24332             }
24333         }
24334         
24335         
24336         
24337         // hides menus... - so this cant be on a menu...
24338         Roo.bootstrap.MenuMgr.hideAll();
24339         */
24340         Roo.bootstrap.MenuMgr.hideAll();
24341         //this.editorsyncValue();
24342     },
24343     onFirstFocus: function() {
24344         this.buttons.each(function(item){
24345            item.enable();
24346         });
24347     },
24348     toggleSourceEdit : function(sourceEditMode){
24349         
24350           
24351         if(sourceEditMode){
24352             Roo.log("disabling buttons");
24353            this.buttons.each( function(item){
24354                 if(item.cmd != 'pencil'){
24355                     item.disable();
24356                 }
24357             });
24358           
24359         }else{
24360             Roo.log("enabling buttons");
24361             if(this.editorcore.initialized){
24362                 this.buttons.each( function(item){
24363                     item.enable();
24364                 });
24365             }
24366             
24367         }
24368         Roo.log("calling toggole on editor");
24369         // tell the editor that it's been pressed..
24370         this.editor.toggleSourceEdit(sourceEditMode);
24371        
24372     }
24373 });
24374
24375
24376
24377
24378
24379 /**
24380  * @class Roo.bootstrap.Table.AbstractSelectionModel
24381  * @extends Roo.util.Observable
24382  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24383  * implemented by descendant classes.  This class should not be directly instantiated.
24384  * @constructor
24385  */
24386 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24387     this.locked = false;
24388     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24389 };
24390
24391
24392 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24393     /** @ignore Called by the grid automatically. Do not call directly. */
24394     init : function(grid){
24395         this.grid = grid;
24396         this.initEvents();
24397     },
24398
24399     /**
24400      * Locks the selections.
24401      */
24402     lock : function(){
24403         this.locked = true;
24404     },
24405
24406     /**
24407      * Unlocks the selections.
24408      */
24409     unlock : function(){
24410         this.locked = false;
24411     },
24412
24413     /**
24414      * Returns true if the selections are locked.
24415      * @return {Boolean}
24416      */
24417     isLocked : function(){
24418         return this.locked;
24419     }
24420 });
24421 /**
24422  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24423  * @class Roo.bootstrap.Table.RowSelectionModel
24424  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24425  * It supports multiple selections and keyboard selection/navigation. 
24426  * @constructor
24427  * @param {Object} config
24428  */
24429
24430 Roo.bootstrap.Table.RowSelectionModel = function(config){
24431     Roo.apply(this, config);
24432     this.selections = new Roo.util.MixedCollection(false, function(o){
24433         return o.id;
24434     });
24435
24436     this.last = false;
24437     this.lastActive = false;
24438
24439     this.addEvents({
24440         /**
24441              * @event selectionchange
24442              * Fires when the selection changes
24443              * @param {SelectionModel} this
24444              */
24445             "selectionchange" : true,
24446         /**
24447              * @event afterselectionchange
24448              * Fires after the selection changes (eg. by key press or clicking)
24449              * @param {SelectionModel} this
24450              */
24451             "afterselectionchange" : true,
24452         /**
24453              * @event beforerowselect
24454              * Fires when a row is selected being selected, return false to cancel.
24455              * @param {SelectionModel} this
24456              * @param {Number} rowIndex The selected index
24457              * @param {Boolean} keepExisting False if other selections will be cleared
24458              */
24459             "beforerowselect" : true,
24460         /**
24461              * @event rowselect
24462              * Fires when a row is selected.
24463              * @param {SelectionModel} this
24464              * @param {Number} rowIndex The selected index
24465              * @param {Roo.data.Record} r The record
24466              */
24467             "rowselect" : true,
24468         /**
24469              * @event rowdeselect
24470              * Fires when a row is deselected.
24471              * @param {SelectionModel} this
24472              * @param {Number} rowIndex The selected index
24473              */
24474         "rowdeselect" : true
24475     });
24476     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24477     this.locked = false;
24478  };
24479
24480 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24481     /**
24482      * @cfg {Boolean} singleSelect
24483      * True to allow selection of only one row at a time (defaults to false)
24484      */
24485     singleSelect : false,
24486
24487     // private
24488     initEvents : function()
24489     {
24490
24491         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24492         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24493         //}else{ // allow click to work like normal
24494          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24495         //}
24496         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24497         this.grid.on("rowclick", this.handleMouseDown, this);
24498         
24499         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24500             "up" : function(e){
24501                 if(!e.shiftKey){
24502                     this.selectPrevious(e.shiftKey);
24503                 }else if(this.last !== false && this.lastActive !== false){
24504                     var last = this.last;
24505                     this.selectRange(this.last,  this.lastActive-1);
24506                     this.grid.getView().focusRow(this.lastActive);
24507                     if(last !== false){
24508                         this.last = last;
24509                     }
24510                 }else{
24511                     this.selectFirstRow();
24512                 }
24513                 this.fireEvent("afterselectionchange", this);
24514             },
24515             "down" : function(e){
24516                 if(!e.shiftKey){
24517                     this.selectNext(e.shiftKey);
24518                 }else if(this.last !== false && this.lastActive !== false){
24519                     var last = this.last;
24520                     this.selectRange(this.last,  this.lastActive+1);
24521                     this.grid.getView().focusRow(this.lastActive);
24522                     if(last !== false){
24523                         this.last = last;
24524                     }
24525                 }else{
24526                     this.selectFirstRow();
24527                 }
24528                 this.fireEvent("afterselectionchange", this);
24529             },
24530             scope: this
24531         });
24532         this.grid.store.on('load', function(){
24533             this.selections.clear();
24534         },this);
24535         /*
24536         var view = this.grid.view;
24537         view.on("refresh", this.onRefresh, this);
24538         view.on("rowupdated", this.onRowUpdated, this);
24539         view.on("rowremoved", this.onRemove, this);
24540         */
24541     },
24542
24543     // private
24544     onRefresh : function()
24545     {
24546         var ds = this.grid.store, i, v = this.grid.view;
24547         var s = this.selections;
24548         s.each(function(r){
24549             if((i = ds.indexOfId(r.id)) != -1){
24550                 v.onRowSelect(i);
24551             }else{
24552                 s.remove(r);
24553             }
24554         });
24555     },
24556
24557     // private
24558     onRemove : function(v, index, r){
24559         this.selections.remove(r);
24560     },
24561
24562     // private
24563     onRowUpdated : function(v, index, r){
24564         if(this.isSelected(r)){
24565             v.onRowSelect(index);
24566         }
24567     },
24568
24569     /**
24570      * Select records.
24571      * @param {Array} records The records to select
24572      * @param {Boolean} keepExisting (optional) True to keep existing selections
24573      */
24574     selectRecords : function(records, keepExisting)
24575     {
24576         if(!keepExisting){
24577             this.clearSelections();
24578         }
24579             var ds = this.grid.store;
24580         for(var i = 0, len = records.length; i < len; i++){
24581             this.selectRow(ds.indexOf(records[i]), true);
24582         }
24583     },
24584
24585     /**
24586      * Gets the number of selected rows.
24587      * @return {Number}
24588      */
24589     getCount : function(){
24590         return this.selections.length;
24591     },
24592
24593     /**
24594      * Selects the first row in the grid.
24595      */
24596     selectFirstRow : function(){
24597         this.selectRow(0);
24598     },
24599
24600     /**
24601      * Select the last row.
24602      * @param {Boolean} keepExisting (optional) True to keep existing selections
24603      */
24604     selectLastRow : function(keepExisting){
24605         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24606         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24607     },
24608
24609     /**
24610      * Selects the row immediately following the last selected row.
24611      * @param {Boolean} keepExisting (optional) True to keep existing selections
24612      */
24613     selectNext : function(keepExisting)
24614     {
24615             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24616             this.selectRow(this.last+1, keepExisting);
24617             this.grid.getView().focusRow(this.last);
24618         }
24619     },
24620
24621     /**
24622      * Selects the row that precedes the last selected row.
24623      * @param {Boolean} keepExisting (optional) True to keep existing selections
24624      */
24625     selectPrevious : function(keepExisting){
24626         if(this.last){
24627             this.selectRow(this.last-1, keepExisting);
24628             this.grid.getView().focusRow(this.last);
24629         }
24630     },
24631
24632     /**
24633      * Returns the selected records
24634      * @return {Array} Array of selected records
24635      */
24636     getSelections : function(){
24637         return [].concat(this.selections.items);
24638     },
24639
24640     /**
24641      * Returns the first selected record.
24642      * @return {Record}
24643      */
24644     getSelected : function(){
24645         return this.selections.itemAt(0);
24646     },
24647
24648
24649     /**
24650      * Clears all selections.
24651      */
24652     clearSelections : function(fast)
24653     {
24654         if(this.locked) {
24655             return;
24656         }
24657         if(fast !== true){
24658                 var ds = this.grid.store;
24659             var s = this.selections;
24660             s.each(function(r){
24661                 this.deselectRow(ds.indexOfId(r.id));
24662             }, this);
24663             s.clear();
24664         }else{
24665             this.selections.clear();
24666         }
24667         this.last = false;
24668     },
24669
24670
24671     /**
24672      * Selects all rows.
24673      */
24674     selectAll : function(){
24675         if(this.locked) {
24676             return;
24677         }
24678         this.selections.clear();
24679         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24680             this.selectRow(i, true);
24681         }
24682     },
24683
24684     /**
24685      * Returns True if there is a selection.
24686      * @return {Boolean}
24687      */
24688     hasSelection : function(){
24689         return this.selections.length > 0;
24690     },
24691
24692     /**
24693      * Returns True if the specified row is selected.
24694      * @param {Number/Record} record The record or index of the record to check
24695      * @return {Boolean}
24696      */
24697     isSelected : function(index){
24698             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24699         return (r && this.selections.key(r.id) ? true : false);
24700     },
24701
24702     /**
24703      * Returns True if the specified record id is selected.
24704      * @param {String} id The id of record to check
24705      * @return {Boolean}
24706      */
24707     isIdSelected : function(id){
24708         return (this.selections.key(id) ? true : false);
24709     },
24710
24711
24712     // private
24713     handleMouseDBClick : function(e, t){
24714         
24715     },
24716     // private
24717     handleMouseDown : function(e, t)
24718     {
24719             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24720         if(this.isLocked() || rowIndex < 0 ){
24721             return;
24722         };
24723         if(e.shiftKey && this.last !== false){
24724             var last = this.last;
24725             this.selectRange(last, rowIndex, e.ctrlKey);
24726             this.last = last; // reset the last
24727             t.focus();
24728     
24729         }else{
24730             var isSelected = this.isSelected(rowIndex);
24731             //Roo.log("select row:" + rowIndex);
24732             if(isSelected){
24733                 this.deselectRow(rowIndex);
24734             } else {
24735                         this.selectRow(rowIndex, true);
24736             }
24737     
24738             /*
24739                 if(e.button !== 0 && isSelected){
24740                 alert('rowIndex 2: ' + rowIndex);
24741                     view.focusRow(rowIndex);
24742                 }else if(e.ctrlKey && isSelected){
24743                     this.deselectRow(rowIndex);
24744                 }else if(!isSelected){
24745                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24746                     view.focusRow(rowIndex);
24747                 }
24748             */
24749         }
24750         this.fireEvent("afterselectionchange", this);
24751     },
24752     // private
24753     handleDragableRowClick :  function(grid, rowIndex, e) 
24754     {
24755         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24756             this.selectRow(rowIndex, false);
24757             grid.view.focusRow(rowIndex);
24758              this.fireEvent("afterselectionchange", this);
24759         }
24760     },
24761     
24762     /**
24763      * Selects multiple rows.
24764      * @param {Array} rows Array of the indexes of the row to select
24765      * @param {Boolean} keepExisting (optional) True to keep existing selections
24766      */
24767     selectRows : function(rows, keepExisting){
24768         if(!keepExisting){
24769             this.clearSelections();
24770         }
24771         for(var i = 0, len = rows.length; i < len; i++){
24772             this.selectRow(rows[i], true);
24773         }
24774     },
24775
24776     /**
24777      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24778      * @param {Number} startRow The index of the first row in the range
24779      * @param {Number} endRow The index of the last row in the range
24780      * @param {Boolean} keepExisting (optional) True to retain existing selections
24781      */
24782     selectRange : function(startRow, endRow, keepExisting){
24783         if(this.locked) {
24784             return;
24785         }
24786         if(!keepExisting){
24787             this.clearSelections();
24788         }
24789         if(startRow <= endRow){
24790             for(var i = startRow; i <= endRow; i++){
24791                 this.selectRow(i, true);
24792             }
24793         }else{
24794             for(var i = startRow; i >= endRow; i--){
24795                 this.selectRow(i, true);
24796             }
24797         }
24798     },
24799
24800     /**
24801      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24802      * @param {Number} startRow The index of the first row in the range
24803      * @param {Number} endRow The index of the last row in the range
24804      */
24805     deselectRange : function(startRow, endRow, preventViewNotify){
24806         if(this.locked) {
24807             return;
24808         }
24809         for(var i = startRow; i <= endRow; i++){
24810             this.deselectRow(i, preventViewNotify);
24811         }
24812     },
24813
24814     /**
24815      * Selects a row.
24816      * @param {Number} row The index of the row to select
24817      * @param {Boolean} keepExisting (optional) True to keep existing selections
24818      */
24819     selectRow : function(index, keepExisting, preventViewNotify)
24820     {
24821             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24822             return;
24823         }
24824         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24825             if(!keepExisting || this.singleSelect){
24826                 this.clearSelections();
24827             }
24828             
24829             var r = this.grid.store.getAt(index);
24830             //console.log('selectRow - record id :' + r.id);
24831             
24832             this.selections.add(r);
24833             this.last = this.lastActive = index;
24834             if(!preventViewNotify){
24835                 var proxy = new Roo.Element(
24836                                 this.grid.getRowDom(index)
24837                 );
24838                 proxy.addClass('bg-info info');
24839             }
24840             this.fireEvent("rowselect", this, index, r);
24841             this.fireEvent("selectionchange", this);
24842         }
24843     },
24844
24845     /**
24846      * Deselects a row.
24847      * @param {Number} row The index of the row to deselect
24848      */
24849     deselectRow : function(index, preventViewNotify)
24850     {
24851         if(this.locked) {
24852             return;
24853         }
24854         if(this.last == index){
24855             this.last = false;
24856         }
24857         if(this.lastActive == index){
24858             this.lastActive = false;
24859         }
24860         
24861         var r = this.grid.store.getAt(index);
24862         if (!r) {
24863             return;
24864         }
24865         
24866         this.selections.remove(r);
24867         //.console.log('deselectRow - record id :' + r.id);
24868         if(!preventViewNotify){
24869         
24870             var proxy = new Roo.Element(
24871                 this.grid.getRowDom(index)
24872             );
24873             proxy.removeClass('bg-info info');
24874         }
24875         this.fireEvent("rowdeselect", this, index);
24876         this.fireEvent("selectionchange", this);
24877     },
24878
24879     // private
24880     restoreLast : function(){
24881         if(this._last){
24882             this.last = this._last;
24883         }
24884     },
24885
24886     // private
24887     acceptsNav : function(row, col, cm){
24888         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24889     },
24890
24891     // private
24892     onEditorKey : function(field, e){
24893         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24894         if(k == e.TAB){
24895             e.stopEvent();
24896             ed.completeEdit();
24897             if(e.shiftKey){
24898                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24899             }else{
24900                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24901             }
24902         }else if(k == e.ENTER && !e.ctrlKey){
24903             e.stopEvent();
24904             ed.completeEdit();
24905             if(e.shiftKey){
24906                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24907             }else{
24908                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24909             }
24910         }else if(k == e.ESC){
24911             ed.cancelEdit();
24912         }
24913         if(newCell){
24914             g.startEditing(newCell[0], newCell[1]);
24915         }
24916     }
24917 });
24918 /*
24919  * Based on:
24920  * Ext JS Library 1.1.1
24921  * Copyright(c) 2006-2007, Ext JS, LLC.
24922  *
24923  * Originally Released Under LGPL - original licence link has changed is not relivant.
24924  *
24925  * Fork - LGPL
24926  * <script type="text/javascript">
24927  */
24928  
24929 /**
24930  * @class Roo.bootstrap.PagingToolbar
24931  * @extends Roo.bootstrap.NavSimplebar
24932  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24933  * @constructor
24934  * Create a new PagingToolbar
24935  * @param {Object} config The config object
24936  * @param {Roo.data.Store} store
24937  */
24938 Roo.bootstrap.PagingToolbar = function(config)
24939 {
24940     // old args format still supported... - xtype is prefered..
24941         // created from xtype...
24942     
24943     this.ds = config.dataSource;
24944     
24945     if (config.store && !this.ds) {
24946         this.store= Roo.factory(config.store, Roo.data);
24947         this.ds = this.store;
24948         this.ds.xmodule = this.xmodule || false;
24949     }
24950     
24951     this.toolbarItems = [];
24952     if (config.items) {
24953         this.toolbarItems = config.items;
24954     }
24955     
24956     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24957     
24958     this.cursor = 0;
24959     
24960     if (this.ds) { 
24961         this.bind(this.ds);
24962     }
24963     
24964     if (Roo.bootstrap.version == 4) {
24965         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24966     } else {
24967         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24968     }
24969     
24970 };
24971
24972 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24973     /**
24974      * @cfg {Roo.data.Store} dataSource
24975      * The underlying data store providing the paged data
24976      */
24977     /**
24978      * @cfg {String/HTMLElement/Element} container
24979      * container The id or element that will contain the toolbar
24980      */
24981     /**
24982      * @cfg {Boolean} displayInfo
24983      * True to display the displayMsg (defaults to false)
24984      */
24985     /**
24986      * @cfg {Number} pageSize
24987      * The number of records to display per page (defaults to 20)
24988      */
24989     pageSize: 20,
24990     /**
24991      * @cfg {String} displayMsg
24992      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24993      */
24994     displayMsg : 'Displaying {0} - {1} of {2}',
24995     /**
24996      * @cfg {String} emptyMsg
24997      * The message to display when no records are found (defaults to "No data to display")
24998      */
24999     emptyMsg : 'No data to display',
25000     /**
25001      * Customizable piece of the default paging text (defaults to "Page")
25002      * @type String
25003      */
25004     beforePageText : "Page",
25005     /**
25006      * Customizable piece of the default paging text (defaults to "of %0")
25007      * @type String
25008      */
25009     afterPageText : "of {0}",
25010     /**
25011      * Customizable piece of the default paging text (defaults to "First Page")
25012      * @type String
25013      */
25014     firstText : "First Page",
25015     /**
25016      * Customizable piece of the default paging text (defaults to "Previous Page")
25017      * @type String
25018      */
25019     prevText : "Previous Page",
25020     /**
25021      * Customizable piece of the default paging text (defaults to "Next Page")
25022      * @type String
25023      */
25024     nextText : "Next Page",
25025     /**
25026      * Customizable piece of the default paging text (defaults to "Last Page")
25027      * @type String
25028      */
25029     lastText : "Last Page",
25030     /**
25031      * Customizable piece of the default paging text (defaults to "Refresh")
25032      * @type String
25033      */
25034     refreshText : "Refresh",
25035
25036     buttons : false,
25037     // private
25038     onRender : function(ct, position) 
25039     {
25040         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
25041         this.navgroup.parentId = this.id;
25042         this.navgroup.onRender(this.el, null);
25043         // add the buttons to the navgroup
25044         
25045         if(this.displayInfo){
25046             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
25047             this.displayEl = this.el.select('.x-paging-info', true).first();
25048 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
25049 //            this.displayEl = navel.el.select('span',true).first();
25050         }
25051         
25052         var _this = this;
25053         
25054         if(this.buttons){
25055             Roo.each(_this.buttons, function(e){ // this might need to use render????
25056                Roo.factory(e).render(_this.el);
25057             });
25058         }
25059             
25060         Roo.each(_this.toolbarItems, function(e) {
25061             _this.navgroup.addItem(e);
25062         });
25063         
25064         
25065         this.first = this.navgroup.addItem({
25066             tooltip: this.firstText,
25067             cls: "prev btn-outline-secondary",
25068             html : ' <i class="fa fa-step-backward"></i>',
25069             disabled: true,
25070             preventDefault: true,
25071             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
25072         });
25073         
25074         this.prev =  this.navgroup.addItem({
25075             tooltip: this.prevText,
25076             cls: "prev btn-outline-secondary",
25077             html : ' <i class="fa fa-backward"></i>',
25078             disabled: true,
25079             preventDefault: true,
25080             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
25081         });
25082     //this.addSeparator();
25083         
25084         
25085         var field = this.navgroup.addItem( {
25086             tagtype : 'span',
25087             cls : 'x-paging-position  btn-outline-secondary',
25088              disabled: true,
25089             html : this.beforePageText  +
25090                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
25091                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
25092          } ); //?? escaped?
25093         
25094         this.field = field.el.select('input', true).first();
25095         this.field.on("keydown", this.onPagingKeydown, this);
25096         this.field.on("focus", function(){this.dom.select();});
25097     
25098     
25099         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
25100         //this.field.setHeight(18);
25101         //this.addSeparator();
25102         this.next = this.navgroup.addItem({
25103             tooltip: this.nextText,
25104             cls: "next btn-outline-secondary",
25105             html : ' <i class="fa fa-forward"></i>',
25106             disabled: true,
25107             preventDefault: true,
25108             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
25109         });
25110         this.last = this.navgroup.addItem({
25111             tooltip: this.lastText,
25112             html : ' <i class="fa fa-step-forward"></i>',
25113             cls: "next btn-outline-secondary",
25114             disabled: true,
25115             preventDefault: true,
25116             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
25117         });
25118     //this.addSeparator();
25119         this.loading = this.navgroup.addItem({
25120             tooltip: this.refreshText,
25121             cls: "btn-outline-secondary",
25122             html : ' <i class="fa fa-refresh"></i>',
25123             preventDefault: true,
25124             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
25125         });
25126         
25127     },
25128
25129     // private
25130     updateInfo : function(){
25131         if(this.displayEl){
25132             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
25133             var msg = count == 0 ?
25134                 this.emptyMsg :
25135                 String.format(
25136                     this.displayMsg,
25137                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
25138                 );
25139             this.displayEl.update(msg);
25140         }
25141     },
25142
25143     // private
25144     onLoad : function(ds, r, o)
25145     {
25146         this.cursor = o.params.start ? o.params.start : 0;
25147         
25148         var d = this.getPageData(),
25149             ap = d.activePage,
25150             ps = d.pages;
25151         
25152         
25153         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
25154         this.field.dom.value = ap;
25155         this.first.setDisabled(ap == 1);
25156         this.prev.setDisabled(ap == 1);
25157         this.next.setDisabled(ap == ps);
25158         this.last.setDisabled(ap == ps);
25159         this.loading.enable();
25160         this.updateInfo();
25161     },
25162
25163     // private
25164     getPageData : function(){
25165         var total = this.ds.getTotalCount();
25166         return {
25167             total : total,
25168             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
25169             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
25170         };
25171     },
25172
25173     // private
25174     onLoadError : function(){
25175         this.loading.enable();
25176     },
25177
25178     // private
25179     onPagingKeydown : function(e){
25180         var k = e.getKey();
25181         var d = this.getPageData();
25182         if(k == e.RETURN){
25183             var v = this.field.dom.value, pageNum;
25184             if(!v || isNaN(pageNum = parseInt(v, 10))){
25185                 this.field.dom.value = d.activePage;
25186                 return;
25187             }
25188             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
25189             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25190             e.stopEvent();
25191         }
25192         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))
25193         {
25194           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
25195           this.field.dom.value = pageNum;
25196           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
25197           e.stopEvent();
25198         }
25199         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
25200         {
25201           var v = this.field.dom.value, pageNum; 
25202           var increment = (e.shiftKey) ? 10 : 1;
25203           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
25204                 increment *= -1;
25205           }
25206           if(!v || isNaN(pageNum = parseInt(v, 10))) {
25207             this.field.dom.value = d.activePage;
25208             return;
25209           }
25210           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25211           {
25212             this.field.dom.value = parseInt(v, 10) + increment;
25213             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25214             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25215           }
25216           e.stopEvent();
25217         }
25218     },
25219
25220     // private
25221     beforeLoad : function(){
25222         if(this.loading){
25223             this.loading.disable();
25224         }
25225     },
25226
25227     // private
25228     onClick : function(which){
25229         
25230         var ds = this.ds;
25231         if (!ds) {
25232             return;
25233         }
25234         
25235         switch(which){
25236             case "first":
25237                 ds.load({params:{start: 0, limit: this.pageSize}});
25238             break;
25239             case "prev":
25240                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25241             break;
25242             case "next":
25243                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25244             break;
25245             case "last":
25246                 var total = ds.getTotalCount();
25247                 var extra = total % this.pageSize;
25248                 var lastStart = extra ? (total - extra) : total-this.pageSize;
25249                 ds.load({params:{start: lastStart, limit: this.pageSize}});
25250             break;
25251             case "refresh":
25252                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25253             break;
25254         }
25255     },
25256
25257     /**
25258      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25259      * @param {Roo.data.Store} store The data store to unbind
25260      */
25261     unbind : function(ds){
25262         ds.un("beforeload", this.beforeLoad, this);
25263         ds.un("load", this.onLoad, this);
25264         ds.un("loadexception", this.onLoadError, this);
25265         ds.un("remove", this.updateInfo, this);
25266         ds.un("add", this.updateInfo, this);
25267         this.ds = undefined;
25268     },
25269
25270     /**
25271      * Binds the paging toolbar to the specified {@link Roo.data.Store}
25272      * @param {Roo.data.Store} store The data store to bind
25273      */
25274     bind : function(ds){
25275         ds.on("beforeload", this.beforeLoad, this);
25276         ds.on("load", this.onLoad, this);
25277         ds.on("loadexception", this.onLoadError, this);
25278         ds.on("remove", this.updateInfo, this);
25279         ds.on("add", this.updateInfo, this);
25280         this.ds = ds;
25281     }
25282 });/*
25283  * - LGPL
25284  *
25285  * element
25286  * 
25287  */
25288
25289 /**
25290  * @class Roo.bootstrap.MessageBar
25291  * @extends Roo.bootstrap.Component
25292  * Bootstrap MessageBar class
25293  * @cfg {String} html contents of the MessageBar
25294  * @cfg {String} weight (info | success | warning | danger) default info
25295  * @cfg {String} beforeClass insert the bar before the given class
25296  * @cfg {Boolean} closable (true | false) default false
25297  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25298  * 
25299  * @constructor
25300  * Create a new Element
25301  * @param {Object} config The config object
25302  */
25303
25304 Roo.bootstrap.MessageBar = function(config){
25305     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25306 };
25307
25308 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25309     
25310     html: '',
25311     weight: 'info',
25312     closable: false,
25313     fixed: false,
25314     beforeClass: 'bootstrap-sticky-wrap',
25315     
25316     getAutoCreate : function(){
25317         
25318         var cfg = {
25319             tag: 'div',
25320             cls: 'alert alert-dismissable alert-' + this.weight,
25321             cn: [
25322                 {
25323                     tag: 'span',
25324                     cls: 'message',
25325                     html: this.html || ''
25326                 }
25327             ]
25328         };
25329         
25330         if(this.fixed){
25331             cfg.cls += ' alert-messages-fixed';
25332         }
25333         
25334         if(this.closable){
25335             cfg.cn.push({
25336                 tag: 'button',
25337                 cls: 'close',
25338                 html: 'x'
25339             });
25340         }
25341         
25342         return cfg;
25343     },
25344     
25345     onRender : function(ct, position)
25346     {
25347         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25348         
25349         if(!this.el){
25350             var cfg = Roo.apply({},  this.getAutoCreate());
25351             cfg.id = Roo.id();
25352             
25353             if (this.cls) {
25354                 cfg.cls += ' ' + this.cls;
25355             }
25356             if (this.style) {
25357                 cfg.style = this.style;
25358             }
25359             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25360             
25361             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25362         }
25363         
25364         this.el.select('>button.close').on('click', this.hide, this);
25365         
25366     },
25367     
25368     show : function()
25369     {
25370         if (!this.rendered) {
25371             this.render();
25372         }
25373         
25374         this.el.show();
25375         
25376         this.fireEvent('show', this);
25377         
25378     },
25379     
25380     hide : function()
25381     {
25382         if (!this.rendered) {
25383             this.render();
25384         }
25385         
25386         this.el.hide();
25387         
25388         this.fireEvent('hide', this);
25389     },
25390     
25391     update : function()
25392     {
25393 //        var e = this.el.dom.firstChild;
25394 //        
25395 //        if(this.closable){
25396 //            e = e.nextSibling;
25397 //        }
25398 //        
25399 //        e.data = this.html || '';
25400
25401         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25402     }
25403    
25404 });
25405
25406  
25407
25408      /*
25409  * - LGPL
25410  *
25411  * Graph
25412  * 
25413  */
25414
25415
25416 /**
25417  * @class Roo.bootstrap.Graph
25418  * @extends Roo.bootstrap.Component
25419  * Bootstrap Graph class
25420 > Prameters
25421  -sm {number} sm 4
25422  -md {number} md 5
25423  @cfg {String} graphtype  bar | vbar | pie
25424  @cfg {number} g_x coodinator | centre x (pie)
25425  @cfg {number} g_y coodinator | centre y (pie)
25426  @cfg {number} g_r radius (pie)
25427  @cfg {number} g_height height of the chart (respected by all elements in the set)
25428  @cfg {number} g_width width of the chart (respected by all elements in the set)
25429  @cfg {Object} title The title of the chart
25430     
25431  -{Array}  values
25432  -opts (object) options for the chart 
25433      o {
25434      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25435      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25436      o vgutter (number)
25437      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.
25438      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25439      o to
25440      o stretch (boolean)
25441      o }
25442  -opts (object) options for the pie
25443      o{
25444      o cut
25445      o startAngle (number)
25446      o endAngle (number)
25447      } 
25448  *
25449  * @constructor
25450  * Create a new Input
25451  * @param {Object} config The config object
25452  */
25453
25454 Roo.bootstrap.Graph = function(config){
25455     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25456     
25457     this.addEvents({
25458         // img events
25459         /**
25460          * @event click
25461          * The img click event for the img.
25462          * @param {Roo.EventObject} e
25463          */
25464         "click" : true
25465     });
25466 };
25467
25468 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25469     
25470     sm: 4,
25471     md: 5,
25472     graphtype: 'bar',
25473     g_height: 250,
25474     g_width: 400,
25475     g_x: 50,
25476     g_y: 50,
25477     g_r: 30,
25478     opts:{
25479         //g_colors: this.colors,
25480         g_type: 'soft',
25481         g_gutter: '20%'
25482
25483     },
25484     title : false,
25485
25486     getAutoCreate : function(){
25487         
25488         var cfg = {
25489             tag: 'div',
25490             html : null
25491         };
25492         
25493         
25494         return  cfg;
25495     },
25496
25497     onRender : function(ct,position){
25498         
25499         
25500         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25501         
25502         if (typeof(Raphael) == 'undefined') {
25503             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25504             return;
25505         }
25506         
25507         this.raphael = Raphael(this.el.dom);
25508         
25509                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25510                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25511                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25512                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25513                 /*
25514                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25515                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25516                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25517                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25518                 
25519                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25520                 r.barchart(330, 10, 300, 220, data1);
25521                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25522                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25523                 */
25524                 
25525                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25526                 // r.barchart(30, 30, 560, 250,  xdata, {
25527                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25528                 //     axis : "0 0 1 1",
25529                 //     axisxlabels :  xdata
25530                 //     //yvalues : cols,
25531                    
25532                 // });
25533 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25534 //        
25535 //        this.load(null,xdata,{
25536 //                axis : "0 0 1 1",
25537 //                axisxlabels :  xdata
25538 //                });
25539
25540     },
25541
25542     load : function(graphtype,xdata,opts)
25543     {
25544         this.raphael.clear();
25545         if(!graphtype) {
25546             graphtype = this.graphtype;
25547         }
25548         if(!opts){
25549             opts = this.opts;
25550         }
25551         var r = this.raphael,
25552             fin = function () {
25553                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25554             },
25555             fout = function () {
25556                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25557             },
25558             pfin = function() {
25559                 this.sector.stop();
25560                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25561
25562                 if (this.label) {
25563                     this.label[0].stop();
25564                     this.label[0].attr({ r: 7.5 });
25565                     this.label[1].attr({ "font-weight": 800 });
25566                 }
25567             },
25568             pfout = function() {
25569                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25570
25571                 if (this.label) {
25572                     this.label[0].animate({ r: 5 }, 500, "bounce");
25573                     this.label[1].attr({ "font-weight": 400 });
25574                 }
25575             };
25576
25577         switch(graphtype){
25578             case 'bar':
25579                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25580                 break;
25581             case 'hbar':
25582                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25583                 break;
25584             case 'pie':
25585 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25586 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25587 //            
25588                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25589                 
25590                 break;
25591
25592         }
25593         
25594         if(this.title){
25595             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25596         }
25597         
25598     },
25599     
25600     setTitle: function(o)
25601     {
25602         this.title = o;
25603     },
25604     
25605     initEvents: function() {
25606         
25607         if(!this.href){
25608             this.el.on('click', this.onClick, this);
25609         }
25610     },
25611     
25612     onClick : function(e)
25613     {
25614         Roo.log('img onclick');
25615         this.fireEvent('click', this, e);
25616     }
25617    
25618 });
25619
25620  
25621 /*
25622  * - LGPL
25623  *
25624  * numberBox
25625  * 
25626  */
25627 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25628
25629 /**
25630  * @class Roo.bootstrap.dash.NumberBox
25631  * @extends Roo.bootstrap.Component
25632  * Bootstrap NumberBox class
25633  * @cfg {String} headline Box headline
25634  * @cfg {String} content Box content
25635  * @cfg {String} icon Box icon
25636  * @cfg {String} footer Footer text
25637  * @cfg {String} fhref Footer href
25638  * 
25639  * @constructor
25640  * Create a new NumberBox
25641  * @param {Object} config The config object
25642  */
25643
25644
25645 Roo.bootstrap.dash.NumberBox = function(config){
25646     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25647     
25648 };
25649
25650 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25651     
25652     headline : '',
25653     content : '',
25654     icon : '',
25655     footer : '',
25656     fhref : '',
25657     ficon : '',
25658     
25659     getAutoCreate : function(){
25660         
25661         var cfg = {
25662             tag : 'div',
25663             cls : 'small-box ',
25664             cn : [
25665                 {
25666                     tag : 'div',
25667                     cls : 'inner',
25668                     cn :[
25669                         {
25670                             tag : 'h3',
25671                             cls : 'roo-headline',
25672                             html : this.headline
25673                         },
25674                         {
25675                             tag : 'p',
25676                             cls : 'roo-content',
25677                             html : this.content
25678                         }
25679                     ]
25680                 }
25681             ]
25682         };
25683         
25684         if(this.icon){
25685             cfg.cn.push({
25686                 tag : 'div',
25687                 cls : 'icon',
25688                 cn :[
25689                     {
25690                         tag : 'i',
25691                         cls : 'ion ' + this.icon
25692                     }
25693                 ]
25694             });
25695         }
25696         
25697         if(this.footer){
25698             var footer = {
25699                 tag : 'a',
25700                 cls : 'small-box-footer',
25701                 href : this.fhref || '#',
25702                 html : this.footer
25703             };
25704             
25705             cfg.cn.push(footer);
25706             
25707         }
25708         
25709         return  cfg;
25710     },
25711
25712     onRender : function(ct,position){
25713         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25714
25715
25716        
25717                 
25718     },
25719
25720     setHeadline: function (value)
25721     {
25722         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25723     },
25724     
25725     setFooter: function (value, href)
25726     {
25727         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25728         
25729         if(href){
25730             this.el.select('a.small-box-footer',true).first().attr('href', href);
25731         }
25732         
25733     },
25734
25735     setContent: function (value)
25736     {
25737         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25738     },
25739
25740     initEvents: function() 
25741     {   
25742         
25743     }
25744     
25745 });
25746
25747  
25748 /*
25749  * - LGPL
25750  *
25751  * TabBox
25752  * 
25753  */
25754 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25755
25756 /**
25757  * @class Roo.bootstrap.dash.TabBox
25758  * @extends Roo.bootstrap.Component
25759  * Bootstrap TabBox class
25760  * @cfg {String} title Title of the TabBox
25761  * @cfg {String} icon Icon of the TabBox
25762  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25763  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25764  * 
25765  * @constructor
25766  * Create a new TabBox
25767  * @param {Object} config The config object
25768  */
25769
25770
25771 Roo.bootstrap.dash.TabBox = function(config){
25772     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25773     this.addEvents({
25774         // raw events
25775         /**
25776          * @event addpane
25777          * When a pane is added
25778          * @param {Roo.bootstrap.dash.TabPane} pane
25779          */
25780         "addpane" : true,
25781         /**
25782          * @event activatepane
25783          * When a pane is activated
25784          * @param {Roo.bootstrap.dash.TabPane} pane
25785          */
25786         "activatepane" : true
25787         
25788          
25789     });
25790     
25791     this.panes = [];
25792 };
25793
25794 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25795
25796     title : '',
25797     icon : false,
25798     showtabs : true,
25799     tabScrollable : false,
25800     
25801     getChildContainer : function()
25802     {
25803         return this.el.select('.tab-content', true).first();
25804     },
25805     
25806     getAutoCreate : function(){
25807         
25808         var header = {
25809             tag: 'li',
25810             cls: 'pull-left header',
25811             html: this.title,
25812             cn : []
25813         };
25814         
25815         if(this.icon){
25816             header.cn.push({
25817                 tag: 'i',
25818                 cls: 'fa ' + this.icon
25819             });
25820         }
25821         
25822         var h = {
25823             tag: 'ul',
25824             cls: 'nav nav-tabs pull-right',
25825             cn: [
25826                 header
25827             ]
25828         };
25829         
25830         if(this.tabScrollable){
25831             h = {
25832                 tag: 'div',
25833                 cls: 'tab-header',
25834                 cn: [
25835                     {
25836                         tag: 'ul',
25837                         cls: 'nav nav-tabs pull-right',
25838                         cn: [
25839                             header
25840                         ]
25841                     }
25842                 ]
25843             };
25844         }
25845         
25846         var cfg = {
25847             tag: 'div',
25848             cls: 'nav-tabs-custom',
25849             cn: [
25850                 h,
25851                 {
25852                     tag: 'div',
25853                     cls: 'tab-content no-padding',
25854                     cn: []
25855                 }
25856             ]
25857         };
25858
25859         return  cfg;
25860     },
25861     initEvents : function()
25862     {
25863         //Roo.log('add add pane handler');
25864         this.on('addpane', this.onAddPane, this);
25865     },
25866      /**
25867      * Updates the box title
25868      * @param {String} html to set the title to.
25869      */
25870     setTitle : function(value)
25871     {
25872         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25873     },
25874     onAddPane : function(pane)
25875     {
25876         this.panes.push(pane);
25877         //Roo.log('addpane');
25878         //Roo.log(pane);
25879         // tabs are rendere left to right..
25880         if(!this.showtabs){
25881             return;
25882         }
25883         
25884         var ctr = this.el.select('.nav-tabs', true).first();
25885          
25886          
25887         var existing = ctr.select('.nav-tab',true);
25888         var qty = existing.getCount();;
25889         
25890         
25891         var tab = ctr.createChild({
25892             tag : 'li',
25893             cls : 'nav-tab' + (qty ? '' : ' active'),
25894             cn : [
25895                 {
25896                     tag : 'a',
25897                     href:'#',
25898                     html : pane.title
25899                 }
25900             ]
25901         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25902         pane.tab = tab;
25903         
25904         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25905         if (!qty) {
25906             pane.el.addClass('active');
25907         }
25908         
25909                 
25910     },
25911     onTabClick : function(ev,un,ob,pane)
25912     {
25913         //Roo.log('tab - prev default');
25914         ev.preventDefault();
25915         
25916         
25917         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25918         pane.tab.addClass('active');
25919         //Roo.log(pane.title);
25920         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25921         // technically we should have a deactivate event.. but maybe add later.
25922         // and it should not de-activate the selected tab...
25923         this.fireEvent('activatepane', pane);
25924         pane.el.addClass('active');
25925         pane.fireEvent('activate');
25926         
25927         
25928     },
25929     
25930     getActivePane : function()
25931     {
25932         var r = false;
25933         Roo.each(this.panes, function(p) {
25934             if(p.el.hasClass('active')){
25935                 r = p;
25936                 return false;
25937             }
25938             
25939             return;
25940         });
25941         
25942         return r;
25943     }
25944     
25945     
25946 });
25947
25948  
25949 /*
25950  * - LGPL
25951  *
25952  * Tab pane
25953  * 
25954  */
25955 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25956 /**
25957  * @class Roo.bootstrap.TabPane
25958  * @extends Roo.bootstrap.Component
25959  * Bootstrap TabPane class
25960  * @cfg {Boolean} active (false | true) Default false
25961  * @cfg {String} title title of panel
25962
25963  * 
25964  * @constructor
25965  * Create a new TabPane
25966  * @param {Object} config The config object
25967  */
25968
25969 Roo.bootstrap.dash.TabPane = function(config){
25970     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25971     
25972     this.addEvents({
25973         // raw events
25974         /**
25975          * @event activate
25976          * When a pane is activated
25977          * @param {Roo.bootstrap.dash.TabPane} pane
25978          */
25979         "activate" : true
25980          
25981     });
25982 };
25983
25984 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25985     
25986     active : false,
25987     title : '',
25988     
25989     // the tabBox that this is attached to.
25990     tab : false,
25991      
25992     getAutoCreate : function() 
25993     {
25994         var cfg = {
25995             tag: 'div',
25996             cls: 'tab-pane'
25997         };
25998         
25999         if(this.active){
26000             cfg.cls += ' active';
26001         }
26002         
26003         return cfg;
26004     },
26005     initEvents  : function()
26006     {
26007         //Roo.log('trigger add pane handler');
26008         this.parent().fireEvent('addpane', this)
26009     },
26010     
26011      /**
26012      * Updates the tab title 
26013      * @param {String} html to set the title to.
26014      */
26015     setTitle: function(str)
26016     {
26017         if (!this.tab) {
26018             return;
26019         }
26020         this.title = str;
26021         this.tab.select('a', true).first().dom.innerHTML = str;
26022         
26023     }
26024     
26025     
26026     
26027 });
26028
26029  
26030
26031
26032  /*
26033  * - LGPL
26034  *
26035  * menu
26036  * 
26037  */
26038 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26039
26040 /**
26041  * @class Roo.bootstrap.menu.Menu
26042  * @extends Roo.bootstrap.Component
26043  * Bootstrap Menu class - container for Menu
26044  * @cfg {String} html Text of the menu
26045  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
26046  * @cfg {String} icon Font awesome icon
26047  * @cfg {String} pos Menu align to (top | bottom) default bottom
26048  * 
26049  * 
26050  * @constructor
26051  * Create a new Menu
26052  * @param {Object} config The config object
26053  */
26054
26055
26056 Roo.bootstrap.menu.Menu = function(config){
26057     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
26058     
26059     this.addEvents({
26060         /**
26061          * @event beforeshow
26062          * Fires before this menu is displayed
26063          * @param {Roo.bootstrap.menu.Menu} this
26064          */
26065         beforeshow : true,
26066         /**
26067          * @event beforehide
26068          * Fires before this menu is hidden
26069          * @param {Roo.bootstrap.menu.Menu} this
26070          */
26071         beforehide : true,
26072         /**
26073          * @event show
26074          * Fires after this menu is displayed
26075          * @param {Roo.bootstrap.menu.Menu} this
26076          */
26077         show : true,
26078         /**
26079          * @event hide
26080          * Fires after this menu is hidden
26081          * @param {Roo.bootstrap.menu.Menu} this
26082          */
26083         hide : true,
26084         /**
26085          * @event click
26086          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
26087          * @param {Roo.bootstrap.menu.Menu} this
26088          * @param {Roo.EventObject} e
26089          */
26090         click : true
26091     });
26092     
26093 };
26094
26095 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
26096     
26097     submenu : false,
26098     html : '',
26099     weight : 'default',
26100     icon : false,
26101     pos : 'bottom',
26102     
26103     
26104     getChildContainer : function() {
26105         if(this.isSubMenu){
26106             return this.el;
26107         }
26108         
26109         return this.el.select('ul.dropdown-menu', true).first();  
26110     },
26111     
26112     getAutoCreate : function()
26113     {
26114         var text = [
26115             {
26116                 tag : 'span',
26117                 cls : 'roo-menu-text',
26118                 html : this.html
26119             }
26120         ];
26121         
26122         if(this.icon){
26123             text.unshift({
26124                 tag : 'i',
26125                 cls : 'fa ' + this.icon
26126             })
26127         }
26128         
26129         
26130         var cfg = {
26131             tag : 'div',
26132             cls : 'btn-group',
26133             cn : [
26134                 {
26135                     tag : 'button',
26136                     cls : 'dropdown-button btn btn-' + this.weight,
26137                     cn : text
26138                 },
26139                 {
26140                     tag : 'button',
26141                     cls : 'dropdown-toggle btn btn-' + this.weight,
26142                     cn : [
26143                         {
26144                             tag : 'span',
26145                             cls : 'caret'
26146                         }
26147                     ]
26148                 },
26149                 {
26150                     tag : 'ul',
26151                     cls : 'dropdown-menu'
26152                 }
26153             ]
26154             
26155         };
26156         
26157         if(this.pos == 'top'){
26158             cfg.cls += ' dropup';
26159         }
26160         
26161         if(this.isSubMenu){
26162             cfg = {
26163                 tag : 'ul',
26164                 cls : 'dropdown-menu'
26165             }
26166         }
26167         
26168         return cfg;
26169     },
26170     
26171     onRender : function(ct, position)
26172     {
26173         this.isSubMenu = ct.hasClass('dropdown-submenu');
26174         
26175         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
26176     },
26177     
26178     initEvents : function() 
26179     {
26180         if(this.isSubMenu){
26181             return;
26182         }
26183         
26184         this.hidden = true;
26185         
26186         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
26187         this.triggerEl.on('click', this.onTriggerPress, this);
26188         
26189         this.buttonEl = this.el.select('button.dropdown-button', true).first();
26190         this.buttonEl.on('click', this.onClick, this);
26191         
26192     },
26193     
26194     list : function()
26195     {
26196         if(this.isSubMenu){
26197             return this.el;
26198         }
26199         
26200         return this.el.select('ul.dropdown-menu', true).first();
26201     },
26202     
26203     onClick : function(e)
26204     {
26205         this.fireEvent("click", this, e);
26206     },
26207     
26208     onTriggerPress  : function(e)
26209     {   
26210         if (this.isVisible()) {
26211             this.hide();
26212         } else {
26213             this.show();
26214         }
26215     },
26216     
26217     isVisible : function(){
26218         return !this.hidden;
26219     },
26220     
26221     show : function()
26222     {
26223         this.fireEvent("beforeshow", this);
26224         
26225         this.hidden = false;
26226         this.el.addClass('open');
26227         
26228         Roo.get(document).on("mouseup", this.onMouseUp, this);
26229         
26230         this.fireEvent("show", this);
26231         
26232         
26233     },
26234     
26235     hide : function()
26236     {
26237         this.fireEvent("beforehide", this);
26238         
26239         this.hidden = true;
26240         this.el.removeClass('open');
26241         
26242         Roo.get(document).un("mouseup", this.onMouseUp);
26243         
26244         this.fireEvent("hide", this);
26245     },
26246     
26247     onMouseUp : function()
26248     {
26249         this.hide();
26250     }
26251     
26252 });
26253
26254  
26255  /*
26256  * - LGPL
26257  *
26258  * menu item
26259  * 
26260  */
26261 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26262
26263 /**
26264  * @class Roo.bootstrap.menu.Item
26265  * @extends Roo.bootstrap.Component
26266  * Bootstrap MenuItem class
26267  * @cfg {Boolean} submenu (true | false) default false
26268  * @cfg {String} html text of the item
26269  * @cfg {String} href the link
26270  * @cfg {Boolean} disable (true | false) default false
26271  * @cfg {Boolean} preventDefault (true | false) default true
26272  * @cfg {String} icon Font awesome icon
26273  * @cfg {String} pos Submenu align to (left | right) default right 
26274  * 
26275  * 
26276  * @constructor
26277  * Create a new Item
26278  * @param {Object} config The config object
26279  */
26280
26281
26282 Roo.bootstrap.menu.Item = function(config){
26283     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26284     this.addEvents({
26285         /**
26286          * @event mouseover
26287          * Fires when the mouse is hovering over this menu
26288          * @param {Roo.bootstrap.menu.Item} this
26289          * @param {Roo.EventObject} e
26290          */
26291         mouseover : true,
26292         /**
26293          * @event mouseout
26294          * Fires when the mouse exits this menu
26295          * @param {Roo.bootstrap.menu.Item} this
26296          * @param {Roo.EventObject} e
26297          */
26298         mouseout : true,
26299         // raw events
26300         /**
26301          * @event click
26302          * The raw click event for the entire grid.
26303          * @param {Roo.EventObject} e
26304          */
26305         click : true
26306     });
26307 };
26308
26309 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26310     
26311     submenu : false,
26312     href : '',
26313     html : '',
26314     preventDefault: true,
26315     disable : false,
26316     icon : false,
26317     pos : 'right',
26318     
26319     getAutoCreate : function()
26320     {
26321         var text = [
26322             {
26323                 tag : 'span',
26324                 cls : 'roo-menu-item-text',
26325                 html : this.html
26326             }
26327         ];
26328         
26329         if(this.icon){
26330             text.unshift({
26331                 tag : 'i',
26332                 cls : 'fa ' + this.icon
26333             })
26334         }
26335         
26336         var cfg = {
26337             tag : 'li',
26338             cn : [
26339                 {
26340                     tag : 'a',
26341                     href : this.href || '#',
26342                     cn : text
26343                 }
26344             ]
26345         };
26346         
26347         if(this.disable){
26348             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26349         }
26350         
26351         if(this.submenu){
26352             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26353             
26354             if(this.pos == 'left'){
26355                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26356             }
26357         }
26358         
26359         return cfg;
26360     },
26361     
26362     initEvents : function() 
26363     {
26364         this.el.on('mouseover', this.onMouseOver, this);
26365         this.el.on('mouseout', this.onMouseOut, this);
26366         
26367         this.el.select('a', true).first().on('click', this.onClick, this);
26368         
26369     },
26370     
26371     onClick : function(e)
26372     {
26373         if(this.preventDefault){
26374             e.preventDefault();
26375         }
26376         
26377         this.fireEvent("click", this, e);
26378     },
26379     
26380     onMouseOver : function(e)
26381     {
26382         if(this.submenu && this.pos == 'left'){
26383             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26384         }
26385         
26386         this.fireEvent("mouseover", this, e);
26387     },
26388     
26389     onMouseOut : function(e)
26390     {
26391         this.fireEvent("mouseout", this, e);
26392     }
26393 });
26394
26395  
26396
26397  /*
26398  * - LGPL
26399  *
26400  * menu separator
26401  * 
26402  */
26403 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26404
26405 /**
26406  * @class Roo.bootstrap.menu.Separator
26407  * @extends Roo.bootstrap.Component
26408  * Bootstrap Separator class
26409  * 
26410  * @constructor
26411  * Create a new Separator
26412  * @param {Object} config The config object
26413  */
26414
26415
26416 Roo.bootstrap.menu.Separator = function(config){
26417     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26418 };
26419
26420 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26421     
26422     getAutoCreate : function(){
26423         var cfg = {
26424             tag : 'li',
26425             cls: 'divider'
26426         };
26427         
26428         return cfg;
26429     }
26430    
26431 });
26432
26433  
26434
26435  /*
26436  * - LGPL
26437  *
26438  * Tooltip
26439  * 
26440  */
26441
26442 /**
26443  * @class Roo.bootstrap.Tooltip
26444  * Bootstrap Tooltip class
26445  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26446  * to determine which dom element triggers the tooltip.
26447  * 
26448  * It needs to add support for additional attributes like tooltip-position
26449  * 
26450  * @constructor
26451  * Create a new Toolti
26452  * @param {Object} config The config object
26453  */
26454
26455 Roo.bootstrap.Tooltip = function(config){
26456     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26457     
26458     this.alignment = Roo.bootstrap.Tooltip.alignment;
26459     
26460     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26461         this.alignment = config.alignment;
26462     }
26463     
26464 };
26465
26466 Roo.apply(Roo.bootstrap.Tooltip, {
26467     /**
26468      * @function init initialize tooltip monitoring.
26469      * @static
26470      */
26471     currentEl : false,
26472     currentTip : false,
26473     currentRegion : false,
26474     
26475     //  init : delay?
26476     
26477     init : function()
26478     {
26479         Roo.get(document).on('mouseover', this.enter ,this);
26480         Roo.get(document).on('mouseout', this.leave, this);
26481          
26482         
26483         this.currentTip = new Roo.bootstrap.Tooltip();
26484     },
26485     
26486     enter : function(ev)
26487     {
26488         var dom = ev.getTarget();
26489         
26490         //Roo.log(['enter',dom]);
26491         var el = Roo.fly(dom);
26492         if (this.currentEl) {
26493             //Roo.log(dom);
26494             //Roo.log(this.currentEl);
26495             //Roo.log(this.currentEl.contains(dom));
26496             if (this.currentEl == el) {
26497                 return;
26498             }
26499             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26500                 return;
26501             }
26502
26503         }
26504         
26505         if (this.currentTip.el) {
26506             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26507         }    
26508         //Roo.log(ev);
26509         
26510         if(!el || el.dom == document){
26511             return;
26512         }
26513         
26514         var bindEl = el;
26515         
26516         // you can not look for children, as if el is the body.. then everythign is the child..
26517         if (!el.attr('tooltip')) { //
26518             if (!el.select("[tooltip]").elements.length) {
26519                 return;
26520             }
26521             // is the mouse over this child...?
26522             bindEl = el.select("[tooltip]").first();
26523             var xy = ev.getXY();
26524             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26525                 //Roo.log("not in region.");
26526                 return;
26527             }
26528             //Roo.log("child element over..");
26529             
26530         }
26531         this.currentEl = bindEl;
26532         this.currentTip.bind(bindEl);
26533         this.currentRegion = Roo.lib.Region.getRegion(dom);
26534         this.currentTip.enter();
26535         
26536     },
26537     leave : function(ev)
26538     {
26539         var dom = ev.getTarget();
26540         //Roo.log(['leave',dom]);
26541         if (!this.currentEl) {
26542             return;
26543         }
26544         
26545         
26546         if (dom != this.currentEl.dom) {
26547             return;
26548         }
26549         var xy = ev.getXY();
26550         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26551             return;
26552         }
26553         // only activate leave if mouse cursor is outside... bounding box..
26554         
26555         
26556         
26557         
26558         if (this.currentTip) {
26559             this.currentTip.leave();
26560         }
26561         //Roo.log('clear currentEl');
26562         this.currentEl = false;
26563         
26564         
26565     },
26566     alignment : {
26567         'left' : ['r-l', [-2,0], 'right'],
26568         'right' : ['l-r', [2,0], 'left'],
26569         'bottom' : ['t-b', [0,2], 'top'],
26570         'top' : [ 'b-t', [0,-2], 'bottom']
26571     }
26572     
26573 });
26574
26575
26576 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26577     
26578     
26579     bindEl : false,
26580     
26581     delay : null, // can be { show : 300 , hide: 500}
26582     
26583     timeout : null,
26584     
26585     hoverState : null, //???
26586     
26587     placement : 'bottom', 
26588     
26589     alignment : false,
26590     
26591     getAutoCreate : function(){
26592     
26593         var cfg = {
26594            cls : 'tooltip',
26595            role : 'tooltip',
26596            cn : [
26597                 {
26598                     cls : 'tooltip-arrow'
26599                 },
26600                 {
26601                     cls : 'tooltip-inner'
26602                 }
26603            ]
26604         };
26605         
26606         return cfg;
26607     },
26608     bind : function(el)
26609     {
26610         this.bindEl = el;
26611     },
26612       
26613     
26614     enter : function () {
26615        
26616         if (this.timeout != null) {
26617             clearTimeout(this.timeout);
26618         }
26619         
26620         this.hoverState = 'in';
26621          //Roo.log("enter - show");
26622         if (!this.delay || !this.delay.show) {
26623             this.show();
26624             return;
26625         }
26626         var _t = this;
26627         this.timeout = setTimeout(function () {
26628             if (_t.hoverState == 'in') {
26629                 _t.show();
26630             }
26631         }, this.delay.show);
26632     },
26633     leave : function()
26634     {
26635         clearTimeout(this.timeout);
26636     
26637         this.hoverState = 'out';
26638          if (!this.delay || !this.delay.hide) {
26639             this.hide();
26640             return;
26641         }
26642        
26643         var _t = this;
26644         this.timeout = setTimeout(function () {
26645             //Roo.log("leave - timeout");
26646             
26647             if (_t.hoverState == 'out') {
26648                 _t.hide();
26649                 Roo.bootstrap.Tooltip.currentEl = false;
26650             }
26651         }, delay);
26652     },
26653     
26654     show : function (msg)
26655     {
26656         if (!this.el) {
26657             this.render(document.body);
26658         }
26659         // set content.
26660         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26661         
26662         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26663         
26664         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26665         
26666         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26667         
26668         var placement = typeof this.placement == 'function' ?
26669             this.placement.call(this, this.el, on_el) :
26670             this.placement;
26671             
26672         var autoToken = /\s?auto?\s?/i;
26673         var autoPlace = autoToken.test(placement);
26674         if (autoPlace) {
26675             placement = placement.replace(autoToken, '') || 'top';
26676         }
26677         
26678         //this.el.detach()
26679         //this.el.setXY([0,0]);
26680         this.el.show();
26681         //this.el.dom.style.display='block';
26682         
26683         //this.el.appendTo(on_el);
26684         
26685         var p = this.getPosition();
26686         var box = this.el.getBox();
26687         
26688         if (autoPlace) {
26689             // fixme..
26690         }
26691         
26692         var align = this.alignment[placement];
26693         
26694         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26695         
26696         if(placement == 'top' || placement == 'bottom'){
26697             if(xy[0] < 0){
26698                 placement = 'right';
26699             }
26700             
26701             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26702                 placement = 'left';
26703             }
26704             
26705             var scroll = Roo.select('body', true).first().getScroll();
26706             
26707             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26708                 placement = 'top';
26709             }
26710             
26711             align = this.alignment[placement];
26712         }
26713         
26714         this.el.alignTo(this.bindEl, align[0],align[1]);
26715         //var arrow = this.el.select('.arrow',true).first();
26716         //arrow.set(align[2], 
26717         
26718         this.el.addClass(placement);
26719         
26720         this.el.addClass('in fade');
26721         
26722         this.hoverState = null;
26723         
26724         if (this.el.hasClass('fade')) {
26725             // fade it?
26726         }
26727         
26728     },
26729     hide : function()
26730     {
26731          
26732         if (!this.el) {
26733             return;
26734         }
26735         //this.el.setXY([0,0]);
26736         this.el.removeClass('in');
26737         //this.el.hide();
26738         
26739     }
26740     
26741 });
26742  
26743
26744  /*
26745  * - LGPL
26746  *
26747  * Location Picker
26748  * 
26749  */
26750
26751 /**
26752  * @class Roo.bootstrap.LocationPicker
26753  * @extends Roo.bootstrap.Component
26754  * Bootstrap LocationPicker class
26755  * @cfg {Number} latitude Position when init default 0
26756  * @cfg {Number} longitude Position when init default 0
26757  * @cfg {Number} zoom default 15
26758  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26759  * @cfg {Boolean} mapTypeControl default false
26760  * @cfg {Boolean} disableDoubleClickZoom default false
26761  * @cfg {Boolean} scrollwheel default true
26762  * @cfg {Boolean} streetViewControl default false
26763  * @cfg {Number} radius default 0
26764  * @cfg {String} locationName
26765  * @cfg {Boolean} draggable default true
26766  * @cfg {Boolean} enableAutocomplete default false
26767  * @cfg {Boolean} enableReverseGeocode default true
26768  * @cfg {String} markerTitle
26769  * 
26770  * @constructor
26771  * Create a new LocationPicker
26772  * @param {Object} config The config object
26773  */
26774
26775
26776 Roo.bootstrap.LocationPicker = function(config){
26777     
26778     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26779     
26780     this.addEvents({
26781         /**
26782          * @event initial
26783          * Fires when the picker initialized.
26784          * @param {Roo.bootstrap.LocationPicker} this
26785          * @param {Google Location} location
26786          */
26787         initial : true,
26788         /**
26789          * @event positionchanged
26790          * Fires when the picker position changed.
26791          * @param {Roo.bootstrap.LocationPicker} this
26792          * @param {Google Location} location
26793          */
26794         positionchanged : true,
26795         /**
26796          * @event resize
26797          * Fires when the map resize.
26798          * @param {Roo.bootstrap.LocationPicker} this
26799          */
26800         resize : true,
26801         /**
26802          * @event show
26803          * Fires when the map show.
26804          * @param {Roo.bootstrap.LocationPicker} this
26805          */
26806         show : true,
26807         /**
26808          * @event hide
26809          * Fires when the map hide.
26810          * @param {Roo.bootstrap.LocationPicker} this
26811          */
26812         hide : true,
26813         /**
26814          * @event mapClick
26815          * Fires when click the map.
26816          * @param {Roo.bootstrap.LocationPicker} this
26817          * @param {Map event} e
26818          */
26819         mapClick : true,
26820         /**
26821          * @event mapRightClick
26822          * Fires when right click the map.
26823          * @param {Roo.bootstrap.LocationPicker} this
26824          * @param {Map event} e
26825          */
26826         mapRightClick : true,
26827         /**
26828          * @event markerClick
26829          * Fires when click the marker.
26830          * @param {Roo.bootstrap.LocationPicker} this
26831          * @param {Map event} e
26832          */
26833         markerClick : true,
26834         /**
26835          * @event markerRightClick
26836          * Fires when right click the marker.
26837          * @param {Roo.bootstrap.LocationPicker} this
26838          * @param {Map event} e
26839          */
26840         markerRightClick : true,
26841         /**
26842          * @event OverlayViewDraw
26843          * Fires when OverlayView Draw
26844          * @param {Roo.bootstrap.LocationPicker} this
26845          */
26846         OverlayViewDraw : true,
26847         /**
26848          * @event OverlayViewOnAdd
26849          * Fires when OverlayView Draw
26850          * @param {Roo.bootstrap.LocationPicker} this
26851          */
26852         OverlayViewOnAdd : true,
26853         /**
26854          * @event OverlayViewOnRemove
26855          * Fires when OverlayView Draw
26856          * @param {Roo.bootstrap.LocationPicker} this
26857          */
26858         OverlayViewOnRemove : true,
26859         /**
26860          * @event OverlayViewShow
26861          * Fires when OverlayView Draw
26862          * @param {Roo.bootstrap.LocationPicker} this
26863          * @param {Pixel} cpx
26864          */
26865         OverlayViewShow : true,
26866         /**
26867          * @event OverlayViewHide
26868          * Fires when OverlayView Draw
26869          * @param {Roo.bootstrap.LocationPicker} this
26870          */
26871         OverlayViewHide : true,
26872         /**
26873          * @event loadexception
26874          * Fires when load google lib failed.
26875          * @param {Roo.bootstrap.LocationPicker} this
26876          */
26877         loadexception : true
26878     });
26879         
26880 };
26881
26882 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26883     
26884     gMapContext: false,
26885     
26886     latitude: 0,
26887     longitude: 0,
26888     zoom: 15,
26889     mapTypeId: false,
26890     mapTypeControl: false,
26891     disableDoubleClickZoom: false,
26892     scrollwheel: true,
26893     streetViewControl: false,
26894     radius: 0,
26895     locationName: '',
26896     draggable: true,
26897     enableAutocomplete: false,
26898     enableReverseGeocode: true,
26899     markerTitle: '',
26900     
26901     getAutoCreate: function()
26902     {
26903
26904         var cfg = {
26905             tag: 'div',
26906             cls: 'roo-location-picker'
26907         };
26908         
26909         return cfg
26910     },
26911     
26912     initEvents: function(ct, position)
26913     {       
26914         if(!this.el.getWidth() || this.isApplied()){
26915             return;
26916         }
26917         
26918         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26919         
26920         this.initial();
26921     },
26922     
26923     initial: function()
26924     {
26925         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26926             this.fireEvent('loadexception', this);
26927             return;
26928         }
26929         
26930         if(!this.mapTypeId){
26931             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26932         }
26933         
26934         this.gMapContext = this.GMapContext();
26935         
26936         this.initOverlayView();
26937         
26938         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26939         
26940         var _this = this;
26941                 
26942         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26943             _this.setPosition(_this.gMapContext.marker.position);
26944         });
26945         
26946         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26947             _this.fireEvent('mapClick', this, event);
26948             
26949         });
26950
26951         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26952             _this.fireEvent('mapRightClick', this, event);
26953             
26954         });
26955         
26956         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26957             _this.fireEvent('markerClick', this, event);
26958             
26959         });
26960
26961         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26962             _this.fireEvent('markerRightClick', this, event);
26963             
26964         });
26965         
26966         this.setPosition(this.gMapContext.location);
26967         
26968         this.fireEvent('initial', this, this.gMapContext.location);
26969     },
26970     
26971     initOverlayView: function()
26972     {
26973         var _this = this;
26974         
26975         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26976             
26977             draw: function()
26978             {
26979                 _this.fireEvent('OverlayViewDraw', _this);
26980             },
26981             
26982             onAdd: function()
26983             {
26984                 _this.fireEvent('OverlayViewOnAdd', _this);
26985             },
26986             
26987             onRemove: function()
26988             {
26989                 _this.fireEvent('OverlayViewOnRemove', _this);
26990             },
26991             
26992             show: function(cpx)
26993             {
26994                 _this.fireEvent('OverlayViewShow', _this, cpx);
26995             },
26996             
26997             hide: function()
26998             {
26999                 _this.fireEvent('OverlayViewHide', _this);
27000             }
27001             
27002         });
27003     },
27004     
27005     fromLatLngToContainerPixel: function(event)
27006     {
27007         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
27008     },
27009     
27010     isApplied: function() 
27011     {
27012         return this.getGmapContext() == false ? false : true;
27013     },
27014     
27015     getGmapContext: function() 
27016     {
27017         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
27018     },
27019     
27020     GMapContext: function() 
27021     {
27022         var position = new google.maps.LatLng(this.latitude, this.longitude);
27023         
27024         var _map = new google.maps.Map(this.el.dom, {
27025             center: position,
27026             zoom: this.zoom,
27027             mapTypeId: this.mapTypeId,
27028             mapTypeControl: this.mapTypeControl,
27029             disableDoubleClickZoom: this.disableDoubleClickZoom,
27030             scrollwheel: this.scrollwheel,
27031             streetViewControl: this.streetViewControl,
27032             locationName: this.locationName,
27033             draggable: this.draggable,
27034             enableAutocomplete: this.enableAutocomplete,
27035             enableReverseGeocode: this.enableReverseGeocode
27036         });
27037         
27038         var _marker = new google.maps.Marker({
27039             position: position,
27040             map: _map,
27041             title: this.markerTitle,
27042             draggable: this.draggable
27043         });
27044         
27045         return {
27046             map: _map,
27047             marker: _marker,
27048             circle: null,
27049             location: position,
27050             radius: this.radius,
27051             locationName: this.locationName,
27052             addressComponents: {
27053                 formatted_address: null,
27054                 addressLine1: null,
27055                 addressLine2: null,
27056                 streetName: null,
27057                 streetNumber: null,
27058                 city: null,
27059                 district: null,
27060                 state: null,
27061                 stateOrProvince: null
27062             },
27063             settings: this,
27064             domContainer: this.el.dom,
27065             geodecoder: new google.maps.Geocoder()
27066         };
27067     },
27068     
27069     drawCircle: function(center, radius, options) 
27070     {
27071         if (this.gMapContext.circle != null) {
27072             this.gMapContext.circle.setMap(null);
27073         }
27074         if (radius > 0) {
27075             radius *= 1;
27076             options = Roo.apply({}, options, {
27077                 strokeColor: "#0000FF",
27078                 strokeOpacity: .35,
27079                 strokeWeight: 2,
27080                 fillColor: "#0000FF",
27081                 fillOpacity: .2
27082             });
27083             
27084             options.map = this.gMapContext.map;
27085             options.radius = radius;
27086             options.center = center;
27087             this.gMapContext.circle = new google.maps.Circle(options);
27088             return this.gMapContext.circle;
27089         }
27090         
27091         return null;
27092     },
27093     
27094     setPosition: function(location) 
27095     {
27096         this.gMapContext.location = location;
27097         this.gMapContext.marker.setPosition(location);
27098         this.gMapContext.map.panTo(location);
27099         this.drawCircle(location, this.gMapContext.radius, {});
27100         
27101         var _this = this;
27102         
27103         if (this.gMapContext.settings.enableReverseGeocode) {
27104             this.gMapContext.geodecoder.geocode({
27105                 latLng: this.gMapContext.location
27106             }, function(results, status) {
27107                 
27108                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
27109                     _this.gMapContext.locationName = results[0].formatted_address;
27110                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
27111                     
27112                     _this.fireEvent('positionchanged', this, location);
27113                 }
27114             });
27115             
27116             return;
27117         }
27118         
27119         this.fireEvent('positionchanged', this, location);
27120     },
27121     
27122     resize: function()
27123     {
27124         google.maps.event.trigger(this.gMapContext.map, "resize");
27125         
27126         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
27127         
27128         this.fireEvent('resize', this);
27129     },
27130     
27131     setPositionByLatLng: function(latitude, longitude)
27132     {
27133         this.setPosition(new google.maps.LatLng(latitude, longitude));
27134     },
27135     
27136     getCurrentPosition: function() 
27137     {
27138         return {
27139             latitude: this.gMapContext.location.lat(),
27140             longitude: this.gMapContext.location.lng()
27141         };
27142     },
27143     
27144     getAddressName: function() 
27145     {
27146         return this.gMapContext.locationName;
27147     },
27148     
27149     getAddressComponents: function() 
27150     {
27151         return this.gMapContext.addressComponents;
27152     },
27153     
27154     address_component_from_google_geocode: function(address_components) 
27155     {
27156         var result = {};
27157         
27158         for (var i = 0; i < address_components.length; i++) {
27159             var component = address_components[i];
27160             if (component.types.indexOf("postal_code") >= 0) {
27161                 result.postalCode = component.short_name;
27162             } else if (component.types.indexOf("street_number") >= 0) {
27163                 result.streetNumber = component.short_name;
27164             } else if (component.types.indexOf("route") >= 0) {
27165                 result.streetName = component.short_name;
27166             } else if (component.types.indexOf("neighborhood") >= 0) {
27167                 result.city = component.short_name;
27168             } else if (component.types.indexOf("locality") >= 0) {
27169                 result.city = component.short_name;
27170             } else if (component.types.indexOf("sublocality") >= 0) {
27171                 result.district = component.short_name;
27172             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
27173                 result.stateOrProvince = component.short_name;
27174             } else if (component.types.indexOf("country") >= 0) {
27175                 result.country = component.short_name;
27176             }
27177         }
27178         
27179         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
27180         result.addressLine2 = "";
27181         return result;
27182     },
27183     
27184     setZoomLevel: function(zoom)
27185     {
27186         this.gMapContext.map.setZoom(zoom);
27187     },
27188     
27189     show: function()
27190     {
27191         if(!this.el){
27192             return;
27193         }
27194         
27195         this.el.show();
27196         
27197         this.resize();
27198         
27199         this.fireEvent('show', this);
27200     },
27201     
27202     hide: function()
27203     {
27204         if(!this.el){
27205             return;
27206         }
27207         
27208         this.el.hide();
27209         
27210         this.fireEvent('hide', this);
27211     }
27212     
27213 });
27214
27215 Roo.apply(Roo.bootstrap.LocationPicker, {
27216     
27217     OverlayView : function(map, options)
27218     {
27219         options = options || {};
27220         
27221         this.setMap(map);
27222     }
27223     
27224     
27225 });/**
27226  * @class Roo.bootstrap.Alert
27227  * @extends Roo.bootstrap.Component
27228  * Bootstrap Alert class - shows an alert area box
27229  * eg
27230  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
27231   Enter a valid email address
27232 </div>
27233  * @licence LGPL
27234  * @cfg {String} title The title of alert
27235  * @cfg {String} html The content of alert
27236  * @cfg {String} weight (  success | info | warning | danger )
27237  * @cfg {String} faicon font-awesomeicon
27238  * 
27239  * @constructor
27240  * Create a new alert
27241  * @param {Object} config The config object
27242  */
27243
27244
27245 Roo.bootstrap.Alert = function(config){
27246     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27247     
27248 };
27249
27250 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
27251     
27252     title: '',
27253     html: '',
27254     weight: false,
27255     faicon: false,
27256     
27257     getAutoCreate : function()
27258     {
27259         
27260         var cfg = {
27261             tag : 'div',
27262             cls : 'alert',
27263             cn : [
27264                 {
27265                     tag : 'i',
27266                     cls : 'roo-alert-icon'
27267                     
27268                 },
27269                 {
27270                     tag : 'b',
27271                     cls : 'roo-alert-title',
27272                     html : this.title
27273                 },
27274                 {
27275                     tag : 'span',
27276                     cls : 'roo-alert-text',
27277                     html : this.html
27278                 }
27279             ]
27280         };
27281         
27282         if(this.faicon){
27283             cfg.cn[0].cls += ' fa ' + this.faicon;
27284         }
27285         
27286         if(this.weight){
27287             cfg.cls += ' alert-' + this.weight;
27288         }
27289         
27290         return cfg;
27291     },
27292     
27293     initEvents: function() 
27294     {
27295         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27296     },
27297     
27298     setTitle : function(str)
27299     {
27300         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27301     },
27302     
27303     setText : function(str)
27304     {
27305         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27306     },
27307     
27308     setWeight : function(weight)
27309     {
27310         if(this.weight){
27311             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27312         }
27313         
27314         this.weight = weight;
27315         
27316         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27317     },
27318     
27319     setIcon : function(icon)
27320     {
27321         if(this.faicon){
27322             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27323         }
27324         
27325         this.faicon = icon;
27326         
27327         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27328     },
27329     
27330     hide: function() 
27331     {
27332         this.el.hide();   
27333     },
27334     
27335     show: function() 
27336     {  
27337         this.el.show();   
27338     }
27339     
27340 });
27341
27342  
27343 /*
27344 * Licence: LGPL
27345 */
27346
27347 /**
27348  * @class Roo.bootstrap.UploadCropbox
27349  * @extends Roo.bootstrap.Component
27350  * Bootstrap UploadCropbox class
27351  * @cfg {String} emptyText show when image has been loaded
27352  * @cfg {String} rotateNotify show when image too small to rotate
27353  * @cfg {Number} errorTimeout default 3000
27354  * @cfg {Number} minWidth default 300
27355  * @cfg {Number} minHeight default 300
27356  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27357  * @cfg {Boolean} isDocument (true|false) default false
27358  * @cfg {String} url action url
27359  * @cfg {String} paramName default 'imageUpload'
27360  * @cfg {String} method default POST
27361  * @cfg {Boolean} loadMask (true|false) default true
27362  * @cfg {Boolean} loadingText default 'Loading...'
27363  * 
27364  * @constructor
27365  * Create a new UploadCropbox
27366  * @param {Object} config The config object
27367  */
27368
27369 Roo.bootstrap.UploadCropbox = function(config){
27370     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27371     
27372     this.addEvents({
27373         /**
27374          * @event beforeselectfile
27375          * Fire before select file
27376          * @param {Roo.bootstrap.UploadCropbox} this
27377          */
27378         "beforeselectfile" : true,
27379         /**
27380          * @event initial
27381          * Fire after initEvent
27382          * @param {Roo.bootstrap.UploadCropbox} this
27383          */
27384         "initial" : true,
27385         /**
27386          * @event crop
27387          * Fire after initEvent
27388          * @param {Roo.bootstrap.UploadCropbox} this
27389          * @param {String} data
27390          */
27391         "crop" : true,
27392         /**
27393          * @event prepare
27394          * Fire when preparing the file data
27395          * @param {Roo.bootstrap.UploadCropbox} this
27396          * @param {Object} file
27397          */
27398         "prepare" : true,
27399         /**
27400          * @event exception
27401          * Fire when get exception
27402          * @param {Roo.bootstrap.UploadCropbox} this
27403          * @param {XMLHttpRequest} xhr
27404          */
27405         "exception" : true,
27406         /**
27407          * @event beforeloadcanvas
27408          * Fire before load the canvas
27409          * @param {Roo.bootstrap.UploadCropbox} this
27410          * @param {String} src
27411          */
27412         "beforeloadcanvas" : true,
27413         /**
27414          * @event trash
27415          * Fire when trash image
27416          * @param {Roo.bootstrap.UploadCropbox} this
27417          */
27418         "trash" : true,
27419         /**
27420          * @event download
27421          * Fire when download the image
27422          * @param {Roo.bootstrap.UploadCropbox} this
27423          */
27424         "download" : true,
27425         /**
27426          * @event footerbuttonclick
27427          * Fire when footerbuttonclick
27428          * @param {Roo.bootstrap.UploadCropbox} this
27429          * @param {String} type
27430          */
27431         "footerbuttonclick" : true,
27432         /**
27433          * @event resize
27434          * Fire when resize
27435          * @param {Roo.bootstrap.UploadCropbox} this
27436          */
27437         "resize" : true,
27438         /**
27439          * @event rotate
27440          * Fire when rotate the image
27441          * @param {Roo.bootstrap.UploadCropbox} this
27442          * @param {String} pos
27443          */
27444         "rotate" : true,
27445         /**
27446          * @event inspect
27447          * Fire when inspect the file
27448          * @param {Roo.bootstrap.UploadCropbox} this
27449          * @param {Object} file
27450          */
27451         "inspect" : true,
27452         /**
27453          * @event upload
27454          * Fire when xhr upload the file
27455          * @param {Roo.bootstrap.UploadCropbox} this
27456          * @param {Object} data
27457          */
27458         "upload" : true,
27459         /**
27460          * @event arrange
27461          * Fire when arrange the file data
27462          * @param {Roo.bootstrap.UploadCropbox} this
27463          * @param {Object} formData
27464          */
27465         "arrange" : true
27466     });
27467     
27468     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27469 };
27470
27471 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27472     
27473     emptyText : 'Click to upload image',
27474     rotateNotify : 'Image is too small to rotate',
27475     errorTimeout : 3000,
27476     scale : 0,
27477     baseScale : 1,
27478     rotate : 0,
27479     dragable : false,
27480     pinching : false,
27481     mouseX : 0,
27482     mouseY : 0,
27483     cropData : false,
27484     minWidth : 300,
27485     minHeight : 300,
27486     file : false,
27487     exif : {},
27488     baseRotate : 1,
27489     cropType : 'image/jpeg',
27490     buttons : false,
27491     canvasLoaded : false,
27492     isDocument : false,
27493     method : 'POST',
27494     paramName : 'imageUpload',
27495     loadMask : true,
27496     loadingText : 'Loading...',
27497     maskEl : false,
27498     
27499     getAutoCreate : function()
27500     {
27501         var cfg = {
27502             tag : 'div',
27503             cls : 'roo-upload-cropbox',
27504             cn : [
27505                 {
27506                     tag : 'input',
27507                     cls : 'roo-upload-cropbox-selector',
27508                     type : 'file'
27509                 },
27510                 {
27511                     tag : 'div',
27512                     cls : 'roo-upload-cropbox-body',
27513                     style : 'cursor:pointer',
27514                     cn : [
27515                         {
27516                             tag : 'div',
27517                             cls : 'roo-upload-cropbox-preview'
27518                         },
27519                         {
27520                             tag : 'div',
27521                             cls : 'roo-upload-cropbox-thumb'
27522                         },
27523                         {
27524                             tag : 'div',
27525                             cls : 'roo-upload-cropbox-empty-notify',
27526                             html : this.emptyText
27527                         },
27528                         {
27529                             tag : 'div',
27530                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27531                             html : this.rotateNotify
27532                         }
27533                     ]
27534                 },
27535                 {
27536                     tag : 'div',
27537                     cls : 'roo-upload-cropbox-footer',
27538                     cn : {
27539                         tag : 'div',
27540                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27541                         cn : []
27542                     }
27543                 }
27544             ]
27545         };
27546         
27547         return cfg;
27548     },
27549     
27550     onRender : function(ct, position)
27551     {
27552         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27553         
27554         if (this.buttons.length) {
27555             
27556             Roo.each(this.buttons, function(bb) {
27557                 
27558                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27559                 
27560                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27561                 
27562             }, this);
27563         }
27564         
27565         if(this.loadMask){
27566             this.maskEl = this.el;
27567         }
27568     },
27569     
27570     initEvents : function()
27571     {
27572         this.urlAPI = (window.createObjectURL && window) || 
27573                                 (window.URL && URL.revokeObjectURL && URL) || 
27574                                 (window.webkitURL && webkitURL);
27575                         
27576         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27577         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27578         
27579         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27580         this.selectorEl.hide();
27581         
27582         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27583         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27584         
27585         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27586         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27587         this.thumbEl.hide();
27588         
27589         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27590         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27591         
27592         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27593         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27594         this.errorEl.hide();
27595         
27596         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27597         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27598         this.footerEl.hide();
27599         
27600         this.setThumbBoxSize();
27601         
27602         this.bind();
27603         
27604         this.resize();
27605         
27606         this.fireEvent('initial', this);
27607     },
27608
27609     bind : function()
27610     {
27611         var _this = this;
27612         
27613         window.addEventListener("resize", function() { _this.resize(); } );
27614         
27615         this.bodyEl.on('click', this.beforeSelectFile, this);
27616         
27617         if(Roo.isTouch){
27618             this.bodyEl.on('touchstart', this.onTouchStart, this);
27619             this.bodyEl.on('touchmove', this.onTouchMove, this);
27620             this.bodyEl.on('touchend', this.onTouchEnd, this);
27621         }
27622         
27623         if(!Roo.isTouch){
27624             this.bodyEl.on('mousedown', this.onMouseDown, this);
27625             this.bodyEl.on('mousemove', this.onMouseMove, this);
27626             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27627             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27628             Roo.get(document).on('mouseup', this.onMouseUp, this);
27629         }
27630         
27631         this.selectorEl.on('change', this.onFileSelected, this);
27632     },
27633     
27634     reset : function()
27635     {    
27636         this.scale = 0;
27637         this.baseScale = 1;
27638         this.rotate = 0;
27639         this.baseRotate = 1;
27640         this.dragable = false;
27641         this.pinching = false;
27642         this.mouseX = 0;
27643         this.mouseY = 0;
27644         this.cropData = false;
27645         this.notifyEl.dom.innerHTML = this.emptyText;
27646         
27647         this.selectorEl.dom.value = '';
27648         
27649     },
27650     
27651     resize : function()
27652     {
27653         if(this.fireEvent('resize', this) != false){
27654             this.setThumbBoxPosition();
27655             this.setCanvasPosition();
27656         }
27657     },
27658     
27659     onFooterButtonClick : function(e, el, o, type)
27660     {
27661         switch (type) {
27662             case 'rotate-left' :
27663                 this.onRotateLeft(e);
27664                 break;
27665             case 'rotate-right' :
27666                 this.onRotateRight(e);
27667                 break;
27668             case 'picture' :
27669                 this.beforeSelectFile(e);
27670                 break;
27671             case 'trash' :
27672                 this.trash(e);
27673                 break;
27674             case 'crop' :
27675                 this.crop(e);
27676                 break;
27677             case 'download' :
27678                 this.download(e);
27679                 break;
27680             default :
27681                 break;
27682         }
27683         
27684         this.fireEvent('footerbuttonclick', this, type);
27685     },
27686     
27687     beforeSelectFile : function(e)
27688     {
27689         e.preventDefault();
27690         
27691         if(this.fireEvent('beforeselectfile', this) != false){
27692             this.selectorEl.dom.click();
27693         }
27694     },
27695     
27696     onFileSelected : function(e)
27697     {
27698         e.preventDefault();
27699         
27700         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27701             return;
27702         }
27703         
27704         var file = this.selectorEl.dom.files[0];
27705         
27706         if(this.fireEvent('inspect', this, file) != false){
27707             this.prepare(file);
27708         }
27709         
27710     },
27711     
27712     trash : function(e)
27713     {
27714         this.fireEvent('trash', this);
27715     },
27716     
27717     download : function(e)
27718     {
27719         this.fireEvent('download', this);
27720     },
27721     
27722     loadCanvas : function(src)
27723     {   
27724         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27725             
27726             this.reset();
27727             
27728             this.imageEl = document.createElement('img');
27729             
27730             var _this = this;
27731             
27732             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27733             
27734             this.imageEl.src = src;
27735         }
27736     },
27737     
27738     onLoadCanvas : function()
27739     {   
27740         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27741         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27742         
27743         this.bodyEl.un('click', this.beforeSelectFile, this);
27744         
27745         this.notifyEl.hide();
27746         this.thumbEl.show();
27747         this.footerEl.show();
27748         
27749         this.baseRotateLevel();
27750         
27751         if(this.isDocument){
27752             this.setThumbBoxSize();
27753         }
27754         
27755         this.setThumbBoxPosition();
27756         
27757         this.baseScaleLevel();
27758         
27759         this.draw();
27760         
27761         this.resize();
27762         
27763         this.canvasLoaded = true;
27764         
27765         if(this.loadMask){
27766             this.maskEl.unmask();
27767         }
27768         
27769     },
27770     
27771     setCanvasPosition : function()
27772     {   
27773         if(!this.canvasEl){
27774             return;
27775         }
27776         
27777         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27778         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27779         
27780         this.previewEl.setLeft(pw);
27781         this.previewEl.setTop(ph);
27782         
27783     },
27784     
27785     onMouseDown : function(e)
27786     {   
27787         e.stopEvent();
27788         
27789         this.dragable = true;
27790         this.pinching = false;
27791         
27792         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27793             this.dragable = false;
27794             return;
27795         }
27796         
27797         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27798         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27799         
27800     },
27801     
27802     onMouseMove : function(e)
27803     {   
27804         e.stopEvent();
27805         
27806         if(!this.canvasLoaded){
27807             return;
27808         }
27809         
27810         if (!this.dragable){
27811             return;
27812         }
27813         
27814         var minX = Math.ceil(this.thumbEl.getLeft(true));
27815         var minY = Math.ceil(this.thumbEl.getTop(true));
27816         
27817         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27818         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27819         
27820         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27821         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27822         
27823         x = x - this.mouseX;
27824         y = y - this.mouseY;
27825         
27826         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27827         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27828         
27829         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27830         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27831         
27832         this.previewEl.setLeft(bgX);
27833         this.previewEl.setTop(bgY);
27834         
27835         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27836         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27837     },
27838     
27839     onMouseUp : function(e)
27840     {   
27841         e.stopEvent();
27842         
27843         this.dragable = false;
27844     },
27845     
27846     onMouseWheel : function(e)
27847     {   
27848         e.stopEvent();
27849         
27850         this.startScale = this.scale;
27851         
27852         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27853         
27854         if(!this.zoomable()){
27855             this.scale = this.startScale;
27856             return;
27857         }
27858         
27859         this.draw();
27860         
27861         return;
27862     },
27863     
27864     zoomable : function()
27865     {
27866         var minScale = this.thumbEl.getWidth() / this.minWidth;
27867         
27868         if(this.minWidth < this.minHeight){
27869             minScale = this.thumbEl.getHeight() / this.minHeight;
27870         }
27871         
27872         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27873         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27874         
27875         if(
27876                 this.isDocument &&
27877                 (this.rotate == 0 || this.rotate == 180) && 
27878                 (
27879                     width > this.imageEl.OriginWidth || 
27880                     height > this.imageEl.OriginHeight ||
27881                     (width < this.minWidth && height < this.minHeight)
27882                 )
27883         ){
27884             return false;
27885         }
27886         
27887         if(
27888                 this.isDocument &&
27889                 (this.rotate == 90 || this.rotate == 270) && 
27890                 (
27891                     width > this.imageEl.OriginWidth || 
27892                     height > this.imageEl.OriginHeight ||
27893                     (width < this.minHeight && height < this.minWidth)
27894                 )
27895         ){
27896             return false;
27897         }
27898         
27899         if(
27900                 !this.isDocument &&
27901                 (this.rotate == 0 || this.rotate == 180) && 
27902                 (
27903                     width < this.minWidth || 
27904                     width > this.imageEl.OriginWidth || 
27905                     height < this.minHeight || 
27906                     height > this.imageEl.OriginHeight
27907                 )
27908         ){
27909             return false;
27910         }
27911         
27912         if(
27913                 !this.isDocument &&
27914                 (this.rotate == 90 || this.rotate == 270) && 
27915                 (
27916                     width < this.minHeight || 
27917                     width > this.imageEl.OriginWidth || 
27918                     height < this.minWidth || 
27919                     height > this.imageEl.OriginHeight
27920                 )
27921         ){
27922             return false;
27923         }
27924         
27925         return true;
27926         
27927     },
27928     
27929     onRotateLeft : function(e)
27930     {   
27931         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27932             
27933             var minScale = this.thumbEl.getWidth() / this.minWidth;
27934             
27935             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27936             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27937             
27938             this.startScale = this.scale;
27939             
27940             while (this.getScaleLevel() < minScale){
27941             
27942                 this.scale = this.scale + 1;
27943                 
27944                 if(!this.zoomable()){
27945                     break;
27946                 }
27947                 
27948                 if(
27949                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27950                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27951                 ){
27952                     continue;
27953                 }
27954                 
27955                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27956
27957                 this.draw();
27958                 
27959                 return;
27960             }
27961             
27962             this.scale = this.startScale;
27963             
27964             this.onRotateFail();
27965             
27966             return false;
27967         }
27968         
27969         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27970
27971         if(this.isDocument){
27972             this.setThumbBoxSize();
27973             this.setThumbBoxPosition();
27974             this.setCanvasPosition();
27975         }
27976         
27977         this.draw();
27978         
27979         this.fireEvent('rotate', this, 'left');
27980         
27981     },
27982     
27983     onRotateRight : function(e)
27984     {
27985         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27986             
27987             var minScale = this.thumbEl.getWidth() / this.minWidth;
27988         
27989             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27990             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27991             
27992             this.startScale = this.scale;
27993             
27994             while (this.getScaleLevel() < minScale){
27995             
27996                 this.scale = this.scale + 1;
27997                 
27998                 if(!this.zoomable()){
27999                     break;
28000                 }
28001                 
28002                 if(
28003                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28004                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28005                 ){
28006                     continue;
28007                 }
28008                 
28009                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28010
28011                 this.draw();
28012                 
28013                 return;
28014             }
28015             
28016             this.scale = this.startScale;
28017             
28018             this.onRotateFail();
28019             
28020             return false;
28021         }
28022         
28023         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28024
28025         if(this.isDocument){
28026             this.setThumbBoxSize();
28027             this.setThumbBoxPosition();
28028             this.setCanvasPosition();
28029         }
28030         
28031         this.draw();
28032         
28033         this.fireEvent('rotate', this, 'right');
28034     },
28035     
28036     onRotateFail : function()
28037     {
28038         this.errorEl.show(true);
28039         
28040         var _this = this;
28041         
28042         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
28043     },
28044     
28045     draw : function()
28046     {
28047         this.previewEl.dom.innerHTML = '';
28048         
28049         var canvasEl = document.createElement("canvas");
28050         
28051         var contextEl = canvasEl.getContext("2d");
28052         
28053         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28054         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28055         var center = this.imageEl.OriginWidth / 2;
28056         
28057         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
28058             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28059             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28060             center = this.imageEl.OriginHeight / 2;
28061         }
28062         
28063         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
28064         
28065         contextEl.translate(center, center);
28066         contextEl.rotate(this.rotate * Math.PI / 180);
28067
28068         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28069         
28070         this.canvasEl = document.createElement("canvas");
28071         
28072         this.contextEl = this.canvasEl.getContext("2d");
28073         
28074         switch (this.rotate) {
28075             case 0 :
28076                 
28077                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28078                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28079                 
28080                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28081                 
28082                 break;
28083             case 90 : 
28084                 
28085                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28086                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28087                 
28088                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28089                     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);
28090                     break;
28091                 }
28092                 
28093                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28094                 
28095                 break;
28096             case 180 :
28097                 
28098                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28099                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28100                 
28101                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28102                     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);
28103                     break;
28104                 }
28105                 
28106                 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);
28107                 
28108                 break;
28109             case 270 :
28110                 
28111                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28112                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28113         
28114                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28115                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28116                     break;
28117                 }
28118                 
28119                 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);
28120                 
28121                 break;
28122             default : 
28123                 break;
28124         }
28125         
28126         this.previewEl.appendChild(this.canvasEl);
28127         
28128         this.setCanvasPosition();
28129     },
28130     
28131     crop : function()
28132     {
28133         if(!this.canvasLoaded){
28134             return;
28135         }
28136         
28137         var imageCanvas = document.createElement("canvas");
28138         
28139         var imageContext = imageCanvas.getContext("2d");
28140         
28141         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28142         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28143         
28144         var center = imageCanvas.width / 2;
28145         
28146         imageContext.translate(center, center);
28147         
28148         imageContext.rotate(this.rotate * Math.PI / 180);
28149         
28150         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28151         
28152         var canvas = document.createElement("canvas");
28153         
28154         var context = canvas.getContext("2d");
28155                 
28156         canvas.width = this.minWidth;
28157         canvas.height = this.minHeight;
28158
28159         switch (this.rotate) {
28160             case 0 :
28161                 
28162                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28163                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28164                 
28165                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28166                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28167                 
28168                 var targetWidth = this.minWidth - 2 * x;
28169                 var targetHeight = this.minHeight - 2 * y;
28170                 
28171                 var scale = 1;
28172                 
28173                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28174                     scale = targetWidth / width;
28175                 }
28176                 
28177                 if(x > 0 && y == 0){
28178                     scale = targetHeight / height;
28179                 }
28180                 
28181                 if(x > 0 && y > 0){
28182                     scale = targetWidth / width;
28183                     
28184                     if(width < height){
28185                         scale = targetHeight / height;
28186                     }
28187                 }
28188                 
28189                 context.scale(scale, scale);
28190                 
28191                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28192                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28193
28194                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28195                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28196
28197                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28198                 
28199                 break;
28200             case 90 : 
28201                 
28202                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28203                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28204                 
28205                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28206                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28207                 
28208                 var targetWidth = this.minWidth - 2 * x;
28209                 var targetHeight = this.minHeight - 2 * y;
28210                 
28211                 var scale = 1;
28212                 
28213                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28214                     scale = targetWidth / width;
28215                 }
28216                 
28217                 if(x > 0 && y == 0){
28218                     scale = targetHeight / height;
28219                 }
28220                 
28221                 if(x > 0 && y > 0){
28222                     scale = targetWidth / width;
28223                     
28224                     if(width < height){
28225                         scale = targetHeight / height;
28226                     }
28227                 }
28228                 
28229                 context.scale(scale, scale);
28230                 
28231                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28232                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28233
28234                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28235                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28236                 
28237                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28238                 
28239                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28240                 
28241                 break;
28242             case 180 :
28243                 
28244                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28245                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28246                 
28247                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28248                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28249                 
28250                 var targetWidth = this.minWidth - 2 * x;
28251                 var targetHeight = this.minHeight - 2 * y;
28252                 
28253                 var scale = 1;
28254                 
28255                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28256                     scale = targetWidth / width;
28257                 }
28258                 
28259                 if(x > 0 && y == 0){
28260                     scale = targetHeight / height;
28261                 }
28262                 
28263                 if(x > 0 && y > 0){
28264                     scale = targetWidth / width;
28265                     
28266                     if(width < height){
28267                         scale = targetHeight / height;
28268                     }
28269                 }
28270                 
28271                 context.scale(scale, scale);
28272                 
28273                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28274                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28275
28276                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28277                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28278
28279                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28280                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28281                 
28282                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28283                 
28284                 break;
28285             case 270 :
28286                 
28287                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28288                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28289                 
28290                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28291                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28292                 
28293                 var targetWidth = this.minWidth - 2 * x;
28294                 var targetHeight = this.minHeight - 2 * y;
28295                 
28296                 var scale = 1;
28297                 
28298                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28299                     scale = targetWidth / width;
28300                 }
28301                 
28302                 if(x > 0 && y == 0){
28303                     scale = targetHeight / height;
28304                 }
28305                 
28306                 if(x > 0 && y > 0){
28307                     scale = targetWidth / width;
28308                     
28309                     if(width < height){
28310                         scale = targetHeight / height;
28311                     }
28312                 }
28313                 
28314                 context.scale(scale, scale);
28315                 
28316                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28317                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28318
28319                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28320                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28321                 
28322                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28323                 
28324                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28325                 
28326                 break;
28327             default : 
28328                 break;
28329         }
28330         
28331         this.cropData = canvas.toDataURL(this.cropType);
28332         
28333         if(this.fireEvent('crop', this, this.cropData) !== false){
28334             this.process(this.file, this.cropData);
28335         }
28336         
28337         return;
28338         
28339     },
28340     
28341     setThumbBoxSize : function()
28342     {
28343         var width, height;
28344         
28345         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28346             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28347             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28348             
28349             this.minWidth = width;
28350             this.minHeight = height;
28351             
28352             if(this.rotate == 90 || this.rotate == 270){
28353                 this.minWidth = height;
28354                 this.minHeight = width;
28355             }
28356         }
28357         
28358         height = 300;
28359         width = Math.ceil(this.minWidth * height / this.minHeight);
28360         
28361         if(this.minWidth > this.minHeight){
28362             width = 300;
28363             height = Math.ceil(this.minHeight * width / this.minWidth);
28364         }
28365         
28366         this.thumbEl.setStyle({
28367             width : width + 'px',
28368             height : height + 'px'
28369         });
28370
28371         return;
28372             
28373     },
28374     
28375     setThumbBoxPosition : function()
28376     {
28377         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28378         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28379         
28380         this.thumbEl.setLeft(x);
28381         this.thumbEl.setTop(y);
28382         
28383     },
28384     
28385     baseRotateLevel : function()
28386     {
28387         this.baseRotate = 1;
28388         
28389         if(
28390                 typeof(this.exif) != 'undefined' &&
28391                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28392                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28393         ){
28394             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28395         }
28396         
28397         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28398         
28399     },
28400     
28401     baseScaleLevel : function()
28402     {
28403         var width, height;
28404         
28405         if(this.isDocument){
28406             
28407             if(this.baseRotate == 6 || this.baseRotate == 8){
28408             
28409                 height = this.thumbEl.getHeight();
28410                 this.baseScale = height / this.imageEl.OriginWidth;
28411
28412                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28413                     width = this.thumbEl.getWidth();
28414                     this.baseScale = width / this.imageEl.OriginHeight;
28415                 }
28416
28417                 return;
28418             }
28419
28420             height = this.thumbEl.getHeight();
28421             this.baseScale = height / this.imageEl.OriginHeight;
28422
28423             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28424                 width = this.thumbEl.getWidth();
28425                 this.baseScale = width / this.imageEl.OriginWidth;
28426             }
28427
28428             return;
28429         }
28430         
28431         if(this.baseRotate == 6 || this.baseRotate == 8){
28432             
28433             width = this.thumbEl.getHeight();
28434             this.baseScale = width / this.imageEl.OriginHeight;
28435             
28436             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28437                 height = this.thumbEl.getWidth();
28438                 this.baseScale = height / this.imageEl.OriginHeight;
28439             }
28440             
28441             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28442                 height = this.thumbEl.getWidth();
28443                 this.baseScale = height / this.imageEl.OriginHeight;
28444                 
28445                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28446                     width = this.thumbEl.getHeight();
28447                     this.baseScale = width / this.imageEl.OriginWidth;
28448                 }
28449             }
28450             
28451             return;
28452         }
28453         
28454         width = this.thumbEl.getWidth();
28455         this.baseScale = width / this.imageEl.OriginWidth;
28456         
28457         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28458             height = this.thumbEl.getHeight();
28459             this.baseScale = height / this.imageEl.OriginHeight;
28460         }
28461         
28462         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28463             
28464             height = this.thumbEl.getHeight();
28465             this.baseScale = height / this.imageEl.OriginHeight;
28466             
28467             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28468                 width = this.thumbEl.getWidth();
28469                 this.baseScale = width / this.imageEl.OriginWidth;
28470             }
28471             
28472         }
28473         
28474         return;
28475     },
28476     
28477     getScaleLevel : function()
28478     {
28479         return this.baseScale * Math.pow(1.1, this.scale);
28480     },
28481     
28482     onTouchStart : function(e)
28483     {
28484         if(!this.canvasLoaded){
28485             this.beforeSelectFile(e);
28486             return;
28487         }
28488         
28489         var touches = e.browserEvent.touches;
28490         
28491         if(!touches){
28492             return;
28493         }
28494         
28495         if(touches.length == 1){
28496             this.onMouseDown(e);
28497             return;
28498         }
28499         
28500         if(touches.length != 2){
28501             return;
28502         }
28503         
28504         var coords = [];
28505         
28506         for(var i = 0, finger; finger = touches[i]; i++){
28507             coords.push(finger.pageX, finger.pageY);
28508         }
28509         
28510         var x = Math.pow(coords[0] - coords[2], 2);
28511         var y = Math.pow(coords[1] - coords[3], 2);
28512         
28513         this.startDistance = Math.sqrt(x + y);
28514         
28515         this.startScale = this.scale;
28516         
28517         this.pinching = true;
28518         this.dragable = false;
28519         
28520     },
28521     
28522     onTouchMove : function(e)
28523     {
28524         if(!this.pinching && !this.dragable){
28525             return;
28526         }
28527         
28528         var touches = e.browserEvent.touches;
28529         
28530         if(!touches){
28531             return;
28532         }
28533         
28534         if(this.dragable){
28535             this.onMouseMove(e);
28536             return;
28537         }
28538         
28539         var coords = [];
28540         
28541         for(var i = 0, finger; finger = touches[i]; i++){
28542             coords.push(finger.pageX, finger.pageY);
28543         }
28544         
28545         var x = Math.pow(coords[0] - coords[2], 2);
28546         var y = Math.pow(coords[1] - coords[3], 2);
28547         
28548         this.endDistance = Math.sqrt(x + y);
28549         
28550         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28551         
28552         if(!this.zoomable()){
28553             this.scale = this.startScale;
28554             return;
28555         }
28556         
28557         this.draw();
28558         
28559     },
28560     
28561     onTouchEnd : function(e)
28562     {
28563         this.pinching = false;
28564         this.dragable = false;
28565         
28566     },
28567     
28568     process : function(file, crop)
28569     {
28570         if(this.loadMask){
28571             this.maskEl.mask(this.loadingText);
28572         }
28573         
28574         this.xhr = new XMLHttpRequest();
28575         
28576         file.xhr = this.xhr;
28577
28578         this.xhr.open(this.method, this.url, true);
28579         
28580         var headers = {
28581             "Accept": "application/json",
28582             "Cache-Control": "no-cache",
28583             "X-Requested-With": "XMLHttpRequest"
28584         };
28585         
28586         for (var headerName in headers) {
28587             var headerValue = headers[headerName];
28588             if (headerValue) {
28589                 this.xhr.setRequestHeader(headerName, headerValue);
28590             }
28591         }
28592         
28593         var _this = this;
28594         
28595         this.xhr.onload = function()
28596         {
28597             _this.xhrOnLoad(_this.xhr);
28598         }
28599         
28600         this.xhr.onerror = function()
28601         {
28602             _this.xhrOnError(_this.xhr);
28603         }
28604         
28605         var formData = new FormData();
28606
28607         formData.append('returnHTML', 'NO');
28608         
28609         if(crop){
28610             formData.append('crop', crop);
28611         }
28612         
28613         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28614             formData.append(this.paramName, file, file.name);
28615         }
28616         
28617         if(typeof(file.filename) != 'undefined'){
28618             formData.append('filename', file.filename);
28619         }
28620         
28621         if(typeof(file.mimetype) != 'undefined'){
28622             formData.append('mimetype', file.mimetype);
28623         }
28624         
28625         if(this.fireEvent('arrange', this, formData) != false){
28626             this.xhr.send(formData);
28627         };
28628     },
28629     
28630     xhrOnLoad : function(xhr)
28631     {
28632         if(this.loadMask){
28633             this.maskEl.unmask();
28634         }
28635         
28636         if (xhr.readyState !== 4) {
28637             this.fireEvent('exception', this, xhr);
28638             return;
28639         }
28640
28641         var response = Roo.decode(xhr.responseText);
28642         
28643         if(!response.success){
28644             this.fireEvent('exception', this, xhr);
28645             return;
28646         }
28647         
28648         var response = Roo.decode(xhr.responseText);
28649         
28650         this.fireEvent('upload', this, response);
28651         
28652     },
28653     
28654     xhrOnError : function()
28655     {
28656         if(this.loadMask){
28657             this.maskEl.unmask();
28658         }
28659         
28660         Roo.log('xhr on error');
28661         
28662         var response = Roo.decode(xhr.responseText);
28663           
28664         Roo.log(response);
28665         
28666     },
28667     
28668     prepare : function(file)
28669     {   
28670         if(this.loadMask){
28671             this.maskEl.mask(this.loadingText);
28672         }
28673         
28674         this.file = false;
28675         this.exif = {};
28676         
28677         if(typeof(file) === 'string'){
28678             this.loadCanvas(file);
28679             return;
28680         }
28681         
28682         if(!file || !this.urlAPI){
28683             return;
28684         }
28685         
28686         this.file = file;
28687         this.cropType = file.type;
28688         
28689         var _this = this;
28690         
28691         if(this.fireEvent('prepare', this, this.file) != false){
28692             
28693             var reader = new FileReader();
28694             
28695             reader.onload = function (e) {
28696                 if (e.target.error) {
28697                     Roo.log(e.target.error);
28698                     return;
28699                 }
28700                 
28701                 var buffer = e.target.result,
28702                     dataView = new DataView(buffer),
28703                     offset = 2,
28704                     maxOffset = dataView.byteLength - 4,
28705                     markerBytes,
28706                     markerLength;
28707                 
28708                 if (dataView.getUint16(0) === 0xffd8) {
28709                     while (offset < maxOffset) {
28710                         markerBytes = dataView.getUint16(offset);
28711                         
28712                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28713                             markerLength = dataView.getUint16(offset + 2) + 2;
28714                             if (offset + markerLength > dataView.byteLength) {
28715                                 Roo.log('Invalid meta data: Invalid segment size.');
28716                                 break;
28717                             }
28718                             
28719                             if(markerBytes == 0xffe1){
28720                                 _this.parseExifData(
28721                                     dataView,
28722                                     offset,
28723                                     markerLength
28724                                 );
28725                             }
28726                             
28727                             offset += markerLength;
28728                             
28729                             continue;
28730                         }
28731                         
28732                         break;
28733                     }
28734                     
28735                 }
28736                 
28737                 var url = _this.urlAPI.createObjectURL(_this.file);
28738                 
28739                 _this.loadCanvas(url);
28740                 
28741                 return;
28742             }
28743             
28744             reader.readAsArrayBuffer(this.file);
28745             
28746         }
28747         
28748     },
28749     
28750     parseExifData : function(dataView, offset, length)
28751     {
28752         var tiffOffset = offset + 10,
28753             littleEndian,
28754             dirOffset;
28755     
28756         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28757             // No Exif data, might be XMP data instead
28758             return;
28759         }
28760         
28761         // Check for the ASCII code for "Exif" (0x45786966):
28762         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28763             // No Exif data, might be XMP data instead
28764             return;
28765         }
28766         if (tiffOffset + 8 > dataView.byteLength) {
28767             Roo.log('Invalid Exif data: Invalid segment size.');
28768             return;
28769         }
28770         // Check for the two null bytes:
28771         if (dataView.getUint16(offset + 8) !== 0x0000) {
28772             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28773             return;
28774         }
28775         // Check the byte alignment:
28776         switch (dataView.getUint16(tiffOffset)) {
28777         case 0x4949:
28778             littleEndian = true;
28779             break;
28780         case 0x4D4D:
28781             littleEndian = false;
28782             break;
28783         default:
28784             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28785             return;
28786         }
28787         // Check for the TIFF tag marker (0x002A):
28788         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28789             Roo.log('Invalid Exif data: Missing TIFF marker.');
28790             return;
28791         }
28792         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28793         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28794         
28795         this.parseExifTags(
28796             dataView,
28797             tiffOffset,
28798             tiffOffset + dirOffset,
28799             littleEndian
28800         );
28801     },
28802     
28803     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28804     {
28805         var tagsNumber,
28806             dirEndOffset,
28807             i;
28808         if (dirOffset + 6 > dataView.byteLength) {
28809             Roo.log('Invalid Exif data: Invalid directory offset.');
28810             return;
28811         }
28812         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28813         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28814         if (dirEndOffset + 4 > dataView.byteLength) {
28815             Roo.log('Invalid Exif data: Invalid directory size.');
28816             return;
28817         }
28818         for (i = 0; i < tagsNumber; i += 1) {
28819             this.parseExifTag(
28820                 dataView,
28821                 tiffOffset,
28822                 dirOffset + 2 + 12 * i, // tag offset
28823                 littleEndian
28824             );
28825         }
28826         // Return the offset to the next directory:
28827         return dataView.getUint32(dirEndOffset, littleEndian);
28828     },
28829     
28830     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28831     {
28832         var tag = dataView.getUint16(offset, littleEndian);
28833         
28834         this.exif[tag] = this.getExifValue(
28835             dataView,
28836             tiffOffset,
28837             offset,
28838             dataView.getUint16(offset + 2, littleEndian), // tag type
28839             dataView.getUint32(offset + 4, littleEndian), // tag length
28840             littleEndian
28841         );
28842     },
28843     
28844     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28845     {
28846         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28847             tagSize,
28848             dataOffset,
28849             values,
28850             i,
28851             str,
28852             c;
28853     
28854         if (!tagType) {
28855             Roo.log('Invalid Exif data: Invalid tag type.');
28856             return;
28857         }
28858         
28859         tagSize = tagType.size * length;
28860         // Determine if the value is contained in the dataOffset bytes,
28861         // or if the value at the dataOffset is a pointer to the actual data:
28862         dataOffset = tagSize > 4 ?
28863                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28864         if (dataOffset + tagSize > dataView.byteLength) {
28865             Roo.log('Invalid Exif data: Invalid data offset.');
28866             return;
28867         }
28868         if (length === 1) {
28869             return tagType.getValue(dataView, dataOffset, littleEndian);
28870         }
28871         values = [];
28872         for (i = 0; i < length; i += 1) {
28873             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28874         }
28875         
28876         if (tagType.ascii) {
28877             str = '';
28878             // Concatenate the chars:
28879             for (i = 0; i < values.length; i += 1) {
28880                 c = values[i];
28881                 // Ignore the terminating NULL byte(s):
28882                 if (c === '\u0000') {
28883                     break;
28884                 }
28885                 str += c;
28886             }
28887             return str;
28888         }
28889         return values;
28890     }
28891     
28892 });
28893
28894 Roo.apply(Roo.bootstrap.UploadCropbox, {
28895     tags : {
28896         'Orientation': 0x0112
28897     },
28898     
28899     Orientation: {
28900             1: 0, //'top-left',
28901 //            2: 'top-right',
28902             3: 180, //'bottom-right',
28903 //            4: 'bottom-left',
28904 //            5: 'left-top',
28905             6: 90, //'right-top',
28906 //            7: 'right-bottom',
28907             8: 270 //'left-bottom'
28908     },
28909     
28910     exifTagTypes : {
28911         // byte, 8-bit unsigned int:
28912         1: {
28913             getValue: function (dataView, dataOffset) {
28914                 return dataView.getUint8(dataOffset);
28915             },
28916             size: 1
28917         },
28918         // ascii, 8-bit byte:
28919         2: {
28920             getValue: function (dataView, dataOffset) {
28921                 return String.fromCharCode(dataView.getUint8(dataOffset));
28922             },
28923             size: 1,
28924             ascii: true
28925         },
28926         // short, 16 bit int:
28927         3: {
28928             getValue: function (dataView, dataOffset, littleEndian) {
28929                 return dataView.getUint16(dataOffset, littleEndian);
28930             },
28931             size: 2
28932         },
28933         // long, 32 bit int:
28934         4: {
28935             getValue: function (dataView, dataOffset, littleEndian) {
28936                 return dataView.getUint32(dataOffset, littleEndian);
28937             },
28938             size: 4
28939         },
28940         // rational = two long values, first is numerator, second is denominator:
28941         5: {
28942             getValue: function (dataView, dataOffset, littleEndian) {
28943                 return dataView.getUint32(dataOffset, littleEndian) /
28944                     dataView.getUint32(dataOffset + 4, littleEndian);
28945             },
28946             size: 8
28947         },
28948         // slong, 32 bit signed int:
28949         9: {
28950             getValue: function (dataView, dataOffset, littleEndian) {
28951                 return dataView.getInt32(dataOffset, littleEndian);
28952             },
28953             size: 4
28954         },
28955         // srational, two slongs, first is numerator, second is denominator:
28956         10: {
28957             getValue: function (dataView, dataOffset, littleEndian) {
28958                 return dataView.getInt32(dataOffset, littleEndian) /
28959                     dataView.getInt32(dataOffset + 4, littleEndian);
28960             },
28961             size: 8
28962         }
28963     },
28964     
28965     footer : {
28966         STANDARD : [
28967             {
28968                 tag : 'div',
28969                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28970                 action : 'rotate-left',
28971                 cn : [
28972                     {
28973                         tag : 'button',
28974                         cls : 'btn btn-default',
28975                         html : '<i class="fa fa-undo"></i>'
28976                     }
28977                 ]
28978             },
28979             {
28980                 tag : 'div',
28981                 cls : 'btn-group roo-upload-cropbox-picture',
28982                 action : 'picture',
28983                 cn : [
28984                     {
28985                         tag : 'button',
28986                         cls : 'btn btn-default',
28987                         html : '<i class="fa fa-picture-o"></i>'
28988                     }
28989                 ]
28990             },
28991             {
28992                 tag : 'div',
28993                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28994                 action : 'rotate-right',
28995                 cn : [
28996                     {
28997                         tag : 'button',
28998                         cls : 'btn btn-default',
28999                         html : '<i class="fa fa-repeat"></i>'
29000                     }
29001                 ]
29002             }
29003         ],
29004         DOCUMENT : [
29005             {
29006                 tag : 'div',
29007                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29008                 action : 'rotate-left',
29009                 cn : [
29010                     {
29011                         tag : 'button',
29012                         cls : 'btn btn-default',
29013                         html : '<i class="fa fa-undo"></i>'
29014                     }
29015                 ]
29016             },
29017             {
29018                 tag : 'div',
29019                 cls : 'btn-group roo-upload-cropbox-download',
29020                 action : 'download',
29021                 cn : [
29022                     {
29023                         tag : 'button',
29024                         cls : 'btn btn-default',
29025                         html : '<i class="fa fa-download"></i>'
29026                     }
29027                 ]
29028             },
29029             {
29030                 tag : 'div',
29031                 cls : 'btn-group roo-upload-cropbox-crop',
29032                 action : 'crop',
29033                 cn : [
29034                     {
29035                         tag : 'button',
29036                         cls : 'btn btn-default',
29037                         html : '<i class="fa fa-crop"></i>'
29038                     }
29039                 ]
29040             },
29041             {
29042                 tag : 'div',
29043                 cls : 'btn-group roo-upload-cropbox-trash',
29044                 action : 'trash',
29045                 cn : [
29046                     {
29047                         tag : 'button',
29048                         cls : 'btn btn-default',
29049                         html : '<i class="fa fa-trash"></i>'
29050                     }
29051                 ]
29052             },
29053             {
29054                 tag : 'div',
29055                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29056                 action : 'rotate-right',
29057                 cn : [
29058                     {
29059                         tag : 'button',
29060                         cls : 'btn btn-default',
29061                         html : '<i class="fa fa-repeat"></i>'
29062                     }
29063                 ]
29064             }
29065         ],
29066         ROTATOR : [
29067             {
29068                 tag : 'div',
29069                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29070                 action : 'rotate-left',
29071                 cn : [
29072                     {
29073                         tag : 'button',
29074                         cls : 'btn btn-default',
29075                         html : '<i class="fa fa-undo"></i>'
29076                     }
29077                 ]
29078             },
29079             {
29080                 tag : 'div',
29081                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29082                 action : 'rotate-right',
29083                 cn : [
29084                     {
29085                         tag : 'button',
29086                         cls : 'btn btn-default',
29087                         html : '<i class="fa fa-repeat"></i>'
29088                     }
29089                 ]
29090             }
29091         ]
29092     }
29093 });
29094
29095 /*
29096 * Licence: LGPL
29097 */
29098
29099 /**
29100  * @class Roo.bootstrap.DocumentManager
29101  * @extends Roo.bootstrap.Component
29102  * Bootstrap DocumentManager class
29103  * @cfg {String} paramName default 'imageUpload'
29104  * @cfg {String} toolTipName default 'filename'
29105  * @cfg {String} method default POST
29106  * @cfg {String} url action url
29107  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
29108  * @cfg {Boolean} multiple multiple upload default true
29109  * @cfg {Number} thumbSize default 300
29110  * @cfg {String} fieldLabel
29111  * @cfg {Number} labelWidth default 4
29112  * @cfg {String} labelAlign (left|top) default left
29113  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
29114 * @cfg {Number} labellg set the width of label (1-12)
29115  * @cfg {Number} labelmd set the width of label (1-12)
29116  * @cfg {Number} labelsm set the width of label (1-12)
29117  * @cfg {Number} labelxs set the width of label (1-12)
29118  * 
29119  * @constructor
29120  * Create a new DocumentManager
29121  * @param {Object} config The config object
29122  */
29123
29124 Roo.bootstrap.DocumentManager = function(config){
29125     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
29126     
29127     this.files = [];
29128     this.delegates = [];
29129     
29130     this.addEvents({
29131         /**
29132          * @event initial
29133          * Fire when initial the DocumentManager
29134          * @param {Roo.bootstrap.DocumentManager} this
29135          */
29136         "initial" : true,
29137         /**
29138          * @event inspect
29139          * inspect selected file
29140          * @param {Roo.bootstrap.DocumentManager} this
29141          * @param {File} file
29142          */
29143         "inspect" : true,
29144         /**
29145          * @event exception
29146          * Fire when xhr load exception
29147          * @param {Roo.bootstrap.DocumentManager} this
29148          * @param {XMLHttpRequest} xhr
29149          */
29150         "exception" : true,
29151         /**
29152          * @event afterupload
29153          * Fire when xhr load exception
29154          * @param {Roo.bootstrap.DocumentManager} this
29155          * @param {XMLHttpRequest} xhr
29156          */
29157         "afterupload" : true,
29158         /**
29159          * @event prepare
29160          * prepare the form data
29161          * @param {Roo.bootstrap.DocumentManager} this
29162          * @param {Object} formData
29163          */
29164         "prepare" : true,
29165         /**
29166          * @event remove
29167          * Fire when remove the file
29168          * @param {Roo.bootstrap.DocumentManager} this
29169          * @param {Object} file
29170          */
29171         "remove" : true,
29172         /**
29173          * @event refresh
29174          * Fire after refresh the file
29175          * @param {Roo.bootstrap.DocumentManager} this
29176          */
29177         "refresh" : true,
29178         /**
29179          * @event click
29180          * Fire after click the image
29181          * @param {Roo.bootstrap.DocumentManager} this
29182          * @param {Object} file
29183          */
29184         "click" : true,
29185         /**
29186          * @event edit
29187          * Fire when upload a image and editable set to true
29188          * @param {Roo.bootstrap.DocumentManager} this
29189          * @param {Object} file
29190          */
29191         "edit" : true,
29192         /**
29193          * @event beforeselectfile
29194          * Fire before select file
29195          * @param {Roo.bootstrap.DocumentManager} this
29196          */
29197         "beforeselectfile" : true,
29198         /**
29199          * @event process
29200          * Fire before process file
29201          * @param {Roo.bootstrap.DocumentManager} this
29202          * @param {Object} file
29203          */
29204         "process" : true,
29205         /**
29206          * @event previewrendered
29207          * Fire when preview rendered
29208          * @param {Roo.bootstrap.DocumentManager} this
29209          * @param {Object} file
29210          */
29211         "previewrendered" : true,
29212         /**
29213          */
29214         "previewResize" : true
29215         
29216     });
29217 };
29218
29219 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
29220     
29221     boxes : 0,
29222     inputName : '',
29223     thumbSize : 300,
29224     multiple : true,
29225     files : false,
29226     method : 'POST',
29227     url : '',
29228     paramName : 'imageUpload',
29229     toolTipName : 'filename',
29230     fieldLabel : '',
29231     labelWidth : 4,
29232     labelAlign : 'left',
29233     editable : true,
29234     delegates : false,
29235     xhr : false, 
29236     
29237     labellg : 0,
29238     labelmd : 0,
29239     labelsm : 0,
29240     labelxs : 0,
29241     
29242     getAutoCreate : function()
29243     {   
29244         var managerWidget = {
29245             tag : 'div',
29246             cls : 'roo-document-manager',
29247             cn : [
29248                 {
29249                     tag : 'input',
29250                     cls : 'roo-document-manager-selector',
29251                     type : 'file'
29252                 },
29253                 {
29254                     tag : 'div',
29255                     cls : 'roo-document-manager-uploader',
29256                     cn : [
29257                         {
29258                             tag : 'div',
29259                             cls : 'roo-document-manager-upload-btn',
29260                             html : '<i class="fa fa-plus"></i>'
29261                         }
29262                     ]
29263                     
29264                 }
29265             ]
29266         };
29267         
29268         var content = [
29269             {
29270                 tag : 'div',
29271                 cls : 'column col-md-12',
29272                 cn : managerWidget
29273             }
29274         ];
29275         
29276         if(this.fieldLabel.length){
29277             
29278             content = [
29279                 {
29280                     tag : 'div',
29281                     cls : 'column col-md-12',
29282                     html : this.fieldLabel
29283                 },
29284                 {
29285                     tag : 'div',
29286                     cls : 'column col-md-12',
29287                     cn : managerWidget
29288                 }
29289             ];
29290
29291             if(this.labelAlign == 'left'){
29292                 content = [
29293                     {
29294                         tag : 'div',
29295                         cls : 'column',
29296                         html : this.fieldLabel
29297                     },
29298                     {
29299                         tag : 'div',
29300                         cls : 'column',
29301                         cn : managerWidget
29302                     }
29303                 ];
29304                 
29305                 if(this.labelWidth > 12){
29306                     content[0].style = "width: " + this.labelWidth + 'px';
29307                 }
29308
29309                 if(this.labelWidth < 13 && this.labelmd == 0){
29310                     this.labelmd = this.labelWidth;
29311                 }
29312
29313                 if(this.labellg > 0){
29314                     content[0].cls += ' col-lg-' + this.labellg;
29315                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29316                 }
29317
29318                 if(this.labelmd > 0){
29319                     content[0].cls += ' col-md-' + this.labelmd;
29320                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29321                 }
29322
29323                 if(this.labelsm > 0){
29324                     content[0].cls += ' col-sm-' + this.labelsm;
29325                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29326                 }
29327
29328                 if(this.labelxs > 0){
29329                     content[0].cls += ' col-xs-' + this.labelxs;
29330                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29331                 }
29332                 
29333             }
29334         }
29335         
29336         var cfg = {
29337             tag : 'div',
29338             cls : 'row clearfix',
29339             cn : content
29340         };
29341         
29342         return cfg;
29343         
29344     },
29345     
29346     initEvents : function()
29347     {
29348         this.managerEl = this.el.select('.roo-document-manager', true).first();
29349         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29350         
29351         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29352         this.selectorEl.hide();
29353         
29354         if(this.multiple){
29355             this.selectorEl.attr('multiple', 'multiple');
29356         }
29357         
29358         this.selectorEl.on('change', this.onFileSelected, this);
29359         
29360         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29361         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29362         
29363         this.uploader.on('click', this.onUploaderClick, this);
29364         
29365         this.renderProgressDialog();
29366         
29367         var _this = this;
29368         
29369         window.addEventListener("resize", function() { _this.refresh(); } );
29370         
29371         this.fireEvent('initial', this);
29372     },
29373     
29374     renderProgressDialog : function()
29375     {
29376         var _this = this;
29377         
29378         this.progressDialog = new Roo.bootstrap.Modal({
29379             cls : 'roo-document-manager-progress-dialog',
29380             allow_close : false,
29381             animate : false,
29382             title : '',
29383             buttons : [
29384                 {
29385                     name  :'cancel',
29386                     weight : 'danger',
29387                     html : 'Cancel'
29388                 }
29389             ], 
29390             listeners : { 
29391                 btnclick : function() {
29392                     _this.uploadCancel();
29393                     this.hide();
29394                 }
29395             }
29396         });
29397          
29398         this.progressDialog.render(Roo.get(document.body));
29399          
29400         this.progress = new Roo.bootstrap.Progress({
29401             cls : 'roo-document-manager-progress',
29402             active : true,
29403             striped : true
29404         });
29405         
29406         this.progress.render(this.progressDialog.getChildContainer());
29407         
29408         this.progressBar = new Roo.bootstrap.ProgressBar({
29409             cls : 'roo-document-manager-progress-bar',
29410             aria_valuenow : 0,
29411             aria_valuemin : 0,
29412             aria_valuemax : 12,
29413             panel : 'success'
29414         });
29415         
29416         this.progressBar.render(this.progress.getChildContainer());
29417     },
29418     
29419     onUploaderClick : function(e)
29420     {
29421         e.preventDefault();
29422      
29423         if(this.fireEvent('beforeselectfile', this) != false){
29424             this.selectorEl.dom.click();
29425         }
29426         
29427     },
29428     
29429     onFileSelected : function(e)
29430     {
29431         e.preventDefault();
29432         
29433         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29434             return;
29435         }
29436         
29437         Roo.each(this.selectorEl.dom.files, function(file){
29438             if(this.fireEvent('inspect', this, file) != false){
29439                 this.files.push(file);
29440             }
29441         }, this);
29442         
29443         this.queue();
29444         
29445     },
29446     
29447     queue : function()
29448     {
29449         this.selectorEl.dom.value = '';
29450         
29451         if(!this.files || !this.files.length){
29452             return;
29453         }
29454         
29455         if(this.boxes > 0 && this.files.length > this.boxes){
29456             this.files = this.files.slice(0, this.boxes);
29457         }
29458         
29459         this.uploader.show();
29460         
29461         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29462             this.uploader.hide();
29463         }
29464         
29465         var _this = this;
29466         
29467         var files = [];
29468         
29469         var docs = [];
29470         
29471         Roo.each(this.files, function(file){
29472             
29473             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29474                 var f = this.renderPreview(file);
29475                 files.push(f);
29476                 return;
29477             }
29478             
29479             if(file.type.indexOf('image') != -1){
29480                 this.delegates.push(
29481                     (function(){
29482                         _this.process(file);
29483                     }).createDelegate(this)
29484                 );
29485         
29486                 return;
29487             }
29488             
29489             docs.push(
29490                 (function(){
29491                     _this.process(file);
29492                 }).createDelegate(this)
29493             );
29494             
29495         }, this);
29496         
29497         this.files = files;
29498         
29499         this.delegates = this.delegates.concat(docs);
29500         
29501         if(!this.delegates.length){
29502             this.refresh();
29503             return;
29504         }
29505         
29506         this.progressBar.aria_valuemax = this.delegates.length;
29507         
29508         this.arrange();
29509         
29510         return;
29511     },
29512     
29513     arrange : function()
29514     {
29515         if(!this.delegates.length){
29516             this.progressDialog.hide();
29517             this.refresh();
29518             return;
29519         }
29520         
29521         var delegate = this.delegates.shift();
29522         
29523         this.progressDialog.show();
29524         
29525         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29526         
29527         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29528         
29529         delegate();
29530     },
29531     
29532     refresh : function()
29533     {
29534         this.uploader.show();
29535         
29536         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29537             this.uploader.hide();
29538         }
29539         
29540         Roo.isTouch ? this.closable(false) : this.closable(true);
29541         
29542         this.fireEvent('refresh', this);
29543     },
29544     
29545     onRemove : function(e, el, o)
29546     {
29547         e.preventDefault();
29548         
29549         this.fireEvent('remove', this, o);
29550         
29551     },
29552     
29553     remove : function(o)
29554     {
29555         var files = [];
29556         
29557         Roo.each(this.files, function(file){
29558             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29559                 files.push(file);
29560                 return;
29561             }
29562
29563             o.target.remove();
29564
29565         }, this);
29566         
29567         this.files = files;
29568         
29569         this.refresh();
29570     },
29571     
29572     clear : function()
29573     {
29574         Roo.each(this.files, function(file){
29575             if(!file.target){
29576                 return;
29577             }
29578             
29579             file.target.remove();
29580
29581         }, this);
29582         
29583         this.files = [];
29584         
29585         this.refresh();
29586     },
29587     
29588     onClick : function(e, el, o)
29589     {
29590         e.preventDefault();
29591         
29592         this.fireEvent('click', this, o);
29593         
29594     },
29595     
29596     closable : function(closable)
29597     {
29598         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29599             
29600             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29601             
29602             if(closable){
29603                 el.show();
29604                 return;
29605             }
29606             
29607             el.hide();
29608             
29609         }, this);
29610     },
29611     
29612     xhrOnLoad : function(xhr)
29613     {
29614         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29615             el.remove();
29616         }, this);
29617         
29618         if (xhr.readyState !== 4) {
29619             this.arrange();
29620             this.fireEvent('exception', this, xhr);
29621             return;
29622         }
29623
29624         var response = Roo.decode(xhr.responseText);
29625         
29626         if(!response.success){
29627             this.arrange();
29628             this.fireEvent('exception', this, xhr);
29629             return;
29630         }
29631         
29632         var file = this.renderPreview(response.data);
29633         
29634         this.files.push(file);
29635         
29636         this.arrange();
29637         
29638         this.fireEvent('afterupload', this, xhr);
29639         
29640     },
29641     
29642     xhrOnError : function(xhr)
29643     {
29644         Roo.log('xhr on error');
29645         
29646         var response = Roo.decode(xhr.responseText);
29647           
29648         Roo.log(response);
29649         
29650         this.arrange();
29651     },
29652     
29653     process : function(file)
29654     {
29655         if(this.fireEvent('process', this, file) !== false){
29656             if(this.editable && file.type.indexOf('image') != -1){
29657                 this.fireEvent('edit', this, file);
29658                 return;
29659             }
29660
29661             this.uploadStart(file, false);
29662
29663             return;
29664         }
29665         
29666     },
29667     
29668     uploadStart : function(file, crop)
29669     {
29670         this.xhr = new XMLHttpRequest();
29671         
29672         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29673             this.arrange();
29674             return;
29675         }
29676         
29677         file.xhr = this.xhr;
29678             
29679         this.managerEl.createChild({
29680             tag : 'div',
29681             cls : 'roo-document-manager-loading',
29682             cn : [
29683                 {
29684                     tag : 'div',
29685                     tooltip : file.name,
29686                     cls : 'roo-document-manager-thumb',
29687                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29688                 }
29689             ]
29690
29691         });
29692
29693         this.xhr.open(this.method, this.url, true);
29694         
29695         var headers = {
29696             "Accept": "application/json",
29697             "Cache-Control": "no-cache",
29698             "X-Requested-With": "XMLHttpRequest"
29699         };
29700         
29701         for (var headerName in headers) {
29702             var headerValue = headers[headerName];
29703             if (headerValue) {
29704                 this.xhr.setRequestHeader(headerName, headerValue);
29705             }
29706         }
29707         
29708         var _this = this;
29709         
29710         this.xhr.onload = function()
29711         {
29712             _this.xhrOnLoad(_this.xhr);
29713         }
29714         
29715         this.xhr.onerror = function()
29716         {
29717             _this.xhrOnError(_this.xhr);
29718         }
29719         
29720         var formData = new FormData();
29721
29722         formData.append('returnHTML', 'NO');
29723         
29724         if(crop){
29725             formData.append('crop', crop);
29726         }
29727         
29728         formData.append(this.paramName, file, file.name);
29729         
29730         var options = {
29731             file : file, 
29732             manually : false
29733         };
29734         
29735         if(this.fireEvent('prepare', this, formData, options) != false){
29736             
29737             if(options.manually){
29738                 return;
29739             }
29740             
29741             this.xhr.send(formData);
29742             return;
29743         };
29744         
29745         this.uploadCancel();
29746     },
29747     
29748     uploadCancel : function()
29749     {
29750         if (this.xhr) {
29751             this.xhr.abort();
29752         }
29753         
29754         this.delegates = [];
29755         
29756         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29757             el.remove();
29758         }, this);
29759         
29760         this.arrange();
29761     },
29762     
29763     renderPreview : function(file)
29764     {
29765         if(typeof(file.target) != 'undefined' && file.target){
29766             return file;
29767         }
29768         
29769         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29770         
29771         var previewEl = this.managerEl.createChild({
29772             tag : 'div',
29773             cls : 'roo-document-manager-preview',
29774             cn : [
29775                 {
29776                     tag : 'div',
29777                     tooltip : file[this.toolTipName],
29778                     cls : 'roo-document-manager-thumb',
29779                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29780                 },
29781                 {
29782                     tag : 'button',
29783                     cls : 'close',
29784                     html : '<i class="fa fa-times-circle"></i>'
29785                 }
29786             ]
29787         });
29788
29789         var close = previewEl.select('button.close', true).first();
29790
29791         close.on('click', this.onRemove, this, file);
29792
29793         file.target = previewEl;
29794
29795         var image = previewEl.select('img', true).first();
29796         
29797         var _this = this;
29798         
29799         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29800         
29801         image.on('click', this.onClick, this, file);
29802         
29803         this.fireEvent('previewrendered', this, file);
29804         
29805         return file;
29806         
29807     },
29808     
29809     onPreviewLoad : function(file, image)
29810     {
29811         if(typeof(file.target) == 'undefined' || !file.target){
29812             return;
29813         }
29814         
29815         var width = image.dom.naturalWidth || image.dom.width;
29816         var height = image.dom.naturalHeight || image.dom.height;
29817         
29818         if(!this.previewResize) {
29819             return;
29820         }
29821         
29822         if(width > height){
29823             file.target.addClass('wide');
29824             return;
29825         }
29826         
29827         file.target.addClass('tall');
29828         return;
29829         
29830     },
29831     
29832     uploadFromSource : function(file, crop)
29833     {
29834         this.xhr = new XMLHttpRequest();
29835         
29836         this.managerEl.createChild({
29837             tag : 'div',
29838             cls : 'roo-document-manager-loading',
29839             cn : [
29840                 {
29841                     tag : 'div',
29842                     tooltip : file.name,
29843                     cls : 'roo-document-manager-thumb',
29844                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29845                 }
29846             ]
29847
29848         });
29849
29850         this.xhr.open(this.method, this.url, true);
29851         
29852         var headers = {
29853             "Accept": "application/json",
29854             "Cache-Control": "no-cache",
29855             "X-Requested-With": "XMLHttpRequest"
29856         };
29857         
29858         for (var headerName in headers) {
29859             var headerValue = headers[headerName];
29860             if (headerValue) {
29861                 this.xhr.setRequestHeader(headerName, headerValue);
29862             }
29863         }
29864         
29865         var _this = this;
29866         
29867         this.xhr.onload = function()
29868         {
29869             _this.xhrOnLoad(_this.xhr);
29870         }
29871         
29872         this.xhr.onerror = function()
29873         {
29874             _this.xhrOnError(_this.xhr);
29875         }
29876         
29877         var formData = new FormData();
29878
29879         formData.append('returnHTML', 'NO');
29880         
29881         formData.append('crop', crop);
29882         
29883         if(typeof(file.filename) != 'undefined'){
29884             formData.append('filename', file.filename);
29885         }
29886         
29887         if(typeof(file.mimetype) != 'undefined'){
29888             formData.append('mimetype', file.mimetype);
29889         }
29890         
29891         Roo.log(formData);
29892         
29893         if(this.fireEvent('prepare', this, formData) != false){
29894             this.xhr.send(formData);
29895         };
29896     }
29897 });
29898
29899 /*
29900 * Licence: LGPL
29901 */
29902
29903 /**
29904  * @class Roo.bootstrap.DocumentViewer
29905  * @extends Roo.bootstrap.Component
29906  * Bootstrap DocumentViewer class
29907  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29908  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29909  * 
29910  * @constructor
29911  * Create a new DocumentViewer
29912  * @param {Object} config The config object
29913  */
29914
29915 Roo.bootstrap.DocumentViewer = function(config){
29916     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29917     
29918     this.addEvents({
29919         /**
29920          * @event initial
29921          * Fire after initEvent
29922          * @param {Roo.bootstrap.DocumentViewer} this
29923          */
29924         "initial" : true,
29925         /**
29926          * @event click
29927          * Fire after click
29928          * @param {Roo.bootstrap.DocumentViewer} this
29929          */
29930         "click" : true,
29931         /**
29932          * @event download
29933          * Fire after download button
29934          * @param {Roo.bootstrap.DocumentViewer} this
29935          */
29936         "download" : true,
29937         /**
29938          * @event trash
29939          * Fire after trash button
29940          * @param {Roo.bootstrap.DocumentViewer} this
29941          */
29942         "trash" : true
29943         
29944     });
29945 };
29946
29947 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29948     
29949     showDownload : true,
29950     
29951     showTrash : true,
29952     
29953     getAutoCreate : function()
29954     {
29955         var cfg = {
29956             tag : 'div',
29957             cls : 'roo-document-viewer',
29958             cn : [
29959                 {
29960                     tag : 'div',
29961                     cls : 'roo-document-viewer-body',
29962                     cn : [
29963                         {
29964                             tag : 'div',
29965                             cls : 'roo-document-viewer-thumb',
29966                             cn : [
29967                                 {
29968                                     tag : 'img',
29969                                     cls : 'roo-document-viewer-image'
29970                                 }
29971                             ]
29972                         }
29973                     ]
29974                 },
29975                 {
29976                     tag : 'div',
29977                     cls : 'roo-document-viewer-footer',
29978                     cn : {
29979                         tag : 'div',
29980                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29981                         cn : [
29982                             {
29983                                 tag : 'div',
29984                                 cls : 'btn-group roo-document-viewer-download',
29985                                 cn : [
29986                                     {
29987                                         tag : 'button',
29988                                         cls : 'btn btn-default',
29989                                         html : '<i class="fa fa-download"></i>'
29990                                     }
29991                                 ]
29992                             },
29993                             {
29994                                 tag : 'div',
29995                                 cls : 'btn-group roo-document-viewer-trash',
29996                                 cn : [
29997                                     {
29998                                         tag : 'button',
29999                                         cls : 'btn btn-default',
30000                                         html : '<i class="fa fa-trash"></i>'
30001                                     }
30002                                 ]
30003                             }
30004                         ]
30005                     }
30006                 }
30007             ]
30008         };
30009         
30010         return cfg;
30011     },
30012     
30013     initEvents : function()
30014     {
30015         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
30016         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
30017         
30018         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
30019         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
30020         
30021         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
30022         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
30023         
30024         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
30025         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
30026         
30027         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
30028         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
30029         
30030         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
30031         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
30032         
30033         this.bodyEl.on('click', this.onClick, this);
30034         this.downloadBtn.on('click', this.onDownload, this);
30035         this.trashBtn.on('click', this.onTrash, this);
30036         
30037         this.downloadBtn.hide();
30038         this.trashBtn.hide();
30039         
30040         if(this.showDownload){
30041             this.downloadBtn.show();
30042         }
30043         
30044         if(this.showTrash){
30045             this.trashBtn.show();
30046         }
30047         
30048         if(!this.showDownload && !this.showTrash) {
30049             this.footerEl.hide();
30050         }
30051         
30052     },
30053     
30054     initial : function()
30055     {
30056         this.fireEvent('initial', this);
30057         
30058     },
30059     
30060     onClick : function(e)
30061     {
30062         e.preventDefault();
30063         
30064         this.fireEvent('click', this);
30065     },
30066     
30067     onDownload : function(e)
30068     {
30069         e.preventDefault();
30070         
30071         this.fireEvent('download', this);
30072     },
30073     
30074     onTrash : function(e)
30075     {
30076         e.preventDefault();
30077         
30078         this.fireEvent('trash', this);
30079     }
30080     
30081 });
30082 /*
30083  * - LGPL
30084  *
30085  * nav progress bar
30086  * 
30087  */
30088
30089 /**
30090  * @class Roo.bootstrap.NavProgressBar
30091  * @extends Roo.bootstrap.Component
30092  * Bootstrap NavProgressBar class
30093  * 
30094  * @constructor
30095  * Create a new nav progress bar
30096  * @param {Object} config The config object
30097  */
30098
30099 Roo.bootstrap.NavProgressBar = function(config){
30100     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
30101
30102     this.bullets = this.bullets || [];
30103    
30104 //    Roo.bootstrap.NavProgressBar.register(this);
30105      this.addEvents({
30106         /**
30107              * @event changed
30108              * Fires when the active item changes
30109              * @param {Roo.bootstrap.NavProgressBar} this
30110              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
30111              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
30112          */
30113         'changed': true
30114      });
30115     
30116 };
30117
30118 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
30119     
30120     bullets : [],
30121     barItems : [],
30122     
30123     getAutoCreate : function()
30124     {
30125         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
30126         
30127         cfg = {
30128             tag : 'div',
30129             cls : 'roo-navigation-bar-group',
30130             cn : [
30131                 {
30132                     tag : 'div',
30133                     cls : 'roo-navigation-top-bar'
30134                 },
30135                 {
30136                     tag : 'div',
30137                     cls : 'roo-navigation-bullets-bar',
30138                     cn : [
30139                         {
30140                             tag : 'ul',
30141                             cls : 'roo-navigation-bar'
30142                         }
30143                     ]
30144                 },
30145                 
30146                 {
30147                     tag : 'div',
30148                     cls : 'roo-navigation-bottom-bar'
30149                 }
30150             ]
30151             
30152         };
30153         
30154         return cfg;
30155         
30156     },
30157     
30158     initEvents: function() 
30159     {
30160         
30161     },
30162     
30163     onRender : function(ct, position) 
30164     {
30165         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30166         
30167         if(this.bullets.length){
30168             Roo.each(this.bullets, function(b){
30169                this.addItem(b);
30170             }, this);
30171         }
30172         
30173         this.format();
30174         
30175     },
30176     
30177     addItem : function(cfg)
30178     {
30179         var item = new Roo.bootstrap.NavProgressItem(cfg);
30180         
30181         item.parentId = this.id;
30182         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
30183         
30184         if(cfg.html){
30185             var top = new Roo.bootstrap.Element({
30186                 tag : 'div',
30187                 cls : 'roo-navigation-bar-text'
30188             });
30189             
30190             var bottom = new Roo.bootstrap.Element({
30191                 tag : 'div',
30192                 cls : 'roo-navigation-bar-text'
30193             });
30194             
30195             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
30196             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
30197             
30198             var topText = new Roo.bootstrap.Element({
30199                 tag : 'span',
30200                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
30201             });
30202             
30203             var bottomText = new Roo.bootstrap.Element({
30204                 tag : 'span',
30205                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30206             });
30207             
30208             topText.onRender(top.el, null);
30209             bottomText.onRender(bottom.el, null);
30210             
30211             item.topEl = top;
30212             item.bottomEl = bottom;
30213         }
30214         
30215         this.barItems.push(item);
30216         
30217         return item;
30218     },
30219     
30220     getActive : function()
30221     {
30222         var active = false;
30223         
30224         Roo.each(this.barItems, function(v){
30225             
30226             if (!v.isActive()) {
30227                 return;
30228             }
30229             
30230             active = v;
30231             return false;
30232             
30233         });
30234         
30235         return active;
30236     },
30237     
30238     setActiveItem : function(item)
30239     {
30240         var prev = false;
30241         
30242         Roo.each(this.barItems, function(v){
30243             if (v.rid == item.rid) {
30244                 return ;
30245             }
30246             
30247             if (v.isActive()) {
30248                 v.setActive(false);
30249                 prev = v;
30250             }
30251         });
30252
30253         item.setActive(true);
30254         
30255         this.fireEvent('changed', this, item, prev);
30256     },
30257     
30258     getBarItem: function(rid)
30259     {
30260         var ret = false;
30261         
30262         Roo.each(this.barItems, function(e) {
30263             if (e.rid != rid) {
30264                 return;
30265             }
30266             
30267             ret =  e;
30268             return false;
30269         });
30270         
30271         return ret;
30272     },
30273     
30274     indexOfItem : function(item)
30275     {
30276         var index = false;
30277         
30278         Roo.each(this.barItems, function(v, i){
30279             
30280             if (v.rid != item.rid) {
30281                 return;
30282             }
30283             
30284             index = i;
30285             return false
30286         });
30287         
30288         return index;
30289     },
30290     
30291     setActiveNext : function()
30292     {
30293         var i = this.indexOfItem(this.getActive());
30294         
30295         if (i > this.barItems.length) {
30296             return;
30297         }
30298         
30299         this.setActiveItem(this.barItems[i+1]);
30300     },
30301     
30302     setActivePrev : function()
30303     {
30304         var i = this.indexOfItem(this.getActive());
30305         
30306         if (i  < 1) {
30307             return;
30308         }
30309         
30310         this.setActiveItem(this.barItems[i-1]);
30311     },
30312     
30313     format : function()
30314     {
30315         if(!this.barItems.length){
30316             return;
30317         }
30318      
30319         var width = 100 / this.barItems.length;
30320         
30321         Roo.each(this.barItems, function(i){
30322             i.el.setStyle('width', width + '%');
30323             i.topEl.el.setStyle('width', width + '%');
30324             i.bottomEl.el.setStyle('width', width + '%');
30325         }, this);
30326         
30327     }
30328     
30329 });
30330 /*
30331  * - LGPL
30332  *
30333  * Nav Progress Item
30334  * 
30335  */
30336
30337 /**
30338  * @class Roo.bootstrap.NavProgressItem
30339  * @extends Roo.bootstrap.Component
30340  * Bootstrap NavProgressItem class
30341  * @cfg {String} rid the reference id
30342  * @cfg {Boolean} active (true|false) Is item active default false
30343  * @cfg {Boolean} disabled (true|false) Is item active default false
30344  * @cfg {String} html
30345  * @cfg {String} position (top|bottom) text position default bottom
30346  * @cfg {String} icon show icon instead of number
30347  * 
30348  * @constructor
30349  * Create a new NavProgressItem
30350  * @param {Object} config The config object
30351  */
30352 Roo.bootstrap.NavProgressItem = function(config){
30353     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30354     this.addEvents({
30355         // raw events
30356         /**
30357          * @event click
30358          * The raw click event for the entire grid.
30359          * @param {Roo.bootstrap.NavProgressItem} this
30360          * @param {Roo.EventObject} e
30361          */
30362         "click" : true
30363     });
30364    
30365 };
30366
30367 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30368     
30369     rid : '',
30370     active : false,
30371     disabled : false,
30372     html : '',
30373     position : 'bottom',
30374     icon : false,
30375     
30376     getAutoCreate : function()
30377     {
30378         var iconCls = 'roo-navigation-bar-item-icon';
30379         
30380         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30381         
30382         var cfg = {
30383             tag: 'li',
30384             cls: 'roo-navigation-bar-item',
30385             cn : [
30386                 {
30387                     tag : 'i',
30388                     cls : iconCls
30389                 }
30390             ]
30391         };
30392         
30393         if(this.active){
30394             cfg.cls += ' active';
30395         }
30396         if(this.disabled){
30397             cfg.cls += ' disabled';
30398         }
30399         
30400         return cfg;
30401     },
30402     
30403     disable : function()
30404     {
30405         this.setDisabled(true);
30406     },
30407     
30408     enable : function()
30409     {
30410         this.setDisabled(false);
30411     },
30412     
30413     initEvents: function() 
30414     {
30415         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30416         
30417         this.iconEl.on('click', this.onClick, this);
30418     },
30419     
30420     onClick : function(e)
30421     {
30422         e.preventDefault();
30423         
30424         if(this.disabled){
30425             return;
30426         }
30427         
30428         if(this.fireEvent('click', this, e) === false){
30429             return;
30430         };
30431         
30432         this.parent().setActiveItem(this);
30433     },
30434     
30435     isActive: function () 
30436     {
30437         return this.active;
30438     },
30439     
30440     setActive : function(state)
30441     {
30442         if(this.active == state){
30443             return;
30444         }
30445         
30446         this.active = state;
30447         
30448         if (state) {
30449             this.el.addClass('active');
30450             return;
30451         }
30452         
30453         this.el.removeClass('active');
30454         
30455         return;
30456     },
30457     
30458     setDisabled : function(state)
30459     {
30460         if(this.disabled == state){
30461             return;
30462         }
30463         
30464         this.disabled = state;
30465         
30466         if (state) {
30467             this.el.addClass('disabled');
30468             return;
30469         }
30470         
30471         this.el.removeClass('disabled');
30472     },
30473     
30474     tooltipEl : function()
30475     {
30476         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30477     }
30478 });
30479  
30480
30481  /*
30482  * - LGPL
30483  *
30484  * FieldLabel
30485  * 
30486  */
30487
30488 /**
30489  * @class Roo.bootstrap.FieldLabel
30490  * @extends Roo.bootstrap.Component
30491  * Bootstrap FieldLabel class
30492  * @cfg {String} html contents of the element
30493  * @cfg {String} tag tag of the element default label
30494  * @cfg {String} cls class of the element
30495  * @cfg {String} target label target 
30496  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30497  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
30498  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
30499  * @cfg {String} iconTooltip default "This field is required"
30500  * @cfg {String} indicatorpos (left|right) default left
30501  * 
30502  * @constructor
30503  * Create a new FieldLabel
30504  * @param {Object} config The config object
30505  */
30506
30507 Roo.bootstrap.FieldLabel = function(config){
30508     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30509     
30510     this.addEvents({
30511             /**
30512              * @event invalid
30513              * Fires after the field has been marked as invalid.
30514              * @param {Roo.form.FieldLabel} this
30515              * @param {String} msg The validation message
30516              */
30517             invalid : true,
30518             /**
30519              * @event valid
30520              * Fires after the field has been validated with no errors.
30521              * @param {Roo.form.FieldLabel} this
30522              */
30523             valid : true
30524         });
30525 };
30526
30527 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30528     
30529     tag: 'label',
30530     cls: '',
30531     html: '',
30532     target: '',
30533     allowBlank : true,
30534     invalidClass : 'has-warning',
30535     validClass : 'has-success',
30536     iconTooltip : 'This field is required',
30537     indicatorpos : 'left',
30538     
30539     getAutoCreate : function(){
30540         
30541         var cls = "";
30542         if (!this.allowBlank) {
30543             cls  = "visible";
30544         }
30545         
30546         var cfg = {
30547             tag : this.tag,
30548             cls : 'roo-bootstrap-field-label ' + this.cls,
30549             for : this.target,
30550             cn : [
30551                 {
30552                     tag : 'i',
30553                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30554                     tooltip : this.iconTooltip
30555                 },
30556                 {
30557                     tag : 'span',
30558                     html : this.html
30559                 }
30560             ] 
30561         };
30562         
30563         if(this.indicatorpos == 'right'){
30564             var cfg = {
30565                 tag : this.tag,
30566                 cls : 'roo-bootstrap-field-label ' + this.cls,
30567                 for : this.target,
30568                 cn : [
30569                     {
30570                         tag : 'span',
30571                         html : this.html
30572                     },
30573                     {
30574                         tag : 'i',
30575                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30576                         tooltip : this.iconTooltip
30577                     }
30578                 ] 
30579             };
30580         }
30581         
30582         return cfg;
30583     },
30584     
30585     initEvents: function() 
30586     {
30587         Roo.bootstrap.Element.superclass.initEvents.call(this);
30588         
30589         this.indicator = this.indicatorEl();
30590         
30591         if(this.indicator){
30592             this.indicator.removeClass('visible');
30593             this.indicator.addClass('invisible');
30594         }
30595         
30596         Roo.bootstrap.FieldLabel.register(this);
30597     },
30598     
30599     indicatorEl : function()
30600     {
30601         var indicator = this.el.select('i.roo-required-indicator',true).first();
30602         
30603         if(!indicator){
30604             return false;
30605         }
30606         
30607         return indicator;
30608         
30609     },
30610     
30611     /**
30612      * Mark this field as valid
30613      */
30614     markValid : function()
30615     {
30616         if(this.indicator){
30617             this.indicator.removeClass('visible');
30618             this.indicator.addClass('invisible');
30619         }
30620         if (Roo.bootstrap.version == 3) {
30621             this.el.removeClass(this.invalidClass);
30622             this.el.addClass(this.validClass);
30623         } else {
30624             this.el.removeClass('is-invalid');
30625             this.el.addClass('is-valid');
30626         }
30627         
30628         
30629         this.fireEvent('valid', this);
30630     },
30631     
30632     /**
30633      * Mark this field as invalid
30634      * @param {String} msg The validation message
30635      */
30636     markInvalid : function(msg)
30637     {
30638         if(this.indicator){
30639             this.indicator.removeClass('invisible');
30640             this.indicator.addClass('visible');
30641         }
30642           if (Roo.bootstrap.version == 3) {
30643             this.el.removeClass(this.validClass);
30644             this.el.addClass(this.invalidClass);
30645         } else {
30646             this.el.removeClass('is-valid');
30647             this.el.addClass('is-invalid');
30648         }
30649         
30650         
30651         this.fireEvent('invalid', this, msg);
30652     }
30653     
30654    
30655 });
30656
30657 Roo.apply(Roo.bootstrap.FieldLabel, {
30658     
30659     groups: {},
30660     
30661      /**
30662     * register a FieldLabel Group
30663     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30664     */
30665     register : function(label)
30666     {
30667         if(this.groups.hasOwnProperty(label.target)){
30668             return;
30669         }
30670      
30671         this.groups[label.target] = label;
30672         
30673     },
30674     /**
30675     * fetch a FieldLabel Group based on the target
30676     * @param {string} target
30677     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30678     */
30679     get: function(target) {
30680         if (typeof(this.groups[target]) == 'undefined') {
30681             return false;
30682         }
30683         
30684         return this.groups[target] ;
30685     }
30686 });
30687
30688  
30689
30690  /*
30691  * - LGPL
30692  *
30693  * page DateSplitField.
30694  * 
30695  */
30696
30697
30698 /**
30699  * @class Roo.bootstrap.DateSplitField
30700  * @extends Roo.bootstrap.Component
30701  * Bootstrap DateSplitField class
30702  * @cfg {string} fieldLabel - the label associated
30703  * @cfg {Number} labelWidth set the width of label (0-12)
30704  * @cfg {String} labelAlign (top|left)
30705  * @cfg {Boolean} dayAllowBlank (true|false) default false
30706  * @cfg {Boolean} monthAllowBlank (true|false) default false
30707  * @cfg {Boolean} yearAllowBlank (true|false) default false
30708  * @cfg {string} dayPlaceholder 
30709  * @cfg {string} monthPlaceholder
30710  * @cfg {string} yearPlaceholder
30711  * @cfg {string} dayFormat default 'd'
30712  * @cfg {string} monthFormat default 'm'
30713  * @cfg {string} yearFormat default 'Y'
30714  * @cfg {Number} labellg set the width of label (1-12)
30715  * @cfg {Number} labelmd set the width of label (1-12)
30716  * @cfg {Number} labelsm set the width of label (1-12)
30717  * @cfg {Number} labelxs set the width of label (1-12)
30718
30719  *     
30720  * @constructor
30721  * Create a new DateSplitField
30722  * @param {Object} config The config object
30723  */
30724
30725 Roo.bootstrap.DateSplitField = function(config){
30726     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30727     
30728     this.addEvents({
30729         // raw events
30730          /**
30731          * @event years
30732          * getting the data of years
30733          * @param {Roo.bootstrap.DateSplitField} this
30734          * @param {Object} years
30735          */
30736         "years" : true,
30737         /**
30738          * @event days
30739          * getting the data of days
30740          * @param {Roo.bootstrap.DateSplitField} this
30741          * @param {Object} days
30742          */
30743         "days" : true,
30744         /**
30745          * @event invalid
30746          * Fires after the field has been marked as invalid.
30747          * @param {Roo.form.Field} this
30748          * @param {String} msg The validation message
30749          */
30750         invalid : true,
30751        /**
30752          * @event valid
30753          * Fires after the field has been validated with no errors.
30754          * @param {Roo.form.Field} this
30755          */
30756         valid : true
30757     });
30758 };
30759
30760 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30761     
30762     fieldLabel : '',
30763     labelAlign : 'top',
30764     labelWidth : 3,
30765     dayAllowBlank : false,
30766     monthAllowBlank : false,
30767     yearAllowBlank : false,
30768     dayPlaceholder : '',
30769     monthPlaceholder : '',
30770     yearPlaceholder : '',
30771     dayFormat : 'd',
30772     monthFormat : 'm',
30773     yearFormat : 'Y',
30774     isFormField : true,
30775     labellg : 0,
30776     labelmd : 0,
30777     labelsm : 0,
30778     labelxs : 0,
30779     
30780     getAutoCreate : function()
30781     {
30782         var cfg = {
30783             tag : 'div',
30784             cls : 'row roo-date-split-field-group',
30785             cn : [
30786                 {
30787                     tag : 'input',
30788                     type : 'hidden',
30789                     cls : 'form-hidden-field roo-date-split-field-group-value',
30790                     name : this.name
30791                 }
30792             ]
30793         };
30794         
30795         var labelCls = 'col-md-12';
30796         var contentCls = 'col-md-4';
30797         
30798         if(this.fieldLabel){
30799             
30800             var label = {
30801                 tag : 'div',
30802                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30803                 cn : [
30804                     {
30805                         tag : 'label',
30806                         html : this.fieldLabel
30807                     }
30808                 ]
30809             };
30810             
30811             if(this.labelAlign == 'left'){
30812             
30813                 if(this.labelWidth > 12){
30814                     label.style = "width: " + this.labelWidth + 'px';
30815                 }
30816
30817                 if(this.labelWidth < 13 && this.labelmd == 0){
30818                     this.labelmd = this.labelWidth;
30819                 }
30820
30821                 if(this.labellg > 0){
30822                     labelCls = ' col-lg-' + this.labellg;
30823                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30824                 }
30825
30826                 if(this.labelmd > 0){
30827                     labelCls = ' col-md-' + this.labelmd;
30828                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30829                 }
30830
30831                 if(this.labelsm > 0){
30832                     labelCls = ' col-sm-' + this.labelsm;
30833                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30834                 }
30835
30836                 if(this.labelxs > 0){
30837                     labelCls = ' col-xs-' + this.labelxs;
30838                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30839                 }
30840             }
30841             
30842             label.cls += ' ' + labelCls;
30843             
30844             cfg.cn.push(label);
30845         }
30846         
30847         Roo.each(['day', 'month', 'year'], function(t){
30848             cfg.cn.push({
30849                 tag : 'div',
30850                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30851             });
30852         }, this);
30853         
30854         return cfg;
30855     },
30856     
30857     inputEl: function ()
30858     {
30859         return this.el.select('.roo-date-split-field-group-value', true).first();
30860     },
30861     
30862     onRender : function(ct, position) 
30863     {
30864         var _this = this;
30865         
30866         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30867         
30868         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30869         
30870         this.dayField = new Roo.bootstrap.ComboBox({
30871             allowBlank : this.dayAllowBlank,
30872             alwaysQuery : true,
30873             displayField : 'value',
30874             editable : false,
30875             fieldLabel : '',
30876             forceSelection : true,
30877             mode : 'local',
30878             placeholder : this.dayPlaceholder,
30879             selectOnFocus : true,
30880             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30881             triggerAction : 'all',
30882             typeAhead : true,
30883             valueField : 'value',
30884             store : new Roo.data.SimpleStore({
30885                 data : (function() {    
30886                     var days = [];
30887                     _this.fireEvent('days', _this, days);
30888                     return days;
30889                 })(),
30890                 fields : [ 'value' ]
30891             }),
30892             listeners : {
30893                 select : function (_self, record, index)
30894                 {
30895                     _this.setValue(_this.getValue());
30896                 }
30897             }
30898         });
30899
30900         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30901         
30902         this.monthField = new Roo.bootstrap.MonthField({
30903             after : '<i class=\"fa fa-calendar\"></i>',
30904             allowBlank : this.monthAllowBlank,
30905             placeholder : this.monthPlaceholder,
30906             readOnly : true,
30907             listeners : {
30908                 render : function (_self)
30909                 {
30910                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30911                         e.preventDefault();
30912                         _self.focus();
30913                     });
30914                 },
30915                 select : function (_self, oldvalue, newvalue)
30916                 {
30917                     _this.setValue(_this.getValue());
30918                 }
30919             }
30920         });
30921         
30922         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30923         
30924         this.yearField = new Roo.bootstrap.ComboBox({
30925             allowBlank : this.yearAllowBlank,
30926             alwaysQuery : true,
30927             displayField : 'value',
30928             editable : false,
30929             fieldLabel : '',
30930             forceSelection : true,
30931             mode : 'local',
30932             placeholder : this.yearPlaceholder,
30933             selectOnFocus : true,
30934             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30935             triggerAction : 'all',
30936             typeAhead : true,
30937             valueField : 'value',
30938             store : new Roo.data.SimpleStore({
30939                 data : (function() {
30940                     var years = [];
30941                     _this.fireEvent('years', _this, years);
30942                     return years;
30943                 })(),
30944                 fields : [ 'value' ]
30945             }),
30946             listeners : {
30947                 select : function (_self, record, index)
30948                 {
30949                     _this.setValue(_this.getValue());
30950                 }
30951             }
30952         });
30953
30954         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30955     },
30956     
30957     setValue : function(v, format)
30958     {
30959         this.inputEl.dom.value = v;
30960         
30961         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30962         
30963         var d = Date.parseDate(v, f);
30964         
30965         if(!d){
30966             this.validate();
30967             return;
30968         }
30969         
30970         this.setDay(d.format(this.dayFormat));
30971         this.setMonth(d.format(this.monthFormat));
30972         this.setYear(d.format(this.yearFormat));
30973         
30974         this.validate();
30975         
30976         return;
30977     },
30978     
30979     setDay : function(v)
30980     {
30981         this.dayField.setValue(v);
30982         this.inputEl.dom.value = this.getValue();
30983         this.validate();
30984         return;
30985     },
30986     
30987     setMonth : function(v)
30988     {
30989         this.monthField.setValue(v, true);
30990         this.inputEl.dom.value = this.getValue();
30991         this.validate();
30992         return;
30993     },
30994     
30995     setYear : function(v)
30996     {
30997         this.yearField.setValue(v);
30998         this.inputEl.dom.value = this.getValue();
30999         this.validate();
31000         return;
31001     },
31002     
31003     getDay : function()
31004     {
31005         return this.dayField.getValue();
31006     },
31007     
31008     getMonth : function()
31009     {
31010         return this.monthField.getValue();
31011     },
31012     
31013     getYear : function()
31014     {
31015         return this.yearField.getValue();
31016     },
31017     
31018     getValue : function()
31019     {
31020         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
31021         
31022         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
31023         
31024         return date;
31025     },
31026     
31027     reset : function()
31028     {
31029         this.setDay('');
31030         this.setMonth('');
31031         this.setYear('');
31032         this.inputEl.dom.value = '';
31033         this.validate();
31034         return;
31035     },
31036     
31037     validate : function()
31038     {
31039         var d = this.dayField.validate();
31040         var m = this.monthField.validate();
31041         var y = this.yearField.validate();
31042         
31043         var valid = true;
31044         
31045         if(
31046                 (!this.dayAllowBlank && !d) ||
31047                 (!this.monthAllowBlank && !m) ||
31048                 (!this.yearAllowBlank && !y)
31049         ){
31050             valid = false;
31051         }
31052         
31053         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
31054             return valid;
31055         }
31056         
31057         if(valid){
31058             this.markValid();
31059             return valid;
31060         }
31061         
31062         this.markInvalid();
31063         
31064         return valid;
31065     },
31066     
31067     markValid : function()
31068     {
31069         
31070         var label = this.el.select('label', true).first();
31071         var icon = this.el.select('i.fa-star', true).first();
31072
31073         if(label && icon){
31074             icon.remove();
31075         }
31076         
31077         this.fireEvent('valid', this);
31078     },
31079     
31080      /**
31081      * Mark this field as invalid
31082      * @param {String} msg The validation message
31083      */
31084     markInvalid : function(msg)
31085     {
31086         
31087         var label = this.el.select('label', true).first();
31088         var icon = this.el.select('i.fa-star', true).first();
31089
31090         if(label && !icon){
31091             this.el.select('.roo-date-split-field-label', true).createChild({
31092                 tag : 'i',
31093                 cls : 'text-danger fa fa-lg fa-star',
31094                 tooltip : 'This field is required',
31095                 style : 'margin-right:5px;'
31096             }, label, true);
31097         }
31098         
31099         this.fireEvent('invalid', this, msg);
31100     },
31101     
31102     clearInvalid : function()
31103     {
31104         var label = this.el.select('label', true).first();
31105         var icon = this.el.select('i.fa-star', true).first();
31106
31107         if(label && icon){
31108             icon.remove();
31109         }
31110         
31111         this.fireEvent('valid', this);
31112     },
31113     
31114     getName: function()
31115     {
31116         return this.name;
31117     }
31118     
31119 });
31120
31121  /**
31122  *
31123  * This is based on 
31124  * http://masonry.desandro.com
31125  *
31126  * The idea is to render all the bricks based on vertical width...
31127  *
31128  * The original code extends 'outlayer' - we might need to use that....
31129  * 
31130  */
31131
31132
31133 /**
31134  * @class Roo.bootstrap.LayoutMasonry
31135  * @extends Roo.bootstrap.Component
31136  * Bootstrap Layout Masonry class
31137  * 
31138  * @constructor
31139  * Create a new Element
31140  * @param {Object} config The config object
31141  */
31142
31143 Roo.bootstrap.LayoutMasonry = function(config){
31144     
31145     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
31146     
31147     this.bricks = [];
31148     
31149     Roo.bootstrap.LayoutMasonry.register(this);
31150     
31151     this.addEvents({
31152         // raw events
31153         /**
31154          * @event layout
31155          * Fire after layout the items
31156          * @param {Roo.bootstrap.LayoutMasonry} this
31157          * @param {Roo.EventObject} e
31158          */
31159         "layout" : true
31160     });
31161     
31162 };
31163
31164 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
31165     
31166     /**
31167      * @cfg {Boolean} isLayoutInstant = no animation?
31168      */   
31169     isLayoutInstant : false, // needed?
31170    
31171     /**
31172      * @cfg {Number} boxWidth  width of the columns
31173      */   
31174     boxWidth : 450,
31175     
31176       /**
31177      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
31178      */   
31179     boxHeight : 0,
31180     
31181     /**
31182      * @cfg {Number} padWidth padding below box..
31183      */   
31184     padWidth : 10, 
31185     
31186     /**
31187      * @cfg {Number} gutter gutter width..
31188      */   
31189     gutter : 10,
31190     
31191      /**
31192      * @cfg {Number} maxCols maximum number of columns
31193      */   
31194     
31195     maxCols: 0,
31196     
31197     /**
31198      * @cfg {Boolean} isAutoInitial defalut true
31199      */   
31200     isAutoInitial : true, 
31201     
31202     containerWidth: 0,
31203     
31204     /**
31205      * @cfg {Boolean} isHorizontal defalut false
31206      */   
31207     isHorizontal : false, 
31208
31209     currentSize : null,
31210     
31211     tag: 'div',
31212     
31213     cls: '',
31214     
31215     bricks: null, //CompositeElement
31216     
31217     cols : 1,
31218     
31219     _isLayoutInited : false,
31220     
31221 //    isAlternative : false, // only use for vertical layout...
31222     
31223     /**
31224      * @cfg {Number} alternativePadWidth padding below box..
31225      */   
31226     alternativePadWidth : 50,
31227     
31228     selectedBrick : [],
31229     
31230     getAutoCreate : function(){
31231         
31232         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31233         
31234         var cfg = {
31235             tag: this.tag,
31236             cls: 'blog-masonary-wrapper ' + this.cls,
31237             cn : {
31238                 cls : 'mas-boxes masonary'
31239             }
31240         };
31241         
31242         return cfg;
31243     },
31244     
31245     getChildContainer: function( )
31246     {
31247         if (this.boxesEl) {
31248             return this.boxesEl;
31249         }
31250         
31251         this.boxesEl = this.el.select('.mas-boxes').first();
31252         
31253         return this.boxesEl;
31254     },
31255     
31256     
31257     initEvents : function()
31258     {
31259         var _this = this;
31260         
31261         if(this.isAutoInitial){
31262             Roo.log('hook children rendered');
31263             this.on('childrenrendered', function() {
31264                 Roo.log('children rendered');
31265                 _this.initial();
31266             } ,this);
31267         }
31268     },
31269     
31270     initial : function()
31271     {
31272         this.selectedBrick = [];
31273         
31274         this.currentSize = this.el.getBox(true);
31275         
31276         Roo.EventManager.onWindowResize(this.resize, this); 
31277
31278         if(!this.isAutoInitial){
31279             this.layout();
31280             return;
31281         }
31282         
31283         this.layout();
31284         
31285         return;
31286         //this.layout.defer(500,this);
31287         
31288     },
31289     
31290     resize : function()
31291     {
31292         var cs = this.el.getBox(true);
31293         
31294         if (
31295                 this.currentSize.width == cs.width && 
31296                 this.currentSize.x == cs.x && 
31297                 this.currentSize.height == cs.height && 
31298                 this.currentSize.y == cs.y 
31299         ) {
31300             Roo.log("no change in with or X or Y");
31301             return;
31302         }
31303         
31304         this.currentSize = cs;
31305         
31306         this.layout();
31307         
31308     },
31309     
31310     layout : function()
31311     {   
31312         this._resetLayout();
31313         
31314         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31315         
31316         this.layoutItems( isInstant );
31317       
31318         this._isLayoutInited = true;
31319         
31320         this.fireEvent('layout', this);
31321         
31322     },
31323     
31324     _resetLayout : function()
31325     {
31326         if(this.isHorizontal){
31327             this.horizontalMeasureColumns();
31328             return;
31329         }
31330         
31331         this.verticalMeasureColumns();
31332         
31333     },
31334     
31335     verticalMeasureColumns : function()
31336     {
31337         this.getContainerWidth();
31338         
31339 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31340 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31341 //            return;
31342 //        }
31343         
31344         var boxWidth = this.boxWidth + this.padWidth;
31345         
31346         if(this.containerWidth < this.boxWidth){
31347             boxWidth = this.containerWidth
31348         }
31349         
31350         var containerWidth = this.containerWidth;
31351         
31352         var cols = Math.floor(containerWidth / boxWidth);
31353         
31354         this.cols = Math.max( cols, 1 );
31355         
31356         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31357         
31358         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31359         
31360         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31361         
31362         this.colWidth = boxWidth + avail - this.padWidth;
31363         
31364         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31365         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31366     },
31367     
31368     horizontalMeasureColumns : function()
31369     {
31370         this.getContainerWidth();
31371         
31372         var boxWidth = this.boxWidth;
31373         
31374         if(this.containerWidth < boxWidth){
31375             boxWidth = this.containerWidth;
31376         }
31377         
31378         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31379         
31380         this.el.setHeight(boxWidth);
31381         
31382     },
31383     
31384     getContainerWidth : function()
31385     {
31386         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31387     },
31388     
31389     layoutItems : function( isInstant )
31390     {
31391         Roo.log(this.bricks);
31392         
31393         var items = Roo.apply([], this.bricks);
31394         
31395         if(this.isHorizontal){
31396             this._horizontalLayoutItems( items , isInstant );
31397             return;
31398         }
31399         
31400 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31401 //            this._verticalAlternativeLayoutItems( items , isInstant );
31402 //            return;
31403 //        }
31404         
31405         this._verticalLayoutItems( items , isInstant );
31406         
31407     },
31408     
31409     _verticalLayoutItems : function ( items , isInstant)
31410     {
31411         if ( !items || !items.length ) {
31412             return;
31413         }
31414         
31415         var standard = [
31416             ['xs', 'xs', 'xs', 'tall'],
31417             ['xs', 'xs', 'tall'],
31418             ['xs', 'xs', 'sm'],
31419             ['xs', 'xs', 'xs'],
31420             ['xs', 'tall'],
31421             ['xs', 'sm'],
31422             ['xs', 'xs'],
31423             ['xs'],
31424             
31425             ['sm', 'xs', 'xs'],
31426             ['sm', 'xs'],
31427             ['sm'],
31428             
31429             ['tall', 'xs', 'xs', 'xs'],
31430             ['tall', 'xs', 'xs'],
31431             ['tall', 'xs'],
31432             ['tall']
31433             
31434         ];
31435         
31436         var queue = [];
31437         
31438         var boxes = [];
31439         
31440         var box = [];
31441         
31442         Roo.each(items, function(item, k){
31443             
31444             switch (item.size) {
31445                 // these layouts take up a full box,
31446                 case 'md' :
31447                 case 'md-left' :
31448                 case 'md-right' :
31449                 case 'wide' :
31450                     
31451                     if(box.length){
31452                         boxes.push(box);
31453                         box = [];
31454                     }
31455                     
31456                     boxes.push([item]);
31457                     
31458                     break;
31459                     
31460                 case 'xs' :
31461                 case 'sm' :
31462                 case 'tall' :
31463                     
31464                     box.push(item);
31465                     
31466                     break;
31467                 default :
31468                     break;
31469                     
31470             }
31471             
31472         }, this);
31473         
31474         if(box.length){
31475             boxes.push(box);
31476             box = [];
31477         }
31478         
31479         var filterPattern = function(box, length)
31480         {
31481             if(!box.length){
31482                 return;
31483             }
31484             
31485             var match = false;
31486             
31487             var pattern = box.slice(0, length);
31488             
31489             var format = [];
31490             
31491             Roo.each(pattern, function(i){
31492                 format.push(i.size);
31493             }, this);
31494             
31495             Roo.each(standard, function(s){
31496                 
31497                 if(String(s) != String(format)){
31498                     return;
31499                 }
31500                 
31501                 match = true;
31502                 return false;
31503                 
31504             }, this);
31505             
31506             if(!match && length == 1){
31507                 return;
31508             }
31509             
31510             if(!match){
31511                 filterPattern(box, length - 1);
31512                 return;
31513             }
31514                 
31515             queue.push(pattern);
31516
31517             box = box.slice(length, box.length);
31518
31519             filterPattern(box, 4);
31520
31521             return;
31522             
31523         }
31524         
31525         Roo.each(boxes, function(box, k){
31526             
31527             if(!box.length){
31528                 return;
31529             }
31530             
31531             if(box.length == 1){
31532                 queue.push(box);
31533                 return;
31534             }
31535             
31536             filterPattern(box, 4);
31537             
31538         }, this);
31539         
31540         this._processVerticalLayoutQueue( queue, isInstant );
31541         
31542     },
31543     
31544 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31545 //    {
31546 //        if ( !items || !items.length ) {
31547 //            return;
31548 //        }
31549 //
31550 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31551 //        
31552 //    },
31553     
31554     _horizontalLayoutItems : function ( items , isInstant)
31555     {
31556         if ( !items || !items.length || items.length < 3) {
31557             return;
31558         }
31559         
31560         items.reverse();
31561         
31562         var eItems = items.slice(0, 3);
31563         
31564         items = items.slice(3, items.length);
31565         
31566         var standard = [
31567             ['xs', 'xs', 'xs', 'wide'],
31568             ['xs', 'xs', 'wide'],
31569             ['xs', 'xs', 'sm'],
31570             ['xs', 'xs', 'xs'],
31571             ['xs', 'wide'],
31572             ['xs', 'sm'],
31573             ['xs', 'xs'],
31574             ['xs'],
31575             
31576             ['sm', 'xs', 'xs'],
31577             ['sm', 'xs'],
31578             ['sm'],
31579             
31580             ['wide', 'xs', 'xs', 'xs'],
31581             ['wide', 'xs', 'xs'],
31582             ['wide', 'xs'],
31583             ['wide'],
31584             
31585             ['wide-thin']
31586         ];
31587         
31588         var queue = [];
31589         
31590         var boxes = [];
31591         
31592         var box = [];
31593         
31594         Roo.each(items, function(item, k){
31595             
31596             switch (item.size) {
31597                 case 'md' :
31598                 case 'md-left' :
31599                 case 'md-right' :
31600                 case 'tall' :
31601                     
31602                     if(box.length){
31603                         boxes.push(box);
31604                         box = [];
31605                     }
31606                     
31607                     boxes.push([item]);
31608                     
31609                     break;
31610                     
31611                 case 'xs' :
31612                 case 'sm' :
31613                 case 'wide' :
31614                 case 'wide-thin' :
31615                     
31616                     box.push(item);
31617                     
31618                     break;
31619                 default :
31620                     break;
31621                     
31622             }
31623             
31624         }, this);
31625         
31626         if(box.length){
31627             boxes.push(box);
31628             box = [];
31629         }
31630         
31631         var filterPattern = function(box, length)
31632         {
31633             if(!box.length){
31634                 return;
31635             }
31636             
31637             var match = false;
31638             
31639             var pattern = box.slice(0, length);
31640             
31641             var format = [];
31642             
31643             Roo.each(pattern, function(i){
31644                 format.push(i.size);
31645             }, this);
31646             
31647             Roo.each(standard, function(s){
31648                 
31649                 if(String(s) != String(format)){
31650                     return;
31651                 }
31652                 
31653                 match = true;
31654                 return false;
31655                 
31656             }, this);
31657             
31658             if(!match && length == 1){
31659                 return;
31660             }
31661             
31662             if(!match){
31663                 filterPattern(box, length - 1);
31664                 return;
31665             }
31666                 
31667             queue.push(pattern);
31668
31669             box = box.slice(length, box.length);
31670
31671             filterPattern(box, 4);
31672
31673             return;
31674             
31675         }
31676         
31677         Roo.each(boxes, function(box, k){
31678             
31679             if(!box.length){
31680                 return;
31681             }
31682             
31683             if(box.length == 1){
31684                 queue.push(box);
31685                 return;
31686             }
31687             
31688             filterPattern(box, 4);
31689             
31690         }, this);
31691         
31692         
31693         var prune = [];
31694         
31695         var pos = this.el.getBox(true);
31696         
31697         var minX = pos.x;
31698         
31699         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31700         
31701         var hit_end = false;
31702         
31703         Roo.each(queue, function(box){
31704             
31705             if(hit_end){
31706                 
31707                 Roo.each(box, function(b){
31708                 
31709                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31710                     b.el.hide();
31711
31712                 }, this);
31713
31714                 return;
31715             }
31716             
31717             var mx = 0;
31718             
31719             Roo.each(box, function(b){
31720                 
31721                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31722                 b.el.show();
31723
31724                 mx = Math.max(mx, b.x);
31725                 
31726             }, this);
31727             
31728             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31729             
31730             if(maxX < minX){
31731                 
31732                 Roo.each(box, function(b){
31733                 
31734                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31735                     b.el.hide();
31736                     
31737                 }, this);
31738                 
31739                 hit_end = true;
31740                 
31741                 return;
31742             }
31743             
31744             prune.push(box);
31745             
31746         }, this);
31747         
31748         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31749     },
31750     
31751     /** Sets position of item in DOM
31752     * @param {Element} item
31753     * @param {Number} x - horizontal position
31754     * @param {Number} y - vertical position
31755     * @param {Boolean} isInstant - disables transitions
31756     */
31757     _processVerticalLayoutQueue : function( queue, isInstant )
31758     {
31759         var pos = this.el.getBox(true);
31760         var x = pos.x;
31761         var y = pos.y;
31762         var maxY = [];
31763         
31764         for (var i = 0; i < this.cols; i++){
31765             maxY[i] = pos.y;
31766         }
31767         
31768         Roo.each(queue, function(box, k){
31769             
31770             var col = k % this.cols;
31771             
31772             Roo.each(box, function(b,kk){
31773                 
31774                 b.el.position('absolute');
31775                 
31776                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31777                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31778                 
31779                 if(b.size == 'md-left' || b.size == 'md-right'){
31780                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31781                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31782                 }
31783                 
31784                 b.el.setWidth(width);
31785                 b.el.setHeight(height);
31786                 // iframe?
31787                 b.el.select('iframe',true).setSize(width,height);
31788                 
31789             }, this);
31790             
31791             for (var i = 0; i < this.cols; i++){
31792                 
31793                 if(maxY[i] < maxY[col]){
31794                     col = i;
31795                     continue;
31796                 }
31797                 
31798                 col = Math.min(col, i);
31799                 
31800             }
31801             
31802             x = pos.x + col * (this.colWidth + this.padWidth);
31803             
31804             y = maxY[col];
31805             
31806             var positions = [];
31807             
31808             switch (box.length){
31809                 case 1 :
31810                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31811                     break;
31812                 case 2 :
31813                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31814                     break;
31815                 case 3 :
31816                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31817                     break;
31818                 case 4 :
31819                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31820                     break;
31821                 default :
31822                     break;
31823             }
31824             
31825             Roo.each(box, function(b,kk){
31826                 
31827                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31828                 
31829                 var sz = b.el.getSize();
31830                 
31831                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31832                 
31833             }, this);
31834             
31835         }, this);
31836         
31837         var mY = 0;
31838         
31839         for (var i = 0; i < this.cols; i++){
31840             mY = Math.max(mY, maxY[i]);
31841         }
31842         
31843         this.el.setHeight(mY - pos.y);
31844         
31845     },
31846     
31847 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31848 //    {
31849 //        var pos = this.el.getBox(true);
31850 //        var x = pos.x;
31851 //        var y = pos.y;
31852 //        var maxX = pos.right;
31853 //        
31854 //        var maxHeight = 0;
31855 //        
31856 //        Roo.each(items, function(item, k){
31857 //            
31858 //            var c = k % 2;
31859 //            
31860 //            item.el.position('absolute');
31861 //                
31862 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31863 //
31864 //            item.el.setWidth(width);
31865 //
31866 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31867 //
31868 //            item.el.setHeight(height);
31869 //            
31870 //            if(c == 0){
31871 //                item.el.setXY([x, y], isInstant ? false : true);
31872 //            } else {
31873 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31874 //            }
31875 //            
31876 //            y = y + height + this.alternativePadWidth;
31877 //            
31878 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31879 //            
31880 //        }, this);
31881 //        
31882 //        this.el.setHeight(maxHeight);
31883 //        
31884 //    },
31885     
31886     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31887     {
31888         var pos = this.el.getBox(true);
31889         
31890         var minX = pos.x;
31891         var minY = pos.y;
31892         
31893         var maxX = pos.right;
31894         
31895         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31896         
31897         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31898         
31899         Roo.each(queue, function(box, k){
31900             
31901             Roo.each(box, function(b, kk){
31902                 
31903                 b.el.position('absolute');
31904                 
31905                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31906                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31907                 
31908                 if(b.size == 'md-left' || b.size == 'md-right'){
31909                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31910                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31911                 }
31912                 
31913                 b.el.setWidth(width);
31914                 b.el.setHeight(height);
31915                 
31916             }, this);
31917             
31918             if(!box.length){
31919                 return;
31920             }
31921             
31922             var positions = [];
31923             
31924             switch (box.length){
31925                 case 1 :
31926                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31927                     break;
31928                 case 2 :
31929                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31930                     break;
31931                 case 3 :
31932                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31933                     break;
31934                 case 4 :
31935                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31936                     break;
31937                 default :
31938                     break;
31939             }
31940             
31941             Roo.each(box, function(b,kk){
31942                 
31943                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31944                 
31945                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31946                 
31947             }, this);
31948             
31949         }, this);
31950         
31951     },
31952     
31953     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31954     {
31955         Roo.each(eItems, function(b,k){
31956             
31957             b.size = (k == 0) ? 'sm' : 'xs';
31958             b.x = (k == 0) ? 2 : 1;
31959             b.y = (k == 0) ? 2 : 1;
31960             
31961             b.el.position('absolute');
31962             
31963             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31964                 
31965             b.el.setWidth(width);
31966             
31967             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31968             
31969             b.el.setHeight(height);
31970             
31971         }, this);
31972
31973         var positions = [];
31974         
31975         positions.push({
31976             x : maxX - this.unitWidth * 2 - this.gutter,
31977             y : minY
31978         });
31979         
31980         positions.push({
31981             x : maxX - this.unitWidth,
31982             y : minY + (this.unitWidth + this.gutter) * 2
31983         });
31984         
31985         positions.push({
31986             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31987             y : minY
31988         });
31989         
31990         Roo.each(eItems, function(b,k){
31991             
31992             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31993
31994         }, this);
31995         
31996     },
31997     
31998     getVerticalOneBoxColPositions : function(x, y, box)
31999     {
32000         var pos = [];
32001         
32002         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
32003         
32004         if(box[0].size == 'md-left'){
32005             rand = 0;
32006         }
32007         
32008         if(box[0].size == 'md-right'){
32009             rand = 1;
32010         }
32011         
32012         pos.push({
32013             x : x + (this.unitWidth + this.gutter) * rand,
32014             y : y
32015         });
32016         
32017         return pos;
32018     },
32019     
32020     getVerticalTwoBoxColPositions : function(x, y, box)
32021     {
32022         var pos = [];
32023         
32024         if(box[0].size == 'xs'){
32025             
32026             pos.push({
32027                 x : x,
32028                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
32029             });
32030
32031             pos.push({
32032                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
32033                 y : y
32034             });
32035             
32036             return pos;
32037             
32038         }
32039         
32040         pos.push({
32041             x : x,
32042             y : y
32043         });
32044
32045         pos.push({
32046             x : x + (this.unitWidth + this.gutter) * 2,
32047             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
32048         });
32049         
32050         return pos;
32051         
32052     },
32053     
32054     getVerticalThreeBoxColPositions : function(x, y, box)
32055     {
32056         var pos = [];
32057         
32058         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32059             
32060             pos.push({
32061                 x : x,
32062                 y : y
32063             });
32064
32065             pos.push({
32066                 x : x + (this.unitWidth + this.gutter) * 1,
32067                 y : y
32068             });
32069             
32070             pos.push({
32071                 x : x + (this.unitWidth + this.gutter) * 2,
32072                 y : y
32073             });
32074             
32075             return pos;
32076             
32077         }
32078         
32079         if(box[0].size == 'xs' && box[1].size == 'xs'){
32080             
32081             pos.push({
32082                 x : x,
32083                 y : y
32084             });
32085
32086             pos.push({
32087                 x : x,
32088                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
32089             });
32090             
32091             pos.push({
32092                 x : x + (this.unitWidth + this.gutter) * 1,
32093                 y : y
32094             });
32095             
32096             return pos;
32097             
32098         }
32099         
32100         pos.push({
32101             x : x,
32102             y : y
32103         });
32104
32105         pos.push({
32106             x : x + (this.unitWidth + this.gutter) * 2,
32107             y : y
32108         });
32109
32110         pos.push({
32111             x : x + (this.unitWidth + this.gutter) * 2,
32112             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
32113         });
32114             
32115         return pos;
32116         
32117     },
32118     
32119     getVerticalFourBoxColPositions : function(x, y, box)
32120     {
32121         var pos = [];
32122         
32123         if(box[0].size == 'xs'){
32124             
32125             pos.push({
32126                 x : x,
32127                 y : y
32128             });
32129
32130             pos.push({
32131                 x : x,
32132                 y : y + (this.unitHeight + this.gutter) * 1
32133             });
32134             
32135             pos.push({
32136                 x : x,
32137                 y : y + (this.unitHeight + this.gutter) * 2
32138             });
32139             
32140             pos.push({
32141                 x : x + (this.unitWidth + this.gutter) * 1,
32142                 y : y
32143             });
32144             
32145             return pos;
32146             
32147         }
32148         
32149         pos.push({
32150             x : x,
32151             y : y
32152         });
32153
32154         pos.push({
32155             x : x + (this.unitWidth + this.gutter) * 2,
32156             y : y
32157         });
32158
32159         pos.push({
32160             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
32161             y : y + (this.unitHeight + this.gutter) * 1
32162         });
32163
32164         pos.push({
32165             x : x + (this.unitWidth + this.gutter) * 2,
32166             y : y + (this.unitWidth + this.gutter) * 2
32167         });
32168
32169         return pos;
32170         
32171     },
32172     
32173     getHorizontalOneBoxColPositions : function(maxX, minY, box)
32174     {
32175         var pos = [];
32176         
32177         if(box[0].size == 'md-left'){
32178             pos.push({
32179                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32180                 y : minY
32181             });
32182             
32183             return pos;
32184         }
32185         
32186         if(box[0].size == 'md-right'){
32187             pos.push({
32188                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32189                 y : minY + (this.unitWidth + this.gutter) * 1
32190             });
32191             
32192             return pos;
32193         }
32194         
32195         var rand = Math.floor(Math.random() * (4 - box[0].y));
32196         
32197         pos.push({
32198             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32199             y : minY + (this.unitWidth + this.gutter) * rand
32200         });
32201         
32202         return pos;
32203         
32204     },
32205     
32206     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
32207     {
32208         var pos = [];
32209         
32210         if(box[0].size == 'xs'){
32211             
32212             pos.push({
32213                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32214                 y : minY
32215             });
32216
32217             pos.push({
32218                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32219                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32220             });
32221             
32222             return pos;
32223             
32224         }
32225         
32226         pos.push({
32227             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32228             y : minY
32229         });
32230
32231         pos.push({
32232             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32233             y : minY + (this.unitWidth + this.gutter) * 2
32234         });
32235         
32236         return pos;
32237         
32238     },
32239     
32240     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32241     {
32242         var pos = [];
32243         
32244         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32245             
32246             pos.push({
32247                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32248                 y : minY
32249             });
32250
32251             pos.push({
32252                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32253                 y : minY + (this.unitWidth + this.gutter) * 1
32254             });
32255             
32256             pos.push({
32257                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32258                 y : minY + (this.unitWidth + this.gutter) * 2
32259             });
32260             
32261             return pos;
32262             
32263         }
32264         
32265         if(box[0].size == 'xs' && box[1].size == 'xs'){
32266             
32267             pos.push({
32268                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32269                 y : minY
32270             });
32271
32272             pos.push({
32273                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32274                 y : minY
32275             });
32276             
32277             pos.push({
32278                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32279                 y : minY + (this.unitWidth + this.gutter) * 1
32280             });
32281             
32282             return pos;
32283             
32284         }
32285         
32286         pos.push({
32287             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32288             y : minY
32289         });
32290
32291         pos.push({
32292             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32293             y : minY + (this.unitWidth + this.gutter) * 2
32294         });
32295
32296         pos.push({
32297             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32298             y : minY + (this.unitWidth + this.gutter) * 2
32299         });
32300             
32301         return pos;
32302         
32303     },
32304     
32305     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32306     {
32307         var pos = [];
32308         
32309         if(box[0].size == 'xs'){
32310             
32311             pos.push({
32312                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32313                 y : minY
32314             });
32315
32316             pos.push({
32317                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32318                 y : minY
32319             });
32320             
32321             pos.push({
32322                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32323                 y : minY
32324             });
32325             
32326             pos.push({
32327                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32328                 y : minY + (this.unitWidth + this.gutter) * 1
32329             });
32330             
32331             return pos;
32332             
32333         }
32334         
32335         pos.push({
32336             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32337             y : minY
32338         });
32339         
32340         pos.push({
32341             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32342             y : minY + (this.unitWidth + this.gutter) * 2
32343         });
32344         
32345         pos.push({
32346             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32347             y : minY + (this.unitWidth + this.gutter) * 2
32348         });
32349         
32350         pos.push({
32351             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1) - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32352             y : minY + (this.unitWidth + this.gutter) * 2
32353         });
32354
32355         return pos;
32356         
32357     },
32358     
32359     /**
32360     * remove a Masonry Brick
32361     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32362     */
32363     removeBrick : function(brick_id)
32364     {
32365         if (!brick_id) {
32366             return;
32367         }
32368         
32369         for (var i = 0; i<this.bricks.length; i++) {
32370             if (this.bricks[i].id == brick_id) {
32371                 this.bricks.splice(i,1);
32372                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32373                 this.initial();
32374             }
32375         }
32376     },
32377     
32378     /**
32379     * adds a Masonry Brick
32380     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32381     */
32382     addBrick : function(cfg)
32383     {
32384         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32385         //this.register(cn);
32386         cn.parentId = this.id;
32387         cn.render(this.el);
32388         return cn;
32389     },
32390     
32391     /**
32392     * register a Masonry Brick
32393     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32394     */
32395     
32396     register : function(brick)
32397     {
32398         this.bricks.push(brick);
32399         brick.masonryId = this.id;
32400     },
32401     
32402     /**
32403     * clear all the Masonry Brick
32404     */
32405     clearAll : function()
32406     {
32407         this.bricks = [];
32408         //this.getChildContainer().dom.innerHTML = "";
32409         this.el.dom.innerHTML = '';
32410     },
32411     
32412     getSelected : function()
32413     {
32414         if (!this.selectedBrick) {
32415             return false;
32416         }
32417         
32418         return this.selectedBrick;
32419     }
32420 });
32421
32422 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32423     
32424     groups: {},
32425      /**
32426     * register a Masonry Layout
32427     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32428     */
32429     
32430     register : function(layout)
32431     {
32432         this.groups[layout.id] = layout;
32433     },
32434     /**
32435     * fetch a  Masonry Layout based on the masonry layout ID
32436     * @param {string} the masonry layout to add
32437     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32438     */
32439     
32440     get: function(layout_id) {
32441         if (typeof(this.groups[layout_id]) == 'undefined') {
32442             return false;
32443         }
32444         return this.groups[layout_id] ;
32445     }
32446     
32447     
32448     
32449 });
32450
32451  
32452
32453  /**
32454  *
32455  * This is based on 
32456  * http://masonry.desandro.com
32457  *
32458  * The idea is to render all the bricks based on vertical width...
32459  *
32460  * The original code extends 'outlayer' - we might need to use that....
32461  * 
32462  */
32463
32464
32465 /**
32466  * @class Roo.bootstrap.LayoutMasonryAuto
32467  * @extends Roo.bootstrap.Component
32468  * Bootstrap Layout Masonry class
32469  * 
32470  * @constructor
32471  * Create a new Element
32472  * @param {Object} config The config object
32473  */
32474
32475 Roo.bootstrap.LayoutMasonryAuto = function(config){
32476     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32477 };
32478
32479 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32480     
32481       /**
32482      * @cfg {Boolean} isFitWidth  - resize the width..
32483      */   
32484     isFitWidth : false,  // options..
32485     /**
32486      * @cfg {Boolean} isOriginLeft = left align?
32487      */   
32488     isOriginLeft : true,
32489     /**
32490      * @cfg {Boolean} isOriginTop = top align?
32491      */   
32492     isOriginTop : false,
32493     /**
32494      * @cfg {Boolean} isLayoutInstant = no animation?
32495      */   
32496     isLayoutInstant : false, // needed?
32497     /**
32498      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32499      */   
32500     isResizingContainer : true,
32501     /**
32502      * @cfg {Number} columnWidth  width of the columns 
32503      */   
32504     
32505     columnWidth : 0,
32506     
32507     /**
32508      * @cfg {Number} maxCols maximum number of columns
32509      */   
32510     
32511     maxCols: 0,
32512     /**
32513      * @cfg {Number} padHeight padding below box..
32514      */   
32515     
32516     padHeight : 10, 
32517     
32518     /**
32519      * @cfg {Boolean} isAutoInitial defalut true
32520      */   
32521     
32522     isAutoInitial : true, 
32523     
32524     // private?
32525     gutter : 0,
32526     
32527     containerWidth: 0,
32528     initialColumnWidth : 0,
32529     currentSize : null,
32530     
32531     colYs : null, // array.
32532     maxY : 0,
32533     padWidth: 10,
32534     
32535     
32536     tag: 'div',
32537     cls: '',
32538     bricks: null, //CompositeElement
32539     cols : 0, // array?
32540     // element : null, // wrapped now this.el
32541     _isLayoutInited : null, 
32542     
32543     
32544     getAutoCreate : function(){
32545         
32546         var cfg = {
32547             tag: this.tag,
32548             cls: 'blog-masonary-wrapper ' + this.cls,
32549             cn : {
32550                 cls : 'mas-boxes masonary'
32551             }
32552         };
32553         
32554         return cfg;
32555     },
32556     
32557     getChildContainer: function( )
32558     {
32559         if (this.boxesEl) {
32560             return this.boxesEl;
32561         }
32562         
32563         this.boxesEl = this.el.select('.mas-boxes').first();
32564         
32565         return this.boxesEl;
32566     },
32567     
32568     
32569     initEvents : function()
32570     {
32571         var _this = this;
32572         
32573         if(this.isAutoInitial){
32574             Roo.log('hook children rendered');
32575             this.on('childrenrendered', function() {
32576                 Roo.log('children rendered');
32577                 _this.initial();
32578             } ,this);
32579         }
32580         
32581     },
32582     
32583     initial : function()
32584     {
32585         this.reloadItems();
32586
32587         this.currentSize = this.el.getBox(true);
32588
32589         /// was window resize... - let's see if this works..
32590         Roo.EventManager.onWindowResize(this.resize, this); 
32591
32592         if(!this.isAutoInitial){
32593             this.layout();
32594             return;
32595         }
32596         
32597         this.layout.defer(500,this);
32598     },
32599     
32600     reloadItems: function()
32601     {
32602         this.bricks = this.el.select('.masonry-brick', true);
32603         
32604         this.bricks.each(function(b) {
32605             //Roo.log(b.getSize());
32606             if (!b.attr('originalwidth')) {
32607                 b.attr('originalwidth',  b.getSize().width);
32608             }
32609             
32610         });
32611         
32612         Roo.log(this.bricks.elements.length);
32613     },
32614     
32615     resize : function()
32616     {
32617         Roo.log('resize');
32618         var cs = this.el.getBox(true);
32619         
32620         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32621             Roo.log("no change in with or X");
32622             return;
32623         }
32624         this.currentSize = cs;
32625         this.layout();
32626     },
32627     
32628     layout : function()
32629     {
32630          Roo.log('layout');
32631         this._resetLayout();
32632         //this._manageStamps();
32633       
32634         // don't animate first layout
32635         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32636         this.layoutItems( isInstant );
32637       
32638         // flag for initalized
32639         this._isLayoutInited = true;
32640     },
32641     
32642     layoutItems : function( isInstant )
32643     {
32644         //var items = this._getItemsForLayout( this.items );
32645         // original code supports filtering layout items.. we just ignore it..
32646         
32647         this._layoutItems( this.bricks , isInstant );
32648       
32649         this._postLayout();
32650     },
32651     _layoutItems : function ( items , isInstant)
32652     {
32653        //this.fireEvent( 'layout', this, items );
32654     
32655
32656         if ( !items || !items.elements.length ) {
32657           // no items, emit event with empty array
32658             return;
32659         }
32660
32661         var queue = [];
32662         items.each(function(item) {
32663             Roo.log("layout item");
32664             Roo.log(item);
32665             // get x/y object from method
32666             var position = this._getItemLayoutPosition( item );
32667             // enqueue
32668             position.item = item;
32669             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32670             queue.push( position );
32671         }, this);
32672       
32673         this._processLayoutQueue( queue );
32674     },
32675     /** Sets position of item in DOM
32676     * @param {Element} item
32677     * @param {Number} x - horizontal position
32678     * @param {Number} y - vertical position
32679     * @param {Boolean} isInstant - disables transitions
32680     */
32681     _processLayoutQueue : function( queue )
32682     {
32683         for ( var i=0, len = queue.length; i < len; i++ ) {
32684             var obj = queue[i];
32685             obj.item.position('absolute');
32686             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32687         }
32688     },
32689       
32690     
32691     /**
32692     * Any logic you want to do after each layout,
32693     * i.e. size the container
32694     */
32695     _postLayout : function()
32696     {
32697         this.resizeContainer();
32698     },
32699     
32700     resizeContainer : function()
32701     {
32702         if ( !this.isResizingContainer ) {
32703             return;
32704         }
32705         var size = this._getContainerSize();
32706         if ( size ) {
32707             this.el.setSize(size.width,size.height);
32708             this.boxesEl.setSize(size.width,size.height);
32709         }
32710     },
32711     
32712     
32713     
32714     _resetLayout : function()
32715     {
32716         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32717         this.colWidth = this.el.getWidth();
32718         //this.gutter = this.el.getWidth(); 
32719         
32720         this.measureColumns();
32721
32722         // reset column Y
32723         var i = this.cols;
32724         this.colYs = [];
32725         while (i--) {
32726             this.colYs.push( 0 );
32727         }
32728     
32729         this.maxY = 0;
32730     },
32731
32732     measureColumns : function()
32733     {
32734         this.getContainerWidth();
32735       // if columnWidth is 0, default to outerWidth of first item
32736         if ( !this.columnWidth ) {
32737             var firstItem = this.bricks.first();
32738             Roo.log(firstItem);
32739             this.columnWidth  = this.containerWidth;
32740             if (firstItem && firstItem.attr('originalwidth') ) {
32741                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32742             }
32743             // columnWidth fall back to item of first element
32744             Roo.log("set column width?");
32745                         this.initialColumnWidth = this.columnWidth  ;
32746
32747             // if first elem has no width, default to size of container
32748             
32749         }
32750         
32751         
32752         if (this.initialColumnWidth) {
32753             this.columnWidth = this.initialColumnWidth;
32754         }
32755         
32756         
32757             
32758         // column width is fixed at the top - however if container width get's smaller we should
32759         // reduce it...
32760         
32761         // this bit calcs how man columns..
32762             
32763         var columnWidth = this.columnWidth += this.gutter;
32764       
32765         // calculate columns
32766         var containerWidth = this.containerWidth + this.gutter;
32767         
32768         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32769         // fix rounding errors, typically with gutters
32770         var excess = columnWidth - containerWidth % columnWidth;
32771         
32772         
32773         // if overshoot is less than a pixel, round up, otherwise floor it
32774         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32775         cols = Math[ mathMethod ]( cols );
32776         this.cols = Math.max( cols, 1 );
32777         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32778         
32779          // padding positioning..
32780         var totalColWidth = this.cols * this.columnWidth;
32781         var padavail = this.containerWidth - totalColWidth;
32782         // so for 2 columns - we need 3 'pads'
32783         
32784         var padNeeded = (1+this.cols) * this.padWidth;
32785         
32786         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32787         
32788         this.columnWidth += padExtra
32789         //this.padWidth = Math.floor(padavail /  ( this.cols));
32790         
32791         // adjust colum width so that padding is fixed??
32792         
32793         // we have 3 columns ... total = width * 3
32794         // we have X left over... that should be used by 
32795         
32796         //if (this.expandC) {
32797             
32798         //}
32799         
32800         
32801         
32802     },
32803     
32804     getContainerWidth : function()
32805     {
32806        /* // container is parent if fit width
32807         var container = this.isFitWidth ? this.element.parentNode : this.element;
32808         // check that this.size and size are there
32809         // IE8 triggers resize on body size change, so they might not be
32810         
32811         var size = getSize( container );  //FIXME
32812         this.containerWidth = size && size.innerWidth; //FIXME
32813         */
32814          
32815         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32816         
32817     },
32818     
32819     _getItemLayoutPosition : function( item )  // what is item?
32820     {
32821         // we resize the item to our columnWidth..
32822       
32823         item.setWidth(this.columnWidth);
32824         item.autoBoxAdjust  = false;
32825         
32826         var sz = item.getSize();
32827  
32828         // how many columns does this brick span
32829         var remainder = this.containerWidth % this.columnWidth;
32830         
32831         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32832         // round if off by 1 pixel, otherwise use ceil
32833         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32834         colSpan = Math.min( colSpan, this.cols );
32835         
32836         // normally this should be '1' as we dont' currently allow multi width columns..
32837         
32838         var colGroup = this._getColGroup( colSpan );
32839         // get the minimum Y value from the columns
32840         var minimumY = Math.min.apply( Math, colGroup );
32841         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32842         
32843         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32844          
32845         // position the brick
32846         var position = {
32847             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32848             y: this.currentSize.y + minimumY + this.padHeight
32849         };
32850         
32851         Roo.log(position);
32852         // apply setHeight to necessary columns
32853         var setHeight = minimumY + sz.height + this.padHeight;
32854         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32855         
32856         var setSpan = this.cols + 1 - colGroup.length;
32857         for ( var i = 0; i < setSpan; i++ ) {
32858           this.colYs[ shortColIndex + i ] = setHeight ;
32859         }
32860       
32861         return position;
32862     },
32863     
32864     /**
32865      * @param {Number} colSpan - number of columns the element spans
32866      * @returns {Array} colGroup
32867      */
32868     _getColGroup : function( colSpan )
32869     {
32870         if ( colSpan < 2 ) {
32871           // if brick spans only one column, use all the column Ys
32872           return this.colYs;
32873         }
32874       
32875         var colGroup = [];
32876         // how many different places could this brick fit horizontally
32877         var groupCount = this.cols + 1 - colSpan;
32878         // for each group potential horizontal position
32879         for ( var i = 0; i < groupCount; i++ ) {
32880           // make an array of colY values for that one group
32881           var groupColYs = this.colYs.slice( i, i + colSpan );
32882           // and get the max value of the array
32883           colGroup[i] = Math.max.apply( Math, groupColYs );
32884         }
32885         return colGroup;
32886     },
32887     /*
32888     _manageStamp : function( stamp )
32889     {
32890         var stampSize =  stamp.getSize();
32891         var offset = stamp.getBox();
32892         // get the columns that this stamp affects
32893         var firstX = this.isOriginLeft ? offset.x : offset.right;
32894         var lastX = firstX + stampSize.width;
32895         var firstCol = Math.floor( firstX / this.columnWidth );
32896         firstCol = Math.max( 0, firstCol );
32897         
32898         var lastCol = Math.floor( lastX / this.columnWidth );
32899         // lastCol should not go over if multiple of columnWidth #425
32900         lastCol -= lastX % this.columnWidth ? 0 : 1;
32901         lastCol = Math.min( this.cols - 1, lastCol );
32902         
32903         // set colYs to bottom of the stamp
32904         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32905             stampSize.height;
32906             
32907         for ( var i = firstCol; i <= lastCol; i++ ) {
32908           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32909         }
32910     },
32911     */
32912     
32913     _getContainerSize : function()
32914     {
32915         this.maxY = Math.max.apply( Math, this.colYs );
32916         var size = {
32917             height: this.maxY
32918         };
32919       
32920         if ( this.isFitWidth ) {
32921             size.width = this._getContainerFitWidth();
32922         }
32923       
32924         return size;
32925     },
32926     
32927     _getContainerFitWidth : function()
32928     {
32929         var unusedCols = 0;
32930         // count unused columns
32931         var i = this.cols;
32932         while ( --i ) {
32933           if ( this.colYs[i] !== 0 ) {
32934             break;
32935           }
32936           unusedCols++;
32937         }
32938         // fit container to columns that have been used
32939         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32940     },
32941     
32942     needsResizeLayout : function()
32943     {
32944         var previousWidth = this.containerWidth;
32945         this.getContainerWidth();
32946         return previousWidth !== this.containerWidth;
32947     }
32948  
32949 });
32950
32951  
32952
32953  /*
32954  * - LGPL
32955  *
32956  * element
32957  * 
32958  */
32959
32960 /**
32961  * @class Roo.bootstrap.MasonryBrick
32962  * @extends Roo.bootstrap.Component
32963  * Bootstrap MasonryBrick class
32964  * 
32965  * @constructor
32966  * Create a new MasonryBrick
32967  * @param {Object} config The config object
32968  */
32969
32970 Roo.bootstrap.MasonryBrick = function(config){
32971     
32972     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32973     
32974     Roo.bootstrap.MasonryBrick.register(this);
32975     
32976     this.addEvents({
32977         // raw events
32978         /**
32979          * @event click
32980          * When a MasonryBrick is clcik
32981          * @param {Roo.bootstrap.MasonryBrick} this
32982          * @param {Roo.EventObject} e
32983          */
32984         "click" : true
32985     });
32986 };
32987
32988 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32989     
32990     /**
32991      * @cfg {String} title
32992      */   
32993     title : '',
32994     /**
32995      * @cfg {String} html
32996      */   
32997     html : '',
32998     /**
32999      * @cfg {String} bgimage
33000      */   
33001     bgimage : '',
33002     /**
33003      * @cfg {String} videourl
33004      */   
33005     videourl : '',
33006     /**
33007      * @cfg {String} cls
33008      */   
33009     cls : '',
33010     /**
33011      * @cfg {String} href
33012      */   
33013     href : '',
33014     /**
33015      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
33016      */   
33017     size : 'xs',
33018     
33019     /**
33020      * @cfg {String} placetitle (center|bottom)
33021      */   
33022     placetitle : '',
33023     
33024     /**
33025      * @cfg {Boolean} isFitContainer defalut true
33026      */   
33027     isFitContainer : true, 
33028     
33029     /**
33030      * @cfg {Boolean} preventDefault defalut false
33031      */   
33032     preventDefault : false, 
33033     
33034     /**
33035      * @cfg {Boolean} inverse defalut false
33036      */   
33037     maskInverse : false, 
33038     
33039     getAutoCreate : function()
33040     {
33041         if(!this.isFitContainer){
33042             return this.getSplitAutoCreate();
33043         }
33044         
33045         var cls = 'masonry-brick masonry-brick-full';
33046         
33047         if(this.href.length){
33048             cls += ' masonry-brick-link';
33049         }
33050         
33051         if(this.bgimage.length){
33052             cls += ' masonry-brick-image';
33053         }
33054         
33055         if(this.maskInverse){
33056             cls += ' mask-inverse';
33057         }
33058         
33059         if(!this.html.length && !this.maskInverse && !this.videourl.length){
33060             cls += ' enable-mask';
33061         }
33062         
33063         if(this.size){
33064             cls += ' masonry-' + this.size + '-brick';
33065         }
33066         
33067         if(this.placetitle.length){
33068             
33069             switch (this.placetitle) {
33070                 case 'center' :
33071                     cls += ' masonry-center-title';
33072                     break;
33073                 case 'bottom' :
33074                     cls += ' masonry-bottom-title';
33075                     break;
33076                 default:
33077                     break;
33078             }
33079             
33080         } else {
33081             if(!this.html.length && !this.bgimage.length){
33082                 cls += ' masonry-center-title';
33083             }
33084
33085             if(!this.html.length && this.bgimage.length){
33086                 cls += ' masonry-bottom-title';
33087             }
33088         }
33089         
33090         if(this.cls){
33091             cls += ' ' + this.cls;
33092         }
33093         
33094         var cfg = {
33095             tag: (this.href.length) ? 'a' : 'div',
33096             cls: cls,
33097             cn: [
33098                 {
33099                     tag: 'div',
33100                     cls: 'masonry-brick-mask'
33101                 },
33102                 {
33103                     tag: 'div',
33104                     cls: 'masonry-brick-paragraph',
33105                     cn: []
33106                 }
33107             ]
33108         };
33109         
33110         if(this.href.length){
33111             cfg.href = this.href;
33112         }
33113         
33114         var cn = cfg.cn[1].cn;
33115         
33116         if(this.title.length){
33117             cn.push({
33118                 tag: 'h4',
33119                 cls: 'masonry-brick-title',
33120                 html: this.title
33121             });
33122         }
33123         
33124         if(this.html.length){
33125             cn.push({
33126                 tag: 'p',
33127                 cls: 'masonry-brick-text',
33128                 html: this.html
33129             });
33130         }
33131         
33132         if (!this.title.length && !this.html.length) {
33133             cfg.cn[1].cls += ' hide';
33134         }
33135         
33136         if(this.bgimage.length){
33137             cfg.cn.push({
33138                 tag: 'img',
33139                 cls: 'masonry-brick-image-view',
33140                 src: this.bgimage
33141             });
33142         }
33143         
33144         if(this.videourl.length){
33145             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33146             // youtube support only?
33147             cfg.cn.push({
33148                 tag: 'iframe',
33149                 cls: 'masonry-brick-image-view',
33150                 src: vurl,
33151                 frameborder : 0,
33152                 allowfullscreen : true
33153             });
33154         }
33155         
33156         return cfg;
33157         
33158     },
33159     
33160     getSplitAutoCreate : function()
33161     {
33162         var cls = 'masonry-brick masonry-brick-split';
33163         
33164         if(this.href.length){
33165             cls += ' masonry-brick-link';
33166         }
33167         
33168         if(this.bgimage.length){
33169             cls += ' masonry-brick-image';
33170         }
33171         
33172         if(this.size){
33173             cls += ' masonry-' + this.size + '-brick';
33174         }
33175         
33176         switch (this.placetitle) {
33177             case 'center' :
33178                 cls += ' masonry-center-title';
33179                 break;
33180             case 'bottom' :
33181                 cls += ' masonry-bottom-title';
33182                 break;
33183             default:
33184                 if(!this.bgimage.length){
33185                     cls += ' masonry-center-title';
33186                 }
33187
33188                 if(this.bgimage.length){
33189                     cls += ' masonry-bottom-title';
33190                 }
33191                 break;
33192         }
33193         
33194         if(this.cls){
33195             cls += ' ' + this.cls;
33196         }
33197         
33198         var cfg = {
33199             tag: (this.href.length) ? 'a' : 'div',
33200             cls: cls,
33201             cn: [
33202                 {
33203                     tag: 'div',
33204                     cls: 'masonry-brick-split-head',
33205                     cn: [
33206                         {
33207                             tag: 'div',
33208                             cls: 'masonry-brick-paragraph',
33209                             cn: []
33210                         }
33211                     ]
33212                 },
33213                 {
33214                     tag: 'div',
33215                     cls: 'masonry-brick-split-body',
33216                     cn: []
33217                 }
33218             ]
33219         };
33220         
33221         if(this.href.length){
33222             cfg.href = this.href;
33223         }
33224         
33225         if(this.title.length){
33226             cfg.cn[0].cn[0].cn.push({
33227                 tag: 'h4',
33228                 cls: 'masonry-brick-title',
33229                 html: this.title
33230             });
33231         }
33232         
33233         if(this.html.length){
33234             cfg.cn[1].cn.push({
33235                 tag: 'p',
33236                 cls: 'masonry-brick-text',
33237                 html: this.html
33238             });
33239         }
33240
33241         if(this.bgimage.length){
33242             cfg.cn[0].cn.push({
33243                 tag: 'img',
33244                 cls: 'masonry-brick-image-view',
33245                 src: this.bgimage
33246             });
33247         }
33248         
33249         if(this.videourl.length){
33250             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33251             // youtube support only?
33252             cfg.cn[0].cn.cn.push({
33253                 tag: 'iframe',
33254                 cls: 'masonry-brick-image-view',
33255                 src: vurl,
33256                 frameborder : 0,
33257                 allowfullscreen : true
33258             });
33259         }
33260         
33261         return cfg;
33262     },
33263     
33264     initEvents: function() 
33265     {
33266         switch (this.size) {
33267             case 'xs' :
33268                 this.x = 1;
33269                 this.y = 1;
33270                 break;
33271             case 'sm' :
33272                 this.x = 2;
33273                 this.y = 2;
33274                 break;
33275             case 'md' :
33276             case 'md-left' :
33277             case 'md-right' :
33278                 this.x = 3;
33279                 this.y = 3;
33280                 break;
33281             case 'tall' :
33282                 this.x = 2;
33283                 this.y = 3;
33284                 break;
33285             case 'wide' :
33286                 this.x = 3;
33287                 this.y = 2;
33288                 break;
33289             case 'wide-thin' :
33290                 this.x = 3;
33291                 this.y = 1;
33292                 break;
33293                         
33294             default :
33295                 break;
33296         }
33297         
33298         if(Roo.isTouch){
33299             this.el.on('touchstart', this.onTouchStart, this);
33300             this.el.on('touchmove', this.onTouchMove, this);
33301             this.el.on('touchend', this.onTouchEnd, this);
33302             this.el.on('contextmenu', this.onContextMenu, this);
33303         } else {
33304             this.el.on('mouseenter'  ,this.enter, this);
33305             this.el.on('mouseleave', this.leave, this);
33306             this.el.on('click', this.onClick, this);
33307         }
33308         
33309         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33310             this.parent().bricks.push(this);   
33311         }
33312         
33313     },
33314     
33315     onClick: function(e, el)
33316     {
33317         var time = this.endTimer - this.startTimer;
33318         // Roo.log(e.preventDefault());
33319         if(Roo.isTouch){
33320             if(time > 1000){
33321                 e.preventDefault();
33322                 return;
33323             }
33324         }
33325         
33326         if(!this.preventDefault){
33327             return;
33328         }
33329         
33330         e.preventDefault();
33331         
33332         if (this.activeClass != '') {
33333             this.selectBrick();
33334         }
33335         
33336         this.fireEvent('click', this, e);
33337     },
33338     
33339     enter: function(e, el)
33340     {
33341         e.preventDefault();
33342         
33343         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33344             return;
33345         }
33346         
33347         if(this.bgimage.length && this.html.length){
33348             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33349         }
33350     },
33351     
33352     leave: function(e, el)
33353     {
33354         e.preventDefault();
33355         
33356         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33357             return;
33358         }
33359         
33360         if(this.bgimage.length && this.html.length){
33361             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33362         }
33363     },
33364     
33365     onTouchStart: function(e, el)
33366     {
33367 //        e.preventDefault();
33368         
33369         this.touchmoved = false;
33370         
33371         if(!this.isFitContainer){
33372             return;
33373         }
33374         
33375         if(!this.bgimage.length || !this.html.length){
33376             return;
33377         }
33378         
33379         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33380         
33381         this.timer = new Date().getTime();
33382         
33383     },
33384     
33385     onTouchMove: function(e, el)
33386     {
33387         this.touchmoved = true;
33388     },
33389     
33390     onContextMenu : function(e,el)
33391     {
33392         e.preventDefault();
33393         e.stopPropagation();
33394         return false;
33395     },
33396     
33397     onTouchEnd: function(e, el)
33398     {
33399 //        e.preventDefault();
33400         
33401         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33402         
33403             this.leave(e,el);
33404             
33405             return;
33406         }
33407         
33408         if(!this.bgimage.length || !this.html.length){
33409             
33410             if(this.href.length){
33411                 window.location.href = this.href;
33412             }
33413             
33414             return;
33415         }
33416         
33417         if(!this.isFitContainer){
33418             return;
33419         }
33420         
33421         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33422         
33423         window.location.href = this.href;
33424     },
33425     
33426     //selection on single brick only
33427     selectBrick : function() {
33428         
33429         if (!this.parentId) {
33430             return;
33431         }
33432         
33433         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33434         var index = m.selectedBrick.indexOf(this.id);
33435         
33436         if ( index > -1) {
33437             m.selectedBrick.splice(index,1);
33438             this.el.removeClass(this.activeClass);
33439             return;
33440         }
33441         
33442         for(var i = 0; i < m.selectedBrick.length; i++) {
33443             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33444             b.el.removeClass(b.activeClass);
33445         }
33446         
33447         m.selectedBrick = [];
33448         
33449         m.selectedBrick.push(this.id);
33450         this.el.addClass(this.activeClass);
33451         return;
33452     },
33453     
33454     isSelected : function(){
33455         return this.el.hasClass(this.activeClass);
33456         
33457     }
33458 });
33459
33460 Roo.apply(Roo.bootstrap.MasonryBrick, {
33461     
33462     //groups: {},
33463     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33464      /**
33465     * register a Masonry Brick
33466     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33467     */
33468     
33469     register : function(brick)
33470     {
33471         //this.groups[brick.id] = brick;
33472         this.groups.add(brick.id, brick);
33473     },
33474     /**
33475     * fetch a  masonry brick based on the masonry brick ID
33476     * @param {string} the masonry brick to add
33477     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33478     */
33479     
33480     get: function(brick_id) 
33481     {
33482         // if (typeof(this.groups[brick_id]) == 'undefined') {
33483         //     return false;
33484         // }
33485         // return this.groups[brick_id] ;
33486         
33487         if(this.groups.key(brick_id)) {
33488             return this.groups.key(brick_id);
33489         }
33490         
33491         return false;
33492     }
33493     
33494     
33495     
33496 });
33497
33498  /*
33499  * - LGPL
33500  *
33501  * element
33502  * 
33503  */
33504
33505 /**
33506  * @class Roo.bootstrap.Brick
33507  * @extends Roo.bootstrap.Component
33508  * Bootstrap Brick class
33509  * 
33510  * @constructor
33511  * Create a new Brick
33512  * @param {Object} config The config object
33513  */
33514
33515 Roo.bootstrap.Brick = function(config){
33516     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33517     
33518     this.addEvents({
33519         // raw events
33520         /**
33521          * @event click
33522          * When a Brick is click
33523          * @param {Roo.bootstrap.Brick} this
33524          * @param {Roo.EventObject} e
33525          */
33526         "click" : true
33527     });
33528 };
33529
33530 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33531     
33532     /**
33533      * @cfg {String} title
33534      */   
33535     title : '',
33536     /**
33537      * @cfg {String} html
33538      */   
33539     html : '',
33540     /**
33541      * @cfg {String} bgimage
33542      */   
33543     bgimage : '',
33544     /**
33545      * @cfg {String} cls
33546      */   
33547     cls : '',
33548     /**
33549      * @cfg {String} href
33550      */   
33551     href : '',
33552     /**
33553      * @cfg {String} video
33554      */   
33555     video : '',
33556     /**
33557      * @cfg {Boolean} square
33558      */   
33559     square : true,
33560     
33561     getAutoCreate : function()
33562     {
33563         var cls = 'roo-brick';
33564         
33565         if(this.href.length){
33566             cls += ' roo-brick-link';
33567         }
33568         
33569         if(this.bgimage.length){
33570             cls += ' roo-brick-image';
33571         }
33572         
33573         if(!this.html.length && !this.bgimage.length){
33574             cls += ' roo-brick-center-title';
33575         }
33576         
33577         if(!this.html.length && this.bgimage.length){
33578             cls += ' roo-brick-bottom-title';
33579         }
33580         
33581         if(this.cls){
33582             cls += ' ' + this.cls;
33583         }
33584         
33585         var cfg = {
33586             tag: (this.href.length) ? 'a' : 'div',
33587             cls: cls,
33588             cn: [
33589                 {
33590                     tag: 'div',
33591                     cls: 'roo-brick-paragraph',
33592                     cn: []
33593                 }
33594             ]
33595         };
33596         
33597         if(this.href.length){
33598             cfg.href = this.href;
33599         }
33600         
33601         var cn = cfg.cn[0].cn;
33602         
33603         if(this.title.length){
33604             cn.push({
33605                 tag: 'h4',
33606                 cls: 'roo-brick-title',
33607                 html: this.title
33608             });
33609         }
33610         
33611         if(this.html.length){
33612             cn.push({
33613                 tag: 'p',
33614                 cls: 'roo-brick-text',
33615                 html: this.html
33616             });
33617         } else {
33618             cn.cls += ' hide';
33619         }
33620         
33621         if(this.bgimage.length){
33622             cfg.cn.push({
33623                 tag: 'img',
33624                 cls: 'roo-brick-image-view',
33625                 src: this.bgimage
33626             });
33627         }
33628         
33629         return cfg;
33630     },
33631     
33632     initEvents: function() 
33633     {
33634         if(this.title.length || this.html.length){
33635             this.el.on('mouseenter'  ,this.enter, this);
33636             this.el.on('mouseleave', this.leave, this);
33637         }
33638         
33639         Roo.EventManager.onWindowResize(this.resize, this); 
33640         
33641         if(this.bgimage.length){
33642             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33643             this.imageEl.on('load', this.onImageLoad, this);
33644             return;
33645         }
33646         
33647         this.resize();
33648     },
33649     
33650     onImageLoad : function()
33651     {
33652         this.resize();
33653     },
33654     
33655     resize : function()
33656     {
33657         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33658         
33659         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33660         
33661         if(this.bgimage.length){
33662             var image = this.el.select('.roo-brick-image-view', true).first();
33663             
33664             image.setWidth(paragraph.getWidth());
33665             
33666             if(this.square){
33667                 image.setHeight(paragraph.getWidth());
33668             }
33669             
33670             this.el.setHeight(image.getHeight());
33671             paragraph.setHeight(image.getHeight());
33672             
33673         }
33674         
33675     },
33676     
33677     enter: function(e, el)
33678     {
33679         e.preventDefault();
33680         
33681         if(this.bgimage.length){
33682             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33683             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33684         }
33685     },
33686     
33687     leave: function(e, el)
33688     {
33689         e.preventDefault();
33690         
33691         if(this.bgimage.length){
33692             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33693             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33694         }
33695     }
33696     
33697 });
33698
33699  
33700
33701  /*
33702  * - LGPL
33703  *
33704  * Number field 
33705  */
33706
33707 /**
33708  * @class Roo.bootstrap.NumberField
33709  * @extends Roo.bootstrap.Input
33710  * Bootstrap NumberField class
33711  * 
33712  * 
33713  * 
33714  * 
33715  * @constructor
33716  * Create a new NumberField
33717  * @param {Object} config The config object
33718  */
33719
33720 Roo.bootstrap.NumberField = function(config){
33721     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33722 };
33723
33724 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33725     
33726     /**
33727      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33728      */
33729     allowDecimals : true,
33730     /**
33731      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33732      */
33733     decimalSeparator : ".",
33734     /**
33735      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33736      */
33737     decimalPrecision : 2,
33738     /**
33739      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33740      */
33741     allowNegative : true,
33742     
33743     /**
33744      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33745      */
33746     allowZero: true,
33747     /**
33748      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33749      */
33750     minValue : Number.NEGATIVE_INFINITY,
33751     /**
33752      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33753      */
33754     maxValue : Number.MAX_VALUE,
33755     /**
33756      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33757      */
33758     minText : "The minimum value for this field is {0}",
33759     /**
33760      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33761      */
33762     maxText : "The maximum value for this field is {0}",
33763     /**
33764      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33765      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33766      */
33767     nanText : "{0} is not a valid number",
33768     /**
33769      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33770      */
33771     thousandsDelimiter : false,
33772     /**
33773      * @cfg {String} valueAlign alignment of value
33774      */
33775     valueAlign : "left",
33776
33777     getAutoCreate : function()
33778     {
33779         var hiddenInput = {
33780             tag: 'input',
33781             type: 'hidden',
33782             id: Roo.id(),
33783             cls: 'hidden-number-input'
33784         };
33785         
33786         if (this.name) {
33787             hiddenInput.name = this.name;
33788         }
33789         
33790         this.name = '';
33791         
33792         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33793         
33794         this.name = hiddenInput.name;
33795         
33796         if(cfg.cn.length > 0) {
33797             cfg.cn.push(hiddenInput);
33798         }
33799         
33800         return cfg;
33801     },
33802
33803     // private
33804     initEvents : function()
33805     {   
33806         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33807         
33808         var allowed = "0123456789";
33809         
33810         if(this.allowDecimals){
33811             allowed += this.decimalSeparator;
33812         }
33813         
33814         if(this.allowNegative){
33815             allowed += "-";
33816         }
33817         
33818         if(this.thousandsDelimiter) {
33819             allowed += ",";
33820         }
33821         
33822         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33823         
33824         var keyPress = function(e){
33825             
33826             var k = e.getKey();
33827             
33828             var c = e.getCharCode();
33829             
33830             if(
33831                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33832                     allowed.indexOf(String.fromCharCode(c)) === -1
33833             ){
33834                 e.stopEvent();
33835                 return;
33836             }
33837             
33838             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33839                 return;
33840             }
33841             
33842             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33843                 e.stopEvent();
33844             }
33845         };
33846         
33847         this.el.on("keypress", keyPress, this);
33848     },
33849     
33850     validateValue : function(value)
33851     {
33852         
33853         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33854             return false;
33855         }
33856         
33857         var num = this.parseValue(value);
33858         
33859         if(isNaN(num)){
33860             this.markInvalid(String.format(this.nanText, value));
33861             return false;
33862         }
33863         
33864         if(num < this.minValue){
33865             this.markInvalid(String.format(this.minText, this.minValue));
33866             return false;
33867         }
33868         
33869         if(num > this.maxValue){
33870             this.markInvalid(String.format(this.maxText, this.maxValue));
33871             return false;
33872         }
33873         
33874         return true;
33875     },
33876
33877     getValue : function()
33878     {
33879         var v = this.hiddenEl().getValue();
33880         
33881         return this.fixPrecision(this.parseValue(v));
33882     },
33883
33884     parseValue : function(value)
33885     {
33886         if(this.thousandsDelimiter) {
33887             value += "";
33888             r = new RegExp(",", "g");
33889             value = value.replace(r, "");
33890         }
33891         
33892         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33893         return isNaN(value) ? '' : value;
33894     },
33895
33896     fixPrecision : function(value)
33897     {
33898         if(this.thousandsDelimiter) {
33899             value += "";
33900             r = new RegExp(",", "g");
33901             value = value.replace(r, "");
33902         }
33903         
33904         var nan = isNaN(value);
33905         
33906         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33907             return nan ? '' : value;
33908         }
33909         return parseFloat(value).toFixed(this.decimalPrecision);
33910     },
33911
33912     setValue : function(v)
33913     {
33914         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33915         
33916         this.value = v;
33917         
33918         if(this.rendered){
33919             
33920             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33921             
33922             this.inputEl().dom.value = (v == '') ? '' :
33923                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33924             
33925             if(!this.allowZero && v === '0') {
33926                 this.hiddenEl().dom.value = '';
33927                 this.inputEl().dom.value = '';
33928             }
33929             
33930             this.validate();
33931         }
33932     },
33933
33934     decimalPrecisionFcn : function(v)
33935     {
33936         return Math.floor(v);
33937     },
33938
33939     beforeBlur : function()
33940     {
33941         var v = this.parseValue(this.getRawValue());
33942         
33943         if(v || v === 0 || v === ''){
33944             this.setValue(v);
33945         }
33946     },
33947     
33948     hiddenEl : function()
33949     {
33950         return this.el.select('input.hidden-number-input',true).first();
33951     }
33952     
33953 });
33954
33955  
33956
33957 /*
33958 * Licence: LGPL
33959 */
33960
33961 /**
33962  * @class Roo.bootstrap.DocumentSlider
33963  * @extends Roo.bootstrap.Component
33964  * Bootstrap DocumentSlider class
33965  * 
33966  * @constructor
33967  * Create a new DocumentViewer
33968  * @param {Object} config The config object
33969  */
33970
33971 Roo.bootstrap.DocumentSlider = function(config){
33972     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33973     
33974     this.files = [];
33975     
33976     this.addEvents({
33977         /**
33978          * @event initial
33979          * Fire after initEvent
33980          * @param {Roo.bootstrap.DocumentSlider} this
33981          */
33982         "initial" : true,
33983         /**
33984          * @event update
33985          * Fire after update
33986          * @param {Roo.bootstrap.DocumentSlider} this
33987          */
33988         "update" : true,
33989         /**
33990          * @event click
33991          * Fire after click
33992          * @param {Roo.bootstrap.DocumentSlider} this
33993          */
33994         "click" : true
33995     });
33996 };
33997
33998 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33999     
34000     files : false,
34001     
34002     indicator : 0,
34003     
34004     getAutoCreate : function()
34005     {
34006         var cfg = {
34007             tag : 'div',
34008             cls : 'roo-document-slider',
34009             cn : [
34010                 {
34011                     tag : 'div',
34012                     cls : 'roo-document-slider-header',
34013                     cn : [
34014                         {
34015                             tag : 'div',
34016                             cls : 'roo-document-slider-header-title'
34017                         }
34018                     ]
34019                 },
34020                 {
34021                     tag : 'div',
34022                     cls : 'roo-document-slider-body',
34023                     cn : [
34024                         {
34025                             tag : 'div',
34026                             cls : 'roo-document-slider-prev',
34027                             cn : [
34028                                 {
34029                                     tag : 'i',
34030                                     cls : 'fa fa-chevron-left'
34031                                 }
34032                             ]
34033                         },
34034                         {
34035                             tag : 'div',
34036                             cls : 'roo-document-slider-thumb',
34037                             cn : [
34038                                 {
34039                                     tag : 'img',
34040                                     cls : 'roo-document-slider-image'
34041                                 }
34042                             ]
34043                         },
34044                         {
34045                             tag : 'div',
34046                             cls : 'roo-document-slider-next',
34047                             cn : [
34048                                 {
34049                                     tag : 'i',
34050                                     cls : 'fa fa-chevron-right'
34051                                 }
34052                             ]
34053                         }
34054                     ]
34055                 }
34056             ]
34057         };
34058         
34059         return cfg;
34060     },
34061     
34062     initEvents : function()
34063     {
34064         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
34065         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
34066         
34067         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
34068         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
34069         
34070         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
34071         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
34072         
34073         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
34074         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
34075         
34076         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
34077         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
34078         
34079         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
34080         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34081         
34082         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
34083         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34084         
34085         this.thumbEl.on('click', this.onClick, this);
34086         
34087         this.prevIndicator.on('click', this.prev, this);
34088         
34089         this.nextIndicator.on('click', this.next, this);
34090         
34091     },
34092     
34093     initial : function()
34094     {
34095         if(this.files.length){
34096             this.indicator = 1;
34097             this.update()
34098         }
34099         
34100         this.fireEvent('initial', this);
34101     },
34102     
34103     update : function()
34104     {
34105         this.imageEl.attr('src', this.files[this.indicator - 1]);
34106         
34107         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
34108         
34109         this.prevIndicator.show();
34110         
34111         if(this.indicator == 1){
34112             this.prevIndicator.hide();
34113         }
34114         
34115         this.nextIndicator.show();
34116         
34117         if(this.indicator == this.files.length){
34118             this.nextIndicator.hide();
34119         }
34120         
34121         this.thumbEl.scrollTo('top');
34122         
34123         this.fireEvent('update', this);
34124     },
34125     
34126     onClick : function(e)
34127     {
34128         e.preventDefault();
34129         
34130         this.fireEvent('click', this);
34131     },
34132     
34133     prev : function(e)
34134     {
34135         e.preventDefault();
34136         
34137         this.indicator = Math.max(1, this.indicator - 1);
34138         
34139         this.update();
34140     },
34141     
34142     next : function(e)
34143     {
34144         e.preventDefault();
34145         
34146         this.indicator = Math.min(this.files.length, this.indicator + 1);
34147         
34148         this.update();
34149     }
34150 });
34151 /*
34152  * - LGPL
34153  *
34154  * RadioSet
34155  *
34156  *
34157  */
34158
34159 /**
34160  * @class Roo.bootstrap.RadioSet
34161  * @extends Roo.bootstrap.Input
34162  * Bootstrap RadioSet class
34163  * @cfg {String} indicatorpos (left|right) default left
34164  * @cfg {Boolean} inline (true|false) inline the element (default true)
34165  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
34166  * @constructor
34167  * Create a new RadioSet
34168  * @param {Object} config The config object
34169  */
34170
34171 Roo.bootstrap.RadioSet = function(config){
34172     
34173     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
34174     
34175     this.radioes = [];
34176     
34177     Roo.bootstrap.RadioSet.register(this);
34178     
34179     this.addEvents({
34180         /**
34181         * @event check
34182         * Fires when the element is checked or unchecked.
34183         * @param {Roo.bootstrap.RadioSet} this This radio
34184         * @param {Roo.bootstrap.Radio} item The checked item
34185         */
34186        check : true,
34187        /**
34188         * @event click
34189         * Fires when the element is click.
34190         * @param {Roo.bootstrap.RadioSet} this This radio set
34191         * @param {Roo.bootstrap.Radio} item The checked item
34192         * @param {Roo.EventObject} e The event object
34193         */
34194        click : true
34195     });
34196     
34197 };
34198
34199 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
34200
34201     radioes : false,
34202     
34203     inline : true,
34204     
34205     weight : '',
34206     
34207     indicatorpos : 'left',
34208     
34209     getAutoCreate : function()
34210     {
34211         var label = {
34212             tag : 'label',
34213             cls : 'roo-radio-set-label',
34214             cn : [
34215                 {
34216                     tag : 'span',
34217                     html : this.fieldLabel
34218                 }
34219             ]
34220         };
34221         if (Roo.bootstrap.version == 3) {
34222             
34223             
34224             if(this.indicatorpos == 'left'){
34225                 label.cn.unshift({
34226                     tag : 'i',
34227                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34228                     tooltip : 'This field is required'
34229                 });
34230             } else {
34231                 label.cn.push({
34232                     tag : 'i',
34233                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34234                     tooltip : 'This field is required'
34235                 });
34236             }
34237         }
34238         var items = {
34239             tag : 'div',
34240             cls : 'roo-radio-set-items'
34241         };
34242         
34243         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34244         
34245         if (align === 'left' && this.fieldLabel.length) {
34246             
34247             items = {
34248                 cls : "roo-radio-set-right", 
34249                 cn: [
34250                     items
34251                 ]
34252             };
34253             
34254             if(this.labelWidth > 12){
34255                 label.style = "width: " + this.labelWidth + 'px';
34256             }
34257             
34258             if(this.labelWidth < 13 && this.labelmd == 0){
34259                 this.labelmd = this.labelWidth;
34260             }
34261             
34262             if(this.labellg > 0){
34263                 label.cls += ' col-lg-' + this.labellg;
34264                 items.cls += ' col-lg-' + (12 - this.labellg);
34265             }
34266             
34267             if(this.labelmd > 0){
34268                 label.cls += ' col-md-' + this.labelmd;
34269                 items.cls += ' col-md-' + (12 - this.labelmd);
34270             }
34271             
34272             if(this.labelsm > 0){
34273                 label.cls += ' col-sm-' + this.labelsm;
34274                 items.cls += ' col-sm-' + (12 - this.labelsm);
34275             }
34276             
34277             if(this.labelxs > 0){
34278                 label.cls += ' col-xs-' + this.labelxs;
34279                 items.cls += ' col-xs-' + (12 - this.labelxs);
34280             }
34281         }
34282         
34283         var cfg = {
34284             tag : 'div',
34285             cls : 'roo-radio-set',
34286             cn : [
34287                 {
34288                     tag : 'input',
34289                     cls : 'roo-radio-set-input',
34290                     type : 'hidden',
34291                     name : this.name,
34292                     value : this.value ? this.value :  ''
34293                 },
34294                 label,
34295                 items
34296             ]
34297         };
34298         
34299         if(this.weight.length){
34300             cfg.cls += ' roo-radio-' + this.weight;
34301         }
34302         
34303         if(this.inline) {
34304             cfg.cls += ' roo-radio-set-inline';
34305         }
34306         
34307         var settings=this;
34308         ['xs','sm','md','lg'].map(function(size){
34309             if (settings[size]) {
34310                 cfg.cls += ' col-' + size + '-' + settings[size];
34311             }
34312         });
34313         
34314         return cfg;
34315         
34316     },
34317
34318     initEvents : function()
34319     {
34320         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34321         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34322         
34323         if(!this.fieldLabel.length){
34324             this.labelEl.hide();
34325         }
34326         
34327         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34328         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34329         
34330         this.indicator = this.indicatorEl();
34331         
34332         if(this.indicator){
34333             this.indicator.addClass('invisible');
34334         }
34335         
34336         this.originalValue = this.getValue();
34337         
34338     },
34339     
34340     inputEl: function ()
34341     {
34342         return this.el.select('.roo-radio-set-input', true).first();
34343     },
34344     
34345     getChildContainer : function()
34346     {
34347         return this.itemsEl;
34348     },
34349     
34350     register : function(item)
34351     {
34352         this.radioes.push(item);
34353         
34354     },
34355     
34356     validate : function()
34357     {   
34358         if(this.getVisibilityEl().hasClass('hidden')){
34359             return true;
34360         }
34361         
34362         var valid = false;
34363         
34364         Roo.each(this.radioes, function(i){
34365             if(!i.checked){
34366                 return;
34367             }
34368             
34369             valid = true;
34370             return false;
34371         });
34372         
34373         if(this.allowBlank) {
34374             return true;
34375         }
34376         
34377         if(this.disabled || valid){
34378             this.markValid();
34379             return true;
34380         }
34381         
34382         this.markInvalid();
34383         return false;
34384         
34385     },
34386     
34387     markValid : function()
34388     {
34389         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34390             this.indicatorEl().removeClass('visible');
34391             this.indicatorEl().addClass('invisible');
34392         }
34393         
34394         
34395         if (Roo.bootstrap.version == 3) {
34396             this.el.removeClass([this.invalidClass, this.validClass]);
34397             this.el.addClass(this.validClass);
34398         } else {
34399             this.el.removeClass(['is-invalid','is-valid']);
34400             this.el.addClass(['is-valid']);
34401         }
34402         this.fireEvent('valid', this);
34403     },
34404     
34405     markInvalid : function(msg)
34406     {
34407         if(this.allowBlank || this.disabled){
34408             return;
34409         }
34410         
34411         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34412             this.indicatorEl().removeClass('invisible');
34413             this.indicatorEl().addClass('visible');
34414         }
34415         if (Roo.bootstrap.version == 3) {
34416             this.el.removeClass([this.invalidClass, this.validClass]);
34417             this.el.addClass(this.invalidClass);
34418         } else {
34419             this.el.removeClass(['is-invalid','is-valid']);
34420             this.el.addClass(['is-invalid']);
34421         }
34422         
34423         this.fireEvent('invalid', this, msg);
34424         
34425     },
34426     
34427     setValue : function(v, suppressEvent)
34428     {   
34429         if(this.value === v){
34430             return;
34431         }
34432         
34433         this.value = v;
34434         
34435         if(this.rendered){
34436             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34437         }
34438         
34439         Roo.each(this.radioes, function(i){
34440             i.checked = false;
34441             i.el.removeClass('checked');
34442         });
34443         
34444         Roo.each(this.radioes, function(i){
34445             
34446             if(i.value === v || i.value.toString() === v.toString()){
34447                 i.checked = true;
34448                 i.el.addClass('checked');
34449                 
34450                 if(suppressEvent !== true){
34451                     this.fireEvent('check', this, i);
34452                 }
34453                 
34454                 return false;
34455             }
34456             
34457         }, this);
34458         
34459         this.validate();
34460     },
34461     
34462     clearInvalid : function(){
34463         
34464         if(!this.el || this.preventMark){
34465             return;
34466         }
34467         
34468         this.el.removeClass([this.invalidClass]);
34469         
34470         this.fireEvent('valid', this);
34471     }
34472     
34473 });
34474
34475 Roo.apply(Roo.bootstrap.RadioSet, {
34476     
34477     groups: {},
34478     
34479     register : function(set)
34480     {
34481         this.groups[set.name] = set;
34482     },
34483     
34484     get: function(name) 
34485     {
34486         if (typeof(this.groups[name]) == 'undefined') {
34487             return false;
34488         }
34489         
34490         return this.groups[name] ;
34491     }
34492     
34493 });
34494 /*
34495  * Based on:
34496  * Ext JS Library 1.1.1
34497  * Copyright(c) 2006-2007, Ext JS, LLC.
34498  *
34499  * Originally Released Under LGPL - original licence link has changed is not relivant.
34500  *
34501  * Fork - LGPL
34502  * <script type="text/javascript">
34503  */
34504
34505
34506 /**
34507  * @class Roo.bootstrap.SplitBar
34508  * @extends Roo.util.Observable
34509  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34510  * <br><br>
34511  * Usage:
34512  * <pre><code>
34513 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34514                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34515 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34516 split.minSize = 100;
34517 split.maxSize = 600;
34518 split.animate = true;
34519 split.on('moved', splitterMoved);
34520 </code></pre>
34521  * @constructor
34522  * Create a new SplitBar
34523  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34524  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34525  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34526  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34527                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34528                         position of the SplitBar).
34529  */
34530 Roo.bootstrap.SplitBar = function(cfg){
34531     
34532     /** @private */
34533     
34534     //{
34535     //  dragElement : elm
34536     //  resizingElement: el,
34537         // optional..
34538     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34539     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34540         // existingProxy ???
34541     //}
34542     
34543     this.el = Roo.get(cfg.dragElement, true);
34544     this.el.dom.unselectable = "on";
34545     /** @private */
34546     this.resizingEl = Roo.get(cfg.resizingElement, true);
34547
34548     /**
34549      * @private
34550      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34551      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34552      * @type Number
34553      */
34554     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34555     
34556     /**
34557      * The minimum size of the resizing element. (Defaults to 0)
34558      * @type Number
34559      */
34560     this.minSize = 0;
34561     
34562     /**
34563      * The maximum size of the resizing element. (Defaults to 2000)
34564      * @type Number
34565      */
34566     this.maxSize = 2000;
34567     
34568     /**
34569      * Whether to animate the transition to the new size
34570      * @type Boolean
34571      */
34572     this.animate = false;
34573     
34574     /**
34575      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34576      * @type Boolean
34577      */
34578     this.useShim = false;
34579     
34580     /** @private */
34581     this.shim = null;
34582     
34583     if(!cfg.existingProxy){
34584         /** @private */
34585         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34586     }else{
34587         this.proxy = Roo.get(cfg.existingProxy).dom;
34588     }
34589     /** @private */
34590     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34591     
34592     /** @private */
34593     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34594     
34595     /** @private */
34596     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34597     
34598     /** @private */
34599     this.dragSpecs = {};
34600     
34601     /**
34602      * @private The adapter to use to positon and resize elements
34603      */
34604     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34605     this.adapter.init(this);
34606     
34607     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34608         /** @private */
34609         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34610         this.el.addClass("roo-splitbar-h");
34611     }else{
34612         /** @private */
34613         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34614         this.el.addClass("roo-splitbar-v");
34615     }
34616     
34617     this.addEvents({
34618         /**
34619          * @event resize
34620          * Fires when the splitter is moved (alias for {@link #event-moved})
34621          * @param {Roo.bootstrap.SplitBar} this
34622          * @param {Number} newSize the new width or height
34623          */
34624         "resize" : true,
34625         /**
34626          * @event moved
34627          * Fires when the splitter is moved
34628          * @param {Roo.bootstrap.SplitBar} this
34629          * @param {Number} newSize the new width or height
34630          */
34631         "moved" : true,
34632         /**
34633          * @event beforeresize
34634          * Fires before the splitter is dragged
34635          * @param {Roo.bootstrap.SplitBar} this
34636          */
34637         "beforeresize" : true,
34638
34639         "beforeapply" : true
34640     });
34641
34642     Roo.util.Observable.call(this);
34643 };
34644
34645 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34646     onStartProxyDrag : function(x, y){
34647         this.fireEvent("beforeresize", this);
34648         if(!this.overlay){
34649             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34650             o.unselectable();
34651             o.enableDisplayMode("block");
34652             // all splitbars share the same overlay
34653             Roo.bootstrap.SplitBar.prototype.overlay = o;
34654         }
34655         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34656         this.overlay.show();
34657         Roo.get(this.proxy).setDisplayed("block");
34658         var size = this.adapter.getElementSize(this);
34659         this.activeMinSize = this.getMinimumSize();;
34660         this.activeMaxSize = this.getMaximumSize();;
34661         var c1 = size - this.activeMinSize;
34662         var c2 = Math.max(this.activeMaxSize - size, 0);
34663         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34664             this.dd.resetConstraints();
34665             this.dd.setXConstraint(
34666                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34667                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34668             );
34669             this.dd.setYConstraint(0, 0);
34670         }else{
34671             this.dd.resetConstraints();
34672             this.dd.setXConstraint(0, 0);
34673             this.dd.setYConstraint(
34674                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34675                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34676             );
34677          }
34678         this.dragSpecs.startSize = size;
34679         this.dragSpecs.startPoint = [x, y];
34680         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34681     },
34682     
34683     /** 
34684      * @private Called after the drag operation by the DDProxy
34685      */
34686     onEndProxyDrag : function(e){
34687         Roo.get(this.proxy).setDisplayed(false);
34688         var endPoint = Roo.lib.Event.getXY(e);
34689         if(this.overlay){
34690             this.overlay.hide();
34691         }
34692         var newSize;
34693         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34694             newSize = this.dragSpecs.startSize + 
34695                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34696                     endPoint[0] - this.dragSpecs.startPoint[0] :
34697                     this.dragSpecs.startPoint[0] - endPoint[0]
34698                 );
34699         }else{
34700             newSize = this.dragSpecs.startSize + 
34701                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34702                     endPoint[1] - this.dragSpecs.startPoint[1] :
34703                     this.dragSpecs.startPoint[1] - endPoint[1]
34704                 );
34705         }
34706         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34707         if(newSize != this.dragSpecs.startSize){
34708             if(this.fireEvent('beforeapply', this, newSize) !== false){
34709                 this.adapter.setElementSize(this, newSize);
34710                 this.fireEvent("moved", this, newSize);
34711                 this.fireEvent("resize", this, newSize);
34712             }
34713         }
34714     },
34715     
34716     /**
34717      * Get the adapter this SplitBar uses
34718      * @return The adapter object
34719      */
34720     getAdapter : function(){
34721         return this.adapter;
34722     },
34723     
34724     /**
34725      * Set the adapter this SplitBar uses
34726      * @param {Object} adapter A SplitBar adapter object
34727      */
34728     setAdapter : function(adapter){
34729         this.adapter = adapter;
34730         this.adapter.init(this);
34731     },
34732     
34733     /**
34734      * Gets the minimum size for the resizing element
34735      * @return {Number} The minimum size
34736      */
34737     getMinimumSize : function(){
34738         return this.minSize;
34739     },
34740     
34741     /**
34742      * Sets the minimum size for the resizing element
34743      * @param {Number} minSize The minimum size
34744      */
34745     setMinimumSize : function(minSize){
34746         this.minSize = minSize;
34747     },
34748     
34749     /**
34750      * Gets the maximum size for the resizing element
34751      * @return {Number} The maximum size
34752      */
34753     getMaximumSize : function(){
34754         return this.maxSize;
34755     },
34756     
34757     /**
34758      * Sets the maximum size for the resizing element
34759      * @param {Number} maxSize The maximum size
34760      */
34761     setMaximumSize : function(maxSize){
34762         this.maxSize = maxSize;
34763     },
34764     
34765     /**
34766      * Sets the initialize size for the resizing element
34767      * @param {Number} size The initial size
34768      */
34769     setCurrentSize : function(size){
34770         var oldAnimate = this.animate;
34771         this.animate = false;
34772         this.adapter.setElementSize(this, size);
34773         this.animate = oldAnimate;
34774     },
34775     
34776     /**
34777      * Destroy this splitbar. 
34778      * @param {Boolean} removeEl True to remove the element
34779      */
34780     destroy : function(removeEl){
34781         if(this.shim){
34782             this.shim.remove();
34783         }
34784         this.dd.unreg();
34785         this.proxy.parentNode.removeChild(this.proxy);
34786         if(removeEl){
34787             this.el.remove();
34788         }
34789     }
34790 });
34791
34792 /**
34793  * @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.
34794  */
34795 Roo.bootstrap.SplitBar.createProxy = function(dir){
34796     var proxy = new Roo.Element(document.createElement("div"));
34797     proxy.unselectable();
34798     var cls = 'roo-splitbar-proxy';
34799     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34800     document.body.appendChild(proxy.dom);
34801     return proxy.dom;
34802 };
34803
34804 /** 
34805  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34806  * Default Adapter. It assumes the splitter and resizing element are not positioned
34807  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34808  */
34809 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34810 };
34811
34812 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34813     // do nothing for now
34814     init : function(s){
34815     
34816     },
34817     /**
34818      * Called before drag operations to get the current size of the resizing element. 
34819      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34820      */
34821      getElementSize : function(s){
34822         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34823             return s.resizingEl.getWidth();
34824         }else{
34825             return s.resizingEl.getHeight();
34826         }
34827     },
34828     
34829     /**
34830      * Called after drag operations to set the size of the resizing element.
34831      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34832      * @param {Number} newSize The new size to set
34833      * @param {Function} onComplete A function to be invoked when resizing is complete
34834      */
34835     setElementSize : function(s, newSize, onComplete){
34836         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34837             if(!s.animate){
34838                 s.resizingEl.setWidth(newSize);
34839                 if(onComplete){
34840                     onComplete(s, newSize);
34841                 }
34842             }else{
34843                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34844             }
34845         }else{
34846             
34847             if(!s.animate){
34848                 s.resizingEl.setHeight(newSize);
34849                 if(onComplete){
34850                     onComplete(s, newSize);
34851                 }
34852             }else{
34853                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34854             }
34855         }
34856     }
34857 };
34858
34859 /** 
34860  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34861  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34862  * Adapter that  moves the splitter element to align with the resized sizing element. 
34863  * Used with an absolute positioned SplitBar.
34864  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34865  * document.body, make sure you assign an id to the body element.
34866  */
34867 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34868     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34869     this.container = Roo.get(container);
34870 };
34871
34872 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34873     init : function(s){
34874         this.basic.init(s);
34875     },
34876     
34877     getElementSize : function(s){
34878         return this.basic.getElementSize(s);
34879     },
34880     
34881     setElementSize : function(s, newSize, onComplete){
34882         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34883     },
34884     
34885     moveSplitter : function(s){
34886         var yes = Roo.bootstrap.SplitBar;
34887         switch(s.placement){
34888             case yes.LEFT:
34889                 s.el.setX(s.resizingEl.getRight());
34890                 break;
34891             case yes.RIGHT:
34892                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34893                 break;
34894             case yes.TOP:
34895                 s.el.setY(s.resizingEl.getBottom());
34896                 break;
34897             case yes.BOTTOM:
34898                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34899                 break;
34900         }
34901     }
34902 };
34903
34904 /**
34905  * Orientation constant - Create a vertical SplitBar
34906  * @static
34907  * @type Number
34908  */
34909 Roo.bootstrap.SplitBar.VERTICAL = 1;
34910
34911 /**
34912  * Orientation constant - Create a horizontal SplitBar
34913  * @static
34914  * @type Number
34915  */
34916 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34917
34918 /**
34919  * Placement constant - The resizing element is to the left of the splitter element
34920  * @static
34921  * @type Number
34922  */
34923 Roo.bootstrap.SplitBar.LEFT = 1;
34924
34925 /**
34926  * Placement constant - The resizing element is to the right of the splitter element
34927  * @static
34928  * @type Number
34929  */
34930 Roo.bootstrap.SplitBar.RIGHT = 2;
34931
34932 /**
34933  * Placement constant - The resizing element is positioned above the splitter element
34934  * @static
34935  * @type Number
34936  */
34937 Roo.bootstrap.SplitBar.TOP = 3;
34938
34939 /**
34940  * Placement constant - The resizing element is positioned under splitter element
34941  * @static
34942  * @type Number
34943  */
34944 Roo.bootstrap.SplitBar.BOTTOM = 4;
34945 Roo.namespace("Roo.bootstrap.layout");/*
34946  * Based on:
34947  * Ext JS Library 1.1.1
34948  * Copyright(c) 2006-2007, Ext JS, LLC.
34949  *
34950  * Originally Released Under LGPL - original licence link has changed is not relivant.
34951  *
34952  * Fork - LGPL
34953  * <script type="text/javascript">
34954  */
34955
34956 /**
34957  * @class Roo.bootstrap.layout.Manager
34958  * @extends Roo.bootstrap.Component
34959  * Base class for layout managers.
34960  */
34961 Roo.bootstrap.layout.Manager = function(config)
34962 {
34963     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34964
34965
34966
34967
34968
34969     /** false to disable window resize monitoring @type Boolean */
34970     this.monitorWindowResize = true;
34971     this.regions = {};
34972     this.addEvents({
34973         /**
34974          * @event layout
34975          * Fires when a layout is performed.
34976          * @param {Roo.LayoutManager} this
34977          */
34978         "layout" : true,
34979         /**
34980          * @event regionresized
34981          * Fires when the user resizes a region.
34982          * @param {Roo.LayoutRegion} region The resized region
34983          * @param {Number} newSize The new size (width for east/west, height for north/south)
34984          */
34985         "regionresized" : true,
34986         /**
34987          * @event regioncollapsed
34988          * Fires when a region is collapsed.
34989          * @param {Roo.LayoutRegion} region The collapsed region
34990          */
34991         "regioncollapsed" : true,
34992         /**
34993          * @event regionexpanded
34994          * Fires when a region is expanded.
34995          * @param {Roo.LayoutRegion} region The expanded region
34996          */
34997         "regionexpanded" : true
34998     });
34999     this.updating = false;
35000
35001     if (config.el) {
35002         this.el = Roo.get(config.el);
35003         this.initEvents();
35004     }
35005
35006 };
35007
35008 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
35009
35010
35011     regions : null,
35012
35013     monitorWindowResize : true,
35014
35015
35016     updating : false,
35017
35018
35019     onRender : function(ct, position)
35020     {
35021         if(!this.el){
35022             this.el = Roo.get(ct);
35023             this.initEvents();
35024         }
35025         //this.fireEvent('render',this);
35026     },
35027
35028
35029     initEvents: function()
35030     {
35031
35032
35033         // ie scrollbar fix
35034         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
35035             document.body.scroll = "no";
35036         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
35037             this.el.position('relative');
35038         }
35039         this.id = this.el.id;
35040         this.el.addClass("roo-layout-container");
35041         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
35042         if(this.el.dom != document.body ) {
35043             this.el.on('resize', this.layout,this);
35044             this.el.on('show', this.layout,this);
35045         }
35046
35047     },
35048
35049     /**
35050      * Returns true if this layout is currently being updated
35051      * @return {Boolean}
35052      */
35053     isUpdating : function(){
35054         return this.updating;
35055     },
35056
35057     /**
35058      * Suspend the LayoutManager from doing auto-layouts while
35059      * making multiple add or remove calls
35060      */
35061     beginUpdate : function(){
35062         this.updating = true;
35063     },
35064
35065     /**
35066      * Restore auto-layouts and optionally disable the manager from performing a layout
35067      * @param {Boolean} noLayout true to disable a layout update
35068      */
35069     endUpdate : function(noLayout){
35070         this.updating = false;
35071         if(!noLayout){
35072             this.layout();
35073         }
35074     },
35075
35076     layout: function(){
35077         // abstract...
35078     },
35079
35080     onRegionResized : function(region, newSize){
35081         this.fireEvent("regionresized", region, newSize);
35082         this.layout();
35083     },
35084
35085     onRegionCollapsed : function(region){
35086         this.fireEvent("regioncollapsed", region);
35087     },
35088
35089     onRegionExpanded : function(region){
35090         this.fireEvent("regionexpanded", region);
35091     },
35092
35093     /**
35094      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
35095      * performs box-model adjustments.
35096      * @return {Object} The size as an object {width: (the width), height: (the height)}
35097      */
35098     getViewSize : function()
35099     {
35100         var size;
35101         if(this.el.dom != document.body){
35102             size = this.el.getSize();
35103         }else{
35104             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
35105         }
35106         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
35107         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35108         return size;
35109     },
35110
35111     /**
35112      * Returns the Element this layout is bound to.
35113      * @return {Roo.Element}
35114      */
35115     getEl : function(){
35116         return this.el;
35117     },
35118
35119     /**
35120      * Returns the specified region.
35121      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
35122      * @return {Roo.LayoutRegion}
35123      */
35124     getRegion : function(target){
35125         return this.regions[target.toLowerCase()];
35126     },
35127
35128     onWindowResize : function(){
35129         if(this.monitorWindowResize){
35130             this.layout();
35131         }
35132     }
35133 });
35134 /*
35135  * Based on:
35136  * Ext JS Library 1.1.1
35137  * Copyright(c) 2006-2007, Ext JS, LLC.
35138  *
35139  * Originally Released Under LGPL - original licence link has changed is not relivant.
35140  *
35141  * Fork - LGPL
35142  * <script type="text/javascript">
35143  */
35144 /**
35145  * @class Roo.bootstrap.layout.Border
35146  * @extends Roo.bootstrap.layout.Manager
35147  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
35148  * please see: examples/bootstrap/nested.html<br><br>
35149  
35150 <b>The container the layout is rendered into can be either the body element or any other element.
35151 If it is not the body element, the container needs to either be an absolute positioned element,
35152 or you will need to add "position:relative" to the css of the container.  You will also need to specify
35153 the container size if it is not the body element.</b>
35154
35155 * @constructor
35156 * Create a new Border
35157 * @param {Object} config Configuration options
35158  */
35159 Roo.bootstrap.layout.Border = function(config){
35160     config = config || {};
35161     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
35162     
35163     
35164     
35165     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35166         if(config[region]){
35167             config[region].region = region;
35168             this.addRegion(config[region]);
35169         }
35170     },this);
35171     
35172 };
35173
35174 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
35175
35176 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
35177     
35178     parent : false, // this might point to a 'nest' or a ???
35179     
35180     /**
35181      * Creates and adds a new region if it doesn't already exist.
35182      * @param {String} target The target region key (north, south, east, west or center).
35183      * @param {Object} config The regions config object
35184      * @return {BorderLayoutRegion} The new region
35185      */
35186     addRegion : function(config)
35187     {
35188         if(!this.regions[config.region]){
35189             var r = this.factory(config);
35190             this.bindRegion(r);
35191         }
35192         return this.regions[config.region];
35193     },
35194
35195     // private (kinda)
35196     bindRegion : function(r){
35197         this.regions[r.config.region] = r;
35198         
35199         r.on("visibilitychange",    this.layout, this);
35200         r.on("paneladded",          this.layout, this);
35201         r.on("panelremoved",        this.layout, this);
35202         r.on("invalidated",         this.layout, this);
35203         r.on("resized",             this.onRegionResized, this);
35204         r.on("collapsed",           this.onRegionCollapsed, this);
35205         r.on("expanded",            this.onRegionExpanded, this);
35206     },
35207
35208     /**
35209      * Performs a layout update.
35210      */
35211     layout : function()
35212     {
35213         if(this.updating) {
35214             return;
35215         }
35216         
35217         // render all the rebions if they have not been done alreayd?
35218         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35219             if(this.regions[region] && !this.regions[region].bodyEl){
35220                 this.regions[region].onRender(this.el)
35221             }
35222         },this);
35223         
35224         var size = this.getViewSize();
35225         var w = size.width;
35226         var h = size.height;
35227         var centerW = w;
35228         var centerH = h;
35229         var centerY = 0;
35230         var centerX = 0;
35231         //var x = 0, y = 0;
35232
35233         var rs = this.regions;
35234         var north = rs["north"];
35235         var south = rs["south"]; 
35236         var west = rs["west"];
35237         var east = rs["east"];
35238         var center = rs["center"];
35239         //if(this.hideOnLayout){ // not supported anymore
35240             //c.el.setStyle("display", "none");
35241         //}
35242         if(north && north.isVisible()){
35243             var b = north.getBox();
35244             var m = north.getMargins();
35245             b.width = w - (m.left+m.right);
35246             b.x = m.left;
35247             b.y = m.top;
35248             centerY = b.height + b.y + m.bottom;
35249             centerH -= centerY;
35250             north.updateBox(this.safeBox(b));
35251         }
35252         if(south && south.isVisible()){
35253             var b = south.getBox();
35254             var m = south.getMargins();
35255             b.width = w - (m.left+m.right);
35256             b.x = m.left;
35257             var totalHeight = (b.height + m.top + m.bottom);
35258             b.y = h - totalHeight + m.top;
35259             centerH -= totalHeight;
35260             south.updateBox(this.safeBox(b));
35261         }
35262         if(west && west.isVisible()){
35263             var b = west.getBox();
35264             var m = west.getMargins();
35265             b.height = centerH - (m.top+m.bottom);
35266             b.x = m.left;
35267             b.y = centerY + m.top;
35268             var totalWidth = (b.width + m.left + m.right);
35269             centerX += totalWidth;
35270             centerW -= totalWidth;
35271             west.updateBox(this.safeBox(b));
35272         }
35273         if(east && east.isVisible()){
35274             var b = east.getBox();
35275             var m = east.getMargins();
35276             b.height = centerH - (m.top+m.bottom);
35277             var totalWidth = (b.width + m.left + m.right);
35278             b.x = w - totalWidth + m.left;
35279             b.y = centerY + m.top;
35280             centerW -= totalWidth;
35281             east.updateBox(this.safeBox(b));
35282         }
35283         if(center){
35284             var m = center.getMargins();
35285             var centerBox = {
35286                 x: centerX + m.left,
35287                 y: centerY + m.top,
35288                 width: centerW - (m.left+m.right),
35289                 height: centerH - (m.top+m.bottom)
35290             };
35291             //if(this.hideOnLayout){
35292                 //center.el.setStyle("display", "block");
35293             //}
35294             center.updateBox(this.safeBox(centerBox));
35295         }
35296         this.el.repaint();
35297         this.fireEvent("layout", this);
35298     },
35299
35300     // private
35301     safeBox : function(box){
35302         box.width = Math.max(0, box.width);
35303         box.height = Math.max(0, box.height);
35304         return box;
35305     },
35306
35307     /**
35308      * Adds a ContentPanel (or subclass) to this layout.
35309      * @param {String} target The target region key (north, south, east, west or center).
35310      * @param {Roo.ContentPanel} panel The panel to add
35311      * @return {Roo.ContentPanel} The added panel
35312      */
35313     add : function(target, panel){
35314          
35315         target = target.toLowerCase();
35316         return this.regions[target].add(panel);
35317     },
35318
35319     /**
35320      * Remove a ContentPanel (or subclass) to this layout.
35321      * @param {String} target The target region key (north, south, east, west or center).
35322      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35323      * @return {Roo.ContentPanel} The removed panel
35324      */
35325     remove : function(target, panel){
35326         target = target.toLowerCase();
35327         return this.regions[target].remove(panel);
35328     },
35329
35330     /**
35331      * Searches all regions for a panel with the specified id
35332      * @param {String} panelId
35333      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35334      */
35335     findPanel : function(panelId){
35336         var rs = this.regions;
35337         for(var target in rs){
35338             if(typeof rs[target] != "function"){
35339                 var p = rs[target].getPanel(panelId);
35340                 if(p){
35341                     return p;
35342                 }
35343             }
35344         }
35345         return null;
35346     },
35347
35348     /**
35349      * Searches all regions for a panel with the specified id and activates (shows) it.
35350      * @param {String/ContentPanel} panelId The panels id or the panel itself
35351      * @return {Roo.ContentPanel} The shown panel or null
35352      */
35353     showPanel : function(panelId) {
35354       var rs = this.regions;
35355       for(var target in rs){
35356          var r = rs[target];
35357          if(typeof r != "function"){
35358             if(r.hasPanel(panelId)){
35359                return r.showPanel(panelId);
35360             }
35361          }
35362       }
35363       return null;
35364    },
35365
35366    /**
35367      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35368      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35369      */
35370    /*
35371     restoreState : function(provider){
35372         if(!provider){
35373             provider = Roo.state.Manager;
35374         }
35375         var sm = new Roo.LayoutStateManager();
35376         sm.init(this, provider);
35377     },
35378 */
35379  
35380  
35381     /**
35382      * Adds a xtype elements to the layout.
35383      * <pre><code>
35384
35385 layout.addxtype({
35386        xtype : 'ContentPanel',
35387        region: 'west',
35388        items: [ .... ]
35389    }
35390 );
35391
35392 layout.addxtype({
35393         xtype : 'NestedLayoutPanel',
35394         region: 'west',
35395         layout: {
35396            center: { },
35397            west: { }   
35398         },
35399         items : [ ... list of content panels or nested layout panels.. ]
35400    }
35401 );
35402 </code></pre>
35403      * @param {Object} cfg Xtype definition of item to add.
35404      */
35405     addxtype : function(cfg)
35406     {
35407         // basically accepts a pannel...
35408         // can accept a layout region..!?!?
35409         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35410         
35411         
35412         // theory?  children can only be panels??
35413         
35414         //if (!cfg.xtype.match(/Panel$/)) {
35415         //    return false;
35416         //}
35417         var ret = false;
35418         
35419         if (typeof(cfg.region) == 'undefined') {
35420             Roo.log("Failed to add Panel, region was not set");
35421             Roo.log(cfg);
35422             return false;
35423         }
35424         var region = cfg.region;
35425         delete cfg.region;
35426         
35427           
35428         var xitems = [];
35429         if (cfg.items) {
35430             xitems = cfg.items;
35431             delete cfg.items;
35432         }
35433         var nb = false;
35434         
35435         if ( region == 'center') {
35436             Roo.log("Center: " + cfg.title);
35437         }
35438         
35439         
35440         switch(cfg.xtype) 
35441         {
35442             case 'Content':  // ContentPanel (el, cfg)
35443             case 'Scroll':  // ContentPanel (el, cfg)
35444             case 'View': 
35445                 cfg.autoCreate = cfg.autoCreate || true;
35446                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35447                 //} else {
35448                 //    var el = this.el.createChild();
35449                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35450                 //}
35451                 
35452                 this.add(region, ret);
35453                 break;
35454             
35455             /*
35456             case 'TreePanel': // our new panel!
35457                 cfg.el = this.el.createChild();
35458                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35459                 this.add(region, ret);
35460                 break;
35461             */
35462             
35463             case 'Nest': 
35464                 // create a new Layout (which is  a Border Layout...
35465                 
35466                 var clayout = cfg.layout;
35467                 clayout.el  = this.el.createChild();
35468                 clayout.items   = clayout.items  || [];
35469                 
35470                 delete cfg.layout;
35471                 
35472                 // replace this exitems with the clayout ones..
35473                 xitems = clayout.items;
35474                  
35475                 // force background off if it's in center...
35476                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35477                     cfg.background = false;
35478                 }
35479                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35480                 
35481                 
35482                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35483                 //console.log('adding nested layout panel '  + cfg.toSource());
35484                 this.add(region, ret);
35485                 nb = {}; /// find first...
35486                 break;
35487             
35488             case 'Grid':
35489                 
35490                 // needs grid and region
35491                 
35492                 //var el = this.getRegion(region).el.createChild();
35493                 /*
35494                  *var el = this.el.createChild();
35495                 // create the grid first...
35496                 cfg.grid.container = el;
35497                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35498                 */
35499                 
35500                 if (region == 'center' && this.active ) {
35501                     cfg.background = false;
35502                 }
35503                 
35504                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35505                 
35506                 this.add(region, ret);
35507                 /*
35508                 if (cfg.background) {
35509                     // render grid on panel activation (if panel background)
35510                     ret.on('activate', function(gp) {
35511                         if (!gp.grid.rendered) {
35512                     //        gp.grid.render(el);
35513                         }
35514                     });
35515                 } else {
35516                   //  cfg.grid.render(el);
35517                 }
35518                 */
35519                 break;
35520            
35521            
35522             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35523                 // it was the old xcomponent building that caused this before.
35524                 // espeically if border is the top element in the tree.
35525                 ret = this;
35526                 break; 
35527                 
35528                     
35529                 
35530                 
35531                 
35532             default:
35533                 /*
35534                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35535                     
35536                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35537                     this.add(region, ret);
35538                 } else {
35539                 */
35540                     Roo.log(cfg);
35541                     throw "Can not add '" + cfg.xtype + "' to Border";
35542                     return null;
35543              
35544                                 
35545              
35546         }
35547         this.beginUpdate();
35548         // add children..
35549         var region = '';
35550         var abn = {};
35551         Roo.each(xitems, function(i)  {
35552             region = nb && i.region ? i.region : false;
35553             
35554             var add = ret.addxtype(i);
35555            
35556             if (region) {
35557                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35558                 if (!i.background) {
35559                     abn[region] = nb[region] ;
35560                 }
35561             }
35562             
35563         });
35564         this.endUpdate();
35565
35566         // make the last non-background panel active..
35567         //if (nb) { Roo.log(abn); }
35568         if (nb) {
35569             
35570             for(var r in abn) {
35571                 region = this.getRegion(r);
35572                 if (region) {
35573                     // tried using nb[r], but it does not work..
35574                      
35575                     region.showPanel(abn[r]);
35576                    
35577                 }
35578             }
35579         }
35580         return ret;
35581         
35582     },
35583     
35584     
35585 // private
35586     factory : function(cfg)
35587     {
35588         
35589         var validRegions = Roo.bootstrap.layout.Border.regions;
35590
35591         var target = cfg.region;
35592         cfg.mgr = this;
35593         
35594         var r = Roo.bootstrap.layout;
35595         Roo.log(target);
35596         switch(target){
35597             case "north":
35598                 return new r.North(cfg);
35599             case "south":
35600                 return new r.South(cfg);
35601             case "east":
35602                 return new r.East(cfg);
35603             case "west":
35604                 return new r.West(cfg);
35605             case "center":
35606                 return new r.Center(cfg);
35607         }
35608         throw 'Layout region "'+target+'" not supported.';
35609     }
35610     
35611     
35612 });
35613  /*
35614  * Based on:
35615  * Ext JS Library 1.1.1
35616  * Copyright(c) 2006-2007, Ext JS, LLC.
35617  *
35618  * Originally Released Under LGPL - original licence link has changed is not relivant.
35619  *
35620  * Fork - LGPL
35621  * <script type="text/javascript">
35622  */
35623  
35624 /**
35625  * @class Roo.bootstrap.layout.Basic
35626  * @extends Roo.util.Observable
35627  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35628  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35629  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35630  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35631  * @cfg {string}   region  the region that it inhabits..
35632  * @cfg {bool}   skipConfig skip config?
35633  * 
35634
35635  */
35636 Roo.bootstrap.layout.Basic = function(config){
35637     
35638     this.mgr = config.mgr;
35639     
35640     this.position = config.region;
35641     
35642     var skipConfig = config.skipConfig;
35643     
35644     this.events = {
35645         /**
35646          * @scope Roo.BasicLayoutRegion
35647          */
35648         
35649         /**
35650          * @event beforeremove
35651          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35652          * @param {Roo.LayoutRegion} this
35653          * @param {Roo.ContentPanel} panel The panel
35654          * @param {Object} e The cancel event object
35655          */
35656         "beforeremove" : true,
35657         /**
35658          * @event invalidated
35659          * Fires when the layout for this region is changed.
35660          * @param {Roo.LayoutRegion} this
35661          */
35662         "invalidated" : true,
35663         /**
35664          * @event visibilitychange
35665          * Fires when this region is shown or hidden 
35666          * @param {Roo.LayoutRegion} this
35667          * @param {Boolean} visibility true or false
35668          */
35669         "visibilitychange" : true,
35670         /**
35671          * @event paneladded
35672          * Fires when a panel is added. 
35673          * @param {Roo.LayoutRegion} this
35674          * @param {Roo.ContentPanel} panel The panel
35675          */
35676         "paneladded" : true,
35677         /**
35678          * @event panelremoved
35679          * Fires when a panel is removed. 
35680          * @param {Roo.LayoutRegion} this
35681          * @param {Roo.ContentPanel} panel The panel
35682          */
35683         "panelremoved" : true,
35684         /**
35685          * @event beforecollapse
35686          * Fires when this region before collapse.
35687          * @param {Roo.LayoutRegion} this
35688          */
35689         "beforecollapse" : true,
35690         /**
35691          * @event collapsed
35692          * Fires when this region is collapsed.
35693          * @param {Roo.LayoutRegion} this
35694          */
35695         "collapsed" : true,
35696         /**
35697          * @event expanded
35698          * Fires when this region is expanded.
35699          * @param {Roo.LayoutRegion} this
35700          */
35701         "expanded" : true,
35702         /**
35703          * @event slideshow
35704          * Fires when this region is slid into view.
35705          * @param {Roo.LayoutRegion} this
35706          */
35707         "slideshow" : true,
35708         /**
35709          * @event slidehide
35710          * Fires when this region slides out of view. 
35711          * @param {Roo.LayoutRegion} this
35712          */
35713         "slidehide" : true,
35714         /**
35715          * @event panelactivated
35716          * Fires when a panel is activated. 
35717          * @param {Roo.LayoutRegion} this
35718          * @param {Roo.ContentPanel} panel The activated panel
35719          */
35720         "panelactivated" : true,
35721         /**
35722          * @event resized
35723          * Fires when the user resizes this region. 
35724          * @param {Roo.LayoutRegion} this
35725          * @param {Number} newSize The new size (width for east/west, height for north/south)
35726          */
35727         "resized" : true
35728     };
35729     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35730     this.panels = new Roo.util.MixedCollection();
35731     this.panels.getKey = this.getPanelId.createDelegate(this);
35732     this.box = null;
35733     this.activePanel = null;
35734     // ensure listeners are added...
35735     
35736     if (config.listeners || config.events) {
35737         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35738             listeners : config.listeners || {},
35739             events : config.events || {}
35740         });
35741     }
35742     
35743     if(skipConfig !== true){
35744         this.applyConfig(config);
35745     }
35746 };
35747
35748 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35749 {
35750     getPanelId : function(p){
35751         return p.getId();
35752     },
35753     
35754     applyConfig : function(config){
35755         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35756         this.config = config;
35757         
35758     },
35759     
35760     /**
35761      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35762      * the width, for horizontal (north, south) the height.
35763      * @param {Number} newSize The new width or height
35764      */
35765     resizeTo : function(newSize){
35766         var el = this.el ? this.el :
35767                  (this.activePanel ? this.activePanel.getEl() : null);
35768         if(el){
35769             switch(this.position){
35770                 case "east":
35771                 case "west":
35772                     el.setWidth(newSize);
35773                     this.fireEvent("resized", this, newSize);
35774                 break;
35775                 case "north":
35776                 case "south":
35777                     el.setHeight(newSize);
35778                     this.fireEvent("resized", this, newSize);
35779                 break;                
35780             }
35781         }
35782     },
35783     
35784     getBox : function(){
35785         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35786     },
35787     
35788     getMargins : function(){
35789         return this.margins;
35790     },
35791     
35792     updateBox : function(box){
35793         this.box = box;
35794         var el = this.activePanel.getEl();
35795         el.dom.style.left = box.x + "px";
35796         el.dom.style.top = box.y + "px";
35797         this.activePanel.setSize(box.width, box.height);
35798     },
35799     
35800     /**
35801      * Returns the container element for this region.
35802      * @return {Roo.Element}
35803      */
35804     getEl : function(){
35805         return this.activePanel;
35806     },
35807     
35808     /**
35809      * Returns true if this region is currently visible.
35810      * @return {Boolean}
35811      */
35812     isVisible : function(){
35813         return this.activePanel ? true : false;
35814     },
35815     
35816     setActivePanel : function(panel){
35817         panel = this.getPanel(panel);
35818         if(this.activePanel && this.activePanel != panel){
35819             this.activePanel.setActiveState(false);
35820             this.activePanel.getEl().setLeftTop(-10000,-10000);
35821         }
35822         this.activePanel = panel;
35823         panel.setActiveState(true);
35824         if(this.box){
35825             panel.setSize(this.box.width, this.box.height);
35826         }
35827         this.fireEvent("panelactivated", this, panel);
35828         this.fireEvent("invalidated");
35829     },
35830     
35831     /**
35832      * Show the specified panel.
35833      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35834      * @return {Roo.ContentPanel} The shown panel or null
35835      */
35836     showPanel : function(panel){
35837         panel = this.getPanel(panel);
35838         if(panel){
35839             this.setActivePanel(panel);
35840         }
35841         return panel;
35842     },
35843     
35844     /**
35845      * Get the active panel for this region.
35846      * @return {Roo.ContentPanel} The active panel or null
35847      */
35848     getActivePanel : function(){
35849         return this.activePanel;
35850     },
35851     
35852     /**
35853      * Add the passed ContentPanel(s)
35854      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35855      * @return {Roo.ContentPanel} The panel added (if only one was added)
35856      */
35857     add : function(panel){
35858         if(arguments.length > 1){
35859             for(var i = 0, len = arguments.length; i < len; i++) {
35860                 this.add(arguments[i]);
35861             }
35862             return null;
35863         }
35864         if(this.hasPanel(panel)){
35865             this.showPanel(panel);
35866             return panel;
35867         }
35868         var el = panel.getEl();
35869         if(el.dom.parentNode != this.mgr.el.dom){
35870             this.mgr.el.dom.appendChild(el.dom);
35871         }
35872         if(panel.setRegion){
35873             panel.setRegion(this);
35874         }
35875         this.panels.add(panel);
35876         el.setStyle("position", "absolute");
35877         if(!panel.background){
35878             this.setActivePanel(panel);
35879             if(this.config.initialSize && this.panels.getCount()==1){
35880                 this.resizeTo(this.config.initialSize);
35881             }
35882         }
35883         this.fireEvent("paneladded", this, panel);
35884         return panel;
35885     },
35886     
35887     /**
35888      * Returns true if the panel is in this region.
35889      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35890      * @return {Boolean}
35891      */
35892     hasPanel : function(panel){
35893         if(typeof panel == "object"){ // must be panel obj
35894             panel = panel.getId();
35895         }
35896         return this.getPanel(panel) ? true : false;
35897     },
35898     
35899     /**
35900      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35901      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35902      * @param {Boolean} preservePanel Overrides the config preservePanel option
35903      * @return {Roo.ContentPanel} The panel that was removed
35904      */
35905     remove : function(panel, preservePanel){
35906         panel = this.getPanel(panel);
35907         if(!panel){
35908             return null;
35909         }
35910         var e = {};
35911         this.fireEvent("beforeremove", this, panel, e);
35912         if(e.cancel === true){
35913             return null;
35914         }
35915         var panelId = panel.getId();
35916         this.panels.removeKey(panelId);
35917         return panel;
35918     },
35919     
35920     /**
35921      * Returns the panel specified or null if it's not in this region.
35922      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35923      * @return {Roo.ContentPanel}
35924      */
35925     getPanel : function(id){
35926         if(typeof id == "object"){ // must be panel obj
35927             return id;
35928         }
35929         return this.panels.get(id);
35930     },
35931     
35932     /**
35933      * Returns this regions position (north/south/east/west/center).
35934      * @return {String} 
35935      */
35936     getPosition: function(){
35937         return this.position;    
35938     }
35939 });/*
35940  * Based on:
35941  * Ext JS Library 1.1.1
35942  * Copyright(c) 2006-2007, Ext JS, LLC.
35943  *
35944  * Originally Released Under LGPL - original licence link has changed is not relivant.
35945  *
35946  * Fork - LGPL
35947  * <script type="text/javascript">
35948  */
35949  
35950 /**
35951  * @class Roo.bootstrap.layout.Region
35952  * @extends Roo.bootstrap.layout.Basic
35953  * This class represents a region in a layout manager.
35954  
35955  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35956  * @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})
35957  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35958  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35959  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35960  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35961  * @cfg {String}    title           The title for the region (overrides panel titles)
35962  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35963  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35964  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35965  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35966  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35967  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35968  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35969  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35970  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35971  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35972
35973  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35974  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35975  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35976  * @cfg {Number}    width           For East/West panels
35977  * @cfg {Number}    height          For North/South panels
35978  * @cfg {Boolean}   split           To show the splitter
35979  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35980  * 
35981  * @cfg {string}   cls             Extra CSS classes to add to region
35982  * 
35983  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35984  * @cfg {string}   region  the region that it inhabits..
35985  *
35986
35987  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35988  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35989
35990  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35991  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35992  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35993  */
35994 Roo.bootstrap.layout.Region = function(config)
35995 {
35996     this.applyConfig(config);
35997
35998     var mgr = config.mgr;
35999     var pos = config.region;
36000     config.skipConfig = true;
36001     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
36002     
36003     if (mgr.el) {
36004         this.onRender(mgr.el);   
36005     }
36006      
36007     this.visible = true;
36008     this.collapsed = false;
36009     this.unrendered_panels = [];
36010 };
36011
36012 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
36013
36014     position: '', // set by wrapper (eg. north/south etc..)
36015     unrendered_panels : null,  // unrendered panels.
36016     
36017     tabPosition : false,
36018     
36019     mgr: false, // points to 'Border'
36020     
36021     
36022     createBody : function(){
36023         /** This region's body element 
36024         * @type Roo.Element */
36025         this.bodyEl = this.el.createChild({
36026                 tag: "div",
36027                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
36028         });
36029     },
36030
36031     onRender: function(ctr, pos)
36032     {
36033         var dh = Roo.DomHelper;
36034         /** This region's container element 
36035         * @type Roo.Element */
36036         this.el = dh.append(ctr.dom, {
36037                 tag: "div",
36038                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
36039             }, true);
36040         /** This region's title element 
36041         * @type Roo.Element */
36042     
36043         this.titleEl = dh.append(this.el.dom,  {
36044                 tag: "div",
36045                 unselectable: "on",
36046                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
36047                 children:[
36048                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
36049                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
36050                 ]
36051             }, true);
36052         
36053         this.titleEl.enableDisplayMode();
36054         /** This region's title text element 
36055         * @type HTMLElement */
36056         this.titleTextEl = this.titleEl.dom.firstChild;
36057         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
36058         /*
36059         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
36060         this.closeBtn.enableDisplayMode();
36061         this.closeBtn.on("click", this.closeClicked, this);
36062         this.closeBtn.hide();
36063     */
36064         this.createBody(this.config);
36065         if(this.config.hideWhenEmpty){
36066             this.hide();
36067             this.on("paneladded", this.validateVisibility, this);
36068             this.on("panelremoved", this.validateVisibility, this);
36069         }
36070         if(this.autoScroll){
36071             this.bodyEl.setStyle("overflow", "auto");
36072         }else{
36073             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
36074         }
36075         //if(c.titlebar !== false){
36076             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
36077                 this.titleEl.hide();
36078             }else{
36079                 this.titleEl.show();
36080                 if(this.config.title){
36081                     this.titleTextEl.innerHTML = this.config.title;
36082                 }
36083             }
36084         //}
36085         if(this.config.collapsed){
36086             this.collapse(true);
36087         }
36088         if(this.config.hidden){
36089             this.hide();
36090         }
36091         
36092         if (this.unrendered_panels && this.unrendered_panels.length) {
36093             for (var i =0;i< this.unrendered_panels.length; i++) {
36094                 this.add(this.unrendered_panels[i]);
36095             }
36096             this.unrendered_panels = null;
36097             
36098         }
36099         
36100     },
36101     
36102     applyConfig : function(c)
36103     {
36104         /*
36105          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
36106             var dh = Roo.DomHelper;
36107             if(c.titlebar !== false){
36108                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
36109                 this.collapseBtn.on("click", this.collapse, this);
36110                 this.collapseBtn.enableDisplayMode();
36111                 /*
36112                 if(c.showPin === true || this.showPin){
36113                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
36114                     this.stickBtn.enableDisplayMode();
36115                     this.stickBtn.on("click", this.expand, this);
36116                     this.stickBtn.hide();
36117                 }
36118                 
36119             }
36120             */
36121             /** This region's collapsed element
36122             * @type Roo.Element */
36123             /*
36124              *
36125             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
36126                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
36127             ]}, true);
36128             
36129             if(c.floatable !== false){
36130                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
36131                this.collapsedEl.on("click", this.collapseClick, this);
36132             }
36133
36134             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
36135                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
36136                    id: "message", unselectable: "on", style:{"float":"left"}});
36137                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
36138              }
36139             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
36140             this.expandBtn.on("click", this.expand, this);
36141             
36142         }
36143         
36144         if(this.collapseBtn){
36145             this.collapseBtn.setVisible(c.collapsible == true);
36146         }
36147         
36148         this.cmargins = c.cmargins || this.cmargins ||
36149                          (this.position == "west" || this.position == "east" ?
36150                              {top: 0, left: 2, right:2, bottom: 0} :
36151                              {top: 2, left: 0, right:0, bottom: 2});
36152         */
36153         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36154         
36155         
36156         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
36157         
36158         this.autoScroll = c.autoScroll || false;
36159         
36160         
36161        
36162         
36163         this.duration = c.duration || .30;
36164         this.slideDuration = c.slideDuration || .45;
36165         this.config = c;
36166        
36167     },
36168     /**
36169      * Returns true if this region is currently visible.
36170      * @return {Boolean}
36171      */
36172     isVisible : function(){
36173         return this.visible;
36174     },
36175
36176     /**
36177      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
36178      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
36179      */
36180     //setCollapsedTitle : function(title){
36181     //    title = title || "&#160;";
36182      //   if(this.collapsedTitleTextEl){
36183       //      this.collapsedTitleTextEl.innerHTML = title;
36184        // }
36185     //},
36186
36187     getBox : function(){
36188         var b;
36189       //  if(!this.collapsed){
36190             b = this.el.getBox(false, true);
36191        // }else{
36192           //  b = this.collapsedEl.getBox(false, true);
36193         //}
36194         return b;
36195     },
36196
36197     getMargins : function(){
36198         return this.margins;
36199         //return this.collapsed ? this.cmargins : this.margins;
36200     },
36201 /*
36202     highlight : function(){
36203         this.el.addClass("x-layout-panel-dragover");
36204     },
36205
36206     unhighlight : function(){
36207         this.el.removeClass("x-layout-panel-dragover");
36208     },
36209 */
36210     updateBox : function(box)
36211     {
36212         if (!this.bodyEl) {
36213             return; // not rendered yet..
36214         }
36215         
36216         this.box = box;
36217         if(!this.collapsed){
36218             this.el.dom.style.left = box.x + "px";
36219             this.el.dom.style.top = box.y + "px";
36220             this.updateBody(box.width, box.height);
36221         }else{
36222             this.collapsedEl.dom.style.left = box.x + "px";
36223             this.collapsedEl.dom.style.top = box.y + "px";
36224             this.collapsedEl.setSize(box.width, box.height);
36225         }
36226         if(this.tabs){
36227             this.tabs.autoSizeTabs();
36228         }
36229     },
36230
36231     updateBody : function(w, h)
36232     {
36233         if(w !== null){
36234             this.el.setWidth(w);
36235             w -= this.el.getBorderWidth("rl");
36236             if(this.config.adjustments){
36237                 w += this.config.adjustments[0];
36238             }
36239         }
36240         if(h !== null && h > 0){
36241             this.el.setHeight(h);
36242             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36243             h -= this.el.getBorderWidth("tb");
36244             if(this.config.adjustments){
36245                 h += this.config.adjustments[1];
36246             }
36247             this.bodyEl.setHeight(h);
36248             if(this.tabs){
36249                 h = this.tabs.syncHeight(h);
36250             }
36251         }
36252         if(this.panelSize){
36253             w = w !== null ? w : this.panelSize.width;
36254             h = h !== null ? h : this.panelSize.height;
36255         }
36256         if(this.activePanel){
36257             var el = this.activePanel.getEl();
36258             w = w !== null ? w : el.getWidth();
36259             h = h !== null ? h : el.getHeight();
36260             this.panelSize = {width: w, height: h};
36261             this.activePanel.setSize(w, h);
36262         }
36263         if(Roo.isIE && this.tabs){
36264             this.tabs.el.repaint();
36265         }
36266     },
36267
36268     /**
36269      * Returns the container element for this region.
36270      * @return {Roo.Element}
36271      */
36272     getEl : function(){
36273         return this.el;
36274     },
36275
36276     /**
36277      * Hides this region.
36278      */
36279     hide : function(){
36280         //if(!this.collapsed){
36281             this.el.dom.style.left = "-2000px";
36282             this.el.hide();
36283         //}else{
36284          //   this.collapsedEl.dom.style.left = "-2000px";
36285          //   this.collapsedEl.hide();
36286        // }
36287         this.visible = false;
36288         this.fireEvent("visibilitychange", this, false);
36289     },
36290
36291     /**
36292      * Shows this region if it was previously hidden.
36293      */
36294     show : function(){
36295         //if(!this.collapsed){
36296             this.el.show();
36297         //}else{
36298         //    this.collapsedEl.show();
36299        // }
36300         this.visible = true;
36301         this.fireEvent("visibilitychange", this, true);
36302     },
36303 /*
36304     closeClicked : function(){
36305         if(this.activePanel){
36306             this.remove(this.activePanel);
36307         }
36308     },
36309
36310     collapseClick : function(e){
36311         if(this.isSlid){
36312            e.stopPropagation();
36313            this.slideIn();
36314         }else{
36315            e.stopPropagation();
36316            this.slideOut();
36317         }
36318     },
36319 */
36320     /**
36321      * Collapses this region.
36322      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36323      */
36324     /*
36325     collapse : function(skipAnim, skipCheck = false){
36326         if(this.collapsed) {
36327             return;
36328         }
36329         
36330         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36331             
36332             this.collapsed = true;
36333             if(this.split){
36334                 this.split.el.hide();
36335             }
36336             if(this.config.animate && skipAnim !== true){
36337                 this.fireEvent("invalidated", this);
36338                 this.animateCollapse();
36339             }else{
36340                 this.el.setLocation(-20000,-20000);
36341                 this.el.hide();
36342                 this.collapsedEl.show();
36343                 this.fireEvent("collapsed", this);
36344                 this.fireEvent("invalidated", this);
36345             }
36346         }
36347         
36348     },
36349 */
36350     animateCollapse : function(){
36351         // overridden
36352     },
36353
36354     /**
36355      * Expands this region if it was previously collapsed.
36356      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36357      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36358      */
36359     /*
36360     expand : function(e, skipAnim){
36361         if(e) {
36362             e.stopPropagation();
36363         }
36364         if(!this.collapsed || this.el.hasActiveFx()) {
36365             return;
36366         }
36367         if(this.isSlid){
36368             this.afterSlideIn();
36369             skipAnim = true;
36370         }
36371         this.collapsed = false;
36372         if(this.config.animate && skipAnim !== true){
36373             this.animateExpand();
36374         }else{
36375             this.el.show();
36376             if(this.split){
36377                 this.split.el.show();
36378             }
36379             this.collapsedEl.setLocation(-2000,-2000);
36380             this.collapsedEl.hide();
36381             this.fireEvent("invalidated", this);
36382             this.fireEvent("expanded", this);
36383         }
36384     },
36385 */
36386     animateExpand : function(){
36387         // overridden
36388     },
36389
36390     initTabs : function()
36391     {
36392         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36393         
36394         var ts = new Roo.bootstrap.panel.Tabs({
36395             el: this.bodyEl.dom,
36396             region : this,
36397             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
36398             disableTooltips: this.config.disableTabTips,
36399             toolbar : this.config.toolbar
36400         });
36401         
36402         if(this.config.hideTabs){
36403             ts.stripWrap.setDisplayed(false);
36404         }
36405         this.tabs = ts;
36406         ts.resizeTabs = this.config.resizeTabs === true;
36407         ts.minTabWidth = this.config.minTabWidth || 40;
36408         ts.maxTabWidth = this.config.maxTabWidth || 250;
36409         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36410         ts.monitorResize = false;
36411         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36412         ts.bodyEl.addClass('roo-layout-tabs-body');
36413         this.panels.each(this.initPanelAsTab, this);
36414     },
36415
36416     initPanelAsTab : function(panel){
36417         var ti = this.tabs.addTab(
36418             panel.getEl().id,
36419             panel.getTitle(),
36420             null,
36421             this.config.closeOnTab && panel.isClosable(),
36422             panel.tpl
36423         );
36424         if(panel.tabTip !== undefined){
36425             ti.setTooltip(panel.tabTip);
36426         }
36427         ti.on("activate", function(){
36428               this.setActivePanel(panel);
36429         }, this);
36430         
36431         if(this.config.closeOnTab){
36432             ti.on("beforeclose", function(t, e){
36433                 e.cancel = true;
36434                 this.remove(panel);
36435             }, this);
36436         }
36437         
36438         panel.tabItem = ti;
36439         
36440         return ti;
36441     },
36442
36443     updatePanelTitle : function(panel, title)
36444     {
36445         if(this.activePanel == panel){
36446             this.updateTitle(title);
36447         }
36448         if(this.tabs){
36449             var ti = this.tabs.getTab(panel.getEl().id);
36450             ti.setText(title);
36451             if(panel.tabTip !== undefined){
36452                 ti.setTooltip(panel.tabTip);
36453             }
36454         }
36455     },
36456
36457     updateTitle : function(title){
36458         if(this.titleTextEl && !this.config.title){
36459             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36460         }
36461     },
36462
36463     setActivePanel : function(panel)
36464     {
36465         panel = this.getPanel(panel);
36466         if(this.activePanel && this.activePanel != panel){
36467             if(this.activePanel.setActiveState(false) === false){
36468                 return;
36469             }
36470         }
36471         this.activePanel = panel;
36472         panel.setActiveState(true);
36473         if(this.panelSize){
36474             panel.setSize(this.panelSize.width, this.panelSize.height);
36475         }
36476         if(this.closeBtn){
36477             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36478         }
36479         this.updateTitle(panel.getTitle());
36480         if(this.tabs){
36481             this.fireEvent("invalidated", this);
36482         }
36483         this.fireEvent("panelactivated", this, panel);
36484     },
36485
36486     /**
36487      * Shows the specified panel.
36488      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36489      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36490      */
36491     showPanel : function(panel)
36492     {
36493         panel = this.getPanel(panel);
36494         if(panel){
36495             if(this.tabs){
36496                 var tab = this.tabs.getTab(panel.getEl().id);
36497                 if(tab.isHidden()){
36498                     this.tabs.unhideTab(tab.id);
36499                 }
36500                 tab.activate();
36501             }else{
36502                 this.setActivePanel(panel);
36503             }
36504         }
36505         return panel;
36506     },
36507
36508     /**
36509      * Get the active panel for this region.
36510      * @return {Roo.ContentPanel} The active panel or null
36511      */
36512     getActivePanel : function(){
36513         return this.activePanel;
36514     },
36515
36516     validateVisibility : function(){
36517         if(this.panels.getCount() < 1){
36518             this.updateTitle("&#160;");
36519             this.closeBtn.hide();
36520             this.hide();
36521         }else{
36522             if(!this.isVisible()){
36523                 this.show();
36524             }
36525         }
36526     },
36527
36528     /**
36529      * Adds the passed ContentPanel(s) to this region.
36530      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36531      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36532      */
36533     add : function(panel)
36534     {
36535         if(arguments.length > 1){
36536             for(var i = 0, len = arguments.length; i < len; i++) {
36537                 this.add(arguments[i]);
36538             }
36539             return null;
36540         }
36541         
36542         // if we have not been rendered yet, then we can not really do much of this..
36543         if (!this.bodyEl) {
36544             this.unrendered_panels.push(panel);
36545             return panel;
36546         }
36547         
36548         
36549         
36550         
36551         if(this.hasPanel(panel)){
36552             this.showPanel(panel);
36553             return panel;
36554         }
36555         panel.setRegion(this);
36556         this.panels.add(panel);
36557        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36558             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36559             // and hide them... ???
36560             this.bodyEl.dom.appendChild(panel.getEl().dom);
36561             if(panel.background !== true){
36562                 this.setActivePanel(panel);
36563             }
36564             this.fireEvent("paneladded", this, panel);
36565             return panel;
36566         }
36567         */
36568         if(!this.tabs){
36569             this.initTabs();
36570         }else{
36571             this.initPanelAsTab(panel);
36572         }
36573         
36574         
36575         if(panel.background !== true){
36576             this.tabs.activate(panel.getEl().id);
36577         }
36578         this.fireEvent("paneladded", this, panel);
36579         return panel;
36580     },
36581
36582     /**
36583      * Hides the tab for the specified panel.
36584      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36585      */
36586     hidePanel : function(panel){
36587         if(this.tabs && (panel = this.getPanel(panel))){
36588             this.tabs.hideTab(panel.getEl().id);
36589         }
36590     },
36591
36592     /**
36593      * Unhides the tab for a previously hidden panel.
36594      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36595      */
36596     unhidePanel : function(panel){
36597         if(this.tabs && (panel = this.getPanel(panel))){
36598             this.tabs.unhideTab(panel.getEl().id);
36599         }
36600     },
36601
36602     clearPanels : function(){
36603         while(this.panels.getCount() > 0){
36604              this.remove(this.panels.first());
36605         }
36606     },
36607
36608     /**
36609      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36610      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36611      * @param {Boolean} preservePanel Overrides the config preservePanel option
36612      * @return {Roo.ContentPanel} The panel that was removed
36613      */
36614     remove : function(panel, preservePanel)
36615     {
36616         panel = this.getPanel(panel);
36617         if(!panel){
36618             return null;
36619         }
36620         var e = {};
36621         this.fireEvent("beforeremove", this, panel, e);
36622         if(e.cancel === true){
36623             return null;
36624         }
36625         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36626         var panelId = panel.getId();
36627         this.panels.removeKey(panelId);
36628         if(preservePanel){
36629             document.body.appendChild(panel.getEl().dom);
36630         }
36631         if(this.tabs){
36632             this.tabs.removeTab(panel.getEl().id);
36633         }else if (!preservePanel){
36634             this.bodyEl.dom.removeChild(panel.getEl().dom);
36635         }
36636         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36637             var p = this.panels.first();
36638             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36639             tempEl.appendChild(p.getEl().dom);
36640             this.bodyEl.update("");
36641             this.bodyEl.dom.appendChild(p.getEl().dom);
36642             tempEl = null;
36643             this.updateTitle(p.getTitle());
36644             this.tabs = null;
36645             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36646             this.setActivePanel(p);
36647         }
36648         panel.setRegion(null);
36649         if(this.activePanel == panel){
36650             this.activePanel = null;
36651         }
36652         if(this.config.autoDestroy !== false && preservePanel !== true){
36653             try{panel.destroy();}catch(e){}
36654         }
36655         this.fireEvent("panelremoved", this, panel);
36656         return panel;
36657     },
36658
36659     /**
36660      * Returns the TabPanel component used by this region
36661      * @return {Roo.TabPanel}
36662      */
36663     getTabs : function(){
36664         return this.tabs;
36665     },
36666
36667     createTool : function(parentEl, className){
36668         var btn = Roo.DomHelper.append(parentEl, {
36669             tag: "div",
36670             cls: "x-layout-tools-button",
36671             children: [ {
36672                 tag: "div",
36673                 cls: "roo-layout-tools-button-inner " + className,
36674                 html: "&#160;"
36675             }]
36676         }, true);
36677         btn.addClassOnOver("roo-layout-tools-button-over");
36678         return btn;
36679     }
36680 });/*
36681  * Based on:
36682  * Ext JS Library 1.1.1
36683  * Copyright(c) 2006-2007, Ext JS, LLC.
36684  *
36685  * Originally Released Under LGPL - original licence link has changed is not relivant.
36686  *
36687  * Fork - LGPL
36688  * <script type="text/javascript">
36689  */
36690  
36691
36692
36693 /**
36694  * @class Roo.SplitLayoutRegion
36695  * @extends Roo.LayoutRegion
36696  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36697  */
36698 Roo.bootstrap.layout.Split = function(config){
36699     this.cursor = config.cursor;
36700     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36701 };
36702
36703 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36704 {
36705     splitTip : "Drag to resize.",
36706     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36707     useSplitTips : false,
36708
36709     applyConfig : function(config){
36710         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36711     },
36712     
36713     onRender : function(ctr,pos) {
36714         
36715         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36716         if(!this.config.split){
36717             return;
36718         }
36719         if(!this.split){
36720             
36721             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36722                             tag: "div",
36723                             id: this.el.id + "-split",
36724                             cls: "roo-layout-split roo-layout-split-"+this.position,
36725                             html: "&#160;"
36726             });
36727             /** The SplitBar for this region 
36728             * @type Roo.SplitBar */
36729             // does not exist yet...
36730             Roo.log([this.position, this.orientation]);
36731             
36732             this.split = new Roo.bootstrap.SplitBar({
36733                 dragElement : splitEl,
36734                 resizingElement: this.el,
36735                 orientation : this.orientation
36736             });
36737             
36738             this.split.on("moved", this.onSplitMove, this);
36739             this.split.useShim = this.config.useShim === true;
36740             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36741             if(this.useSplitTips){
36742                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36743             }
36744             //if(config.collapsible){
36745             //    this.split.el.on("dblclick", this.collapse,  this);
36746             //}
36747         }
36748         if(typeof this.config.minSize != "undefined"){
36749             this.split.minSize = this.config.minSize;
36750         }
36751         if(typeof this.config.maxSize != "undefined"){
36752             this.split.maxSize = this.config.maxSize;
36753         }
36754         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36755             this.hideSplitter();
36756         }
36757         
36758     },
36759
36760     getHMaxSize : function(){
36761          var cmax = this.config.maxSize || 10000;
36762          var center = this.mgr.getRegion("center");
36763          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36764     },
36765
36766     getVMaxSize : function(){
36767          var cmax = this.config.maxSize || 10000;
36768          var center = this.mgr.getRegion("center");
36769          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36770     },
36771
36772     onSplitMove : function(split, newSize){
36773         this.fireEvent("resized", this, newSize);
36774     },
36775     
36776     /** 
36777      * Returns the {@link Roo.SplitBar} for this region.
36778      * @return {Roo.SplitBar}
36779      */
36780     getSplitBar : function(){
36781         return this.split;
36782     },
36783     
36784     hide : function(){
36785         this.hideSplitter();
36786         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36787     },
36788
36789     hideSplitter : function(){
36790         if(this.split){
36791             this.split.el.setLocation(-2000,-2000);
36792             this.split.el.hide();
36793         }
36794     },
36795
36796     show : function(){
36797         if(this.split){
36798             this.split.el.show();
36799         }
36800         Roo.bootstrap.layout.Split.superclass.show.call(this);
36801     },
36802     
36803     beforeSlide: function(){
36804         if(Roo.isGecko){// firefox overflow auto bug workaround
36805             this.bodyEl.clip();
36806             if(this.tabs) {
36807                 this.tabs.bodyEl.clip();
36808             }
36809             if(this.activePanel){
36810                 this.activePanel.getEl().clip();
36811                 
36812                 if(this.activePanel.beforeSlide){
36813                     this.activePanel.beforeSlide();
36814                 }
36815             }
36816         }
36817     },
36818     
36819     afterSlide : function(){
36820         if(Roo.isGecko){// firefox overflow auto bug workaround
36821             this.bodyEl.unclip();
36822             if(this.tabs) {
36823                 this.tabs.bodyEl.unclip();
36824             }
36825             if(this.activePanel){
36826                 this.activePanel.getEl().unclip();
36827                 if(this.activePanel.afterSlide){
36828                     this.activePanel.afterSlide();
36829                 }
36830             }
36831         }
36832     },
36833
36834     initAutoHide : function(){
36835         if(this.autoHide !== false){
36836             if(!this.autoHideHd){
36837                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36838                 this.autoHideHd = {
36839                     "mouseout": function(e){
36840                         if(!e.within(this.el, true)){
36841                             st.delay(500);
36842                         }
36843                     },
36844                     "mouseover" : function(e){
36845                         st.cancel();
36846                     },
36847                     scope : this
36848                 };
36849             }
36850             this.el.on(this.autoHideHd);
36851         }
36852     },
36853
36854     clearAutoHide : function(){
36855         if(this.autoHide !== false){
36856             this.el.un("mouseout", this.autoHideHd.mouseout);
36857             this.el.un("mouseover", this.autoHideHd.mouseover);
36858         }
36859     },
36860
36861     clearMonitor : function(){
36862         Roo.get(document).un("click", this.slideInIf, this);
36863     },
36864
36865     // these names are backwards but not changed for compat
36866     slideOut : function(){
36867         if(this.isSlid || this.el.hasActiveFx()){
36868             return;
36869         }
36870         this.isSlid = true;
36871         if(this.collapseBtn){
36872             this.collapseBtn.hide();
36873         }
36874         this.closeBtnState = this.closeBtn.getStyle('display');
36875         this.closeBtn.hide();
36876         if(this.stickBtn){
36877             this.stickBtn.show();
36878         }
36879         this.el.show();
36880         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36881         this.beforeSlide();
36882         this.el.setStyle("z-index", 10001);
36883         this.el.slideIn(this.getSlideAnchor(), {
36884             callback: function(){
36885                 this.afterSlide();
36886                 this.initAutoHide();
36887                 Roo.get(document).on("click", this.slideInIf, this);
36888                 this.fireEvent("slideshow", this);
36889             },
36890             scope: this,
36891             block: true
36892         });
36893     },
36894
36895     afterSlideIn : function(){
36896         this.clearAutoHide();
36897         this.isSlid = false;
36898         this.clearMonitor();
36899         this.el.setStyle("z-index", "");
36900         if(this.collapseBtn){
36901             this.collapseBtn.show();
36902         }
36903         this.closeBtn.setStyle('display', this.closeBtnState);
36904         if(this.stickBtn){
36905             this.stickBtn.hide();
36906         }
36907         this.fireEvent("slidehide", this);
36908     },
36909
36910     slideIn : function(cb){
36911         if(!this.isSlid || this.el.hasActiveFx()){
36912             Roo.callback(cb);
36913             return;
36914         }
36915         this.isSlid = false;
36916         this.beforeSlide();
36917         this.el.slideOut(this.getSlideAnchor(), {
36918             callback: function(){
36919                 this.el.setLeftTop(-10000, -10000);
36920                 this.afterSlide();
36921                 this.afterSlideIn();
36922                 Roo.callback(cb);
36923             },
36924             scope: this,
36925             block: true
36926         });
36927     },
36928     
36929     slideInIf : function(e){
36930         if(!e.within(this.el)){
36931             this.slideIn();
36932         }
36933     },
36934
36935     animateCollapse : function(){
36936         this.beforeSlide();
36937         this.el.setStyle("z-index", 20000);
36938         var anchor = this.getSlideAnchor();
36939         this.el.slideOut(anchor, {
36940             callback : function(){
36941                 this.el.setStyle("z-index", "");
36942                 this.collapsedEl.slideIn(anchor, {duration:.3});
36943                 this.afterSlide();
36944                 this.el.setLocation(-10000,-10000);
36945                 this.el.hide();
36946                 this.fireEvent("collapsed", this);
36947             },
36948             scope: this,
36949             block: true
36950         });
36951     },
36952
36953     animateExpand : function(){
36954         this.beforeSlide();
36955         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36956         this.el.setStyle("z-index", 20000);
36957         this.collapsedEl.hide({
36958             duration:.1
36959         });
36960         this.el.slideIn(this.getSlideAnchor(), {
36961             callback : function(){
36962                 this.el.setStyle("z-index", "");
36963                 this.afterSlide();
36964                 if(this.split){
36965                     this.split.el.show();
36966                 }
36967                 this.fireEvent("invalidated", this);
36968                 this.fireEvent("expanded", this);
36969             },
36970             scope: this,
36971             block: true
36972         });
36973     },
36974
36975     anchors : {
36976         "west" : "left",
36977         "east" : "right",
36978         "north" : "top",
36979         "south" : "bottom"
36980     },
36981
36982     sanchors : {
36983         "west" : "l",
36984         "east" : "r",
36985         "north" : "t",
36986         "south" : "b"
36987     },
36988
36989     canchors : {
36990         "west" : "tl-tr",
36991         "east" : "tr-tl",
36992         "north" : "tl-bl",
36993         "south" : "bl-tl"
36994     },
36995
36996     getAnchor : function(){
36997         return this.anchors[this.position];
36998     },
36999
37000     getCollapseAnchor : function(){
37001         return this.canchors[this.position];
37002     },
37003
37004     getSlideAnchor : function(){
37005         return this.sanchors[this.position];
37006     },
37007
37008     getAlignAdj : function(){
37009         var cm = this.cmargins;
37010         switch(this.position){
37011             case "west":
37012                 return [0, 0];
37013             break;
37014             case "east":
37015                 return [0, 0];
37016             break;
37017             case "north":
37018                 return [0, 0];
37019             break;
37020             case "south":
37021                 return [0, 0];
37022             break;
37023         }
37024     },
37025
37026     getExpandAdj : function(){
37027         var c = this.collapsedEl, cm = this.cmargins;
37028         switch(this.position){
37029             case "west":
37030                 return [-(cm.right+c.getWidth()+cm.left), 0];
37031             break;
37032             case "east":
37033                 return [cm.right+c.getWidth()+cm.left, 0];
37034             break;
37035             case "north":
37036                 return [0, -(cm.top+cm.bottom+c.getHeight())];
37037             break;
37038             case "south":
37039                 return [0, cm.top+cm.bottom+c.getHeight()];
37040             break;
37041         }
37042     }
37043 });/*
37044  * Based on:
37045  * Ext JS Library 1.1.1
37046  * Copyright(c) 2006-2007, Ext JS, LLC.
37047  *
37048  * Originally Released Under LGPL - original licence link has changed is not relivant.
37049  *
37050  * Fork - LGPL
37051  * <script type="text/javascript">
37052  */
37053 /*
37054  * These classes are private internal classes
37055  */
37056 Roo.bootstrap.layout.Center = function(config){
37057     config.region = "center";
37058     Roo.bootstrap.layout.Region.call(this, config);
37059     this.visible = true;
37060     this.minWidth = config.minWidth || 20;
37061     this.minHeight = config.minHeight || 20;
37062 };
37063
37064 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
37065     hide : function(){
37066         // center panel can't be hidden
37067     },
37068     
37069     show : function(){
37070         // center panel can't be hidden
37071     },
37072     
37073     getMinWidth: function(){
37074         return this.minWidth;
37075     },
37076     
37077     getMinHeight: function(){
37078         return this.minHeight;
37079     }
37080 });
37081
37082
37083
37084
37085  
37086
37087
37088
37089
37090
37091
37092 Roo.bootstrap.layout.North = function(config)
37093 {
37094     config.region = 'north';
37095     config.cursor = 'n-resize';
37096     
37097     Roo.bootstrap.layout.Split.call(this, config);
37098     
37099     
37100     if(this.split){
37101         this.split.placement = Roo.bootstrap.SplitBar.TOP;
37102         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37103         this.split.el.addClass("roo-layout-split-v");
37104     }
37105     var size = config.initialSize || config.height;
37106     if(typeof size != "undefined"){
37107         this.el.setHeight(size);
37108     }
37109 };
37110 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
37111 {
37112     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37113     
37114     
37115     
37116     getBox : function(){
37117         if(this.collapsed){
37118             return this.collapsedEl.getBox();
37119         }
37120         var box = this.el.getBox();
37121         if(this.split){
37122             box.height += this.split.el.getHeight();
37123         }
37124         return box;
37125     },
37126     
37127     updateBox : function(box){
37128         if(this.split && !this.collapsed){
37129             box.height -= this.split.el.getHeight();
37130             this.split.el.setLeft(box.x);
37131             this.split.el.setTop(box.y+box.height);
37132             this.split.el.setWidth(box.width);
37133         }
37134         if(this.collapsed){
37135             this.updateBody(box.width, null);
37136         }
37137         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37138     }
37139 });
37140
37141
37142
37143
37144
37145 Roo.bootstrap.layout.South = function(config){
37146     config.region = 'south';
37147     config.cursor = 's-resize';
37148     Roo.bootstrap.layout.Split.call(this, config);
37149     if(this.split){
37150         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
37151         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37152         this.split.el.addClass("roo-layout-split-v");
37153     }
37154     var size = config.initialSize || config.height;
37155     if(typeof size != "undefined"){
37156         this.el.setHeight(size);
37157     }
37158 };
37159
37160 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
37161     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37162     getBox : function(){
37163         if(this.collapsed){
37164             return this.collapsedEl.getBox();
37165         }
37166         var box = this.el.getBox();
37167         if(this.split){
37168             var sh = this.split.el.getHeight();
37169             box.height += sh;
37170             box.y -= sh;
37171         }
37172         return box;
37173     },
37174     
37175     updateBox : function(box){
37176         if(this.split && !this.collapsed){
37177             var sh = this.split.el.getHeight();
37178             box.height -= sh;
37179             box.y += sh;
37180             this.split.el.setLeft(box.x);
37181             this.split.el.setTop(box.y-sh);
37182             this.split.el.setWidth(box.width);
37183         }
37184         if(this.collapsed){
37185             this.updateBody(box.width, null);
37186         }
37187         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37188     }
37189 });
37190
37191 Roo.bootstrap.layout.East = function(config){
37192     config.region = "east";
37193     config.cursor = "e-resize";
37194     Roo.bootstrap.layout.Split.call(this, config);
37195     if(this.split){
37196         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
37197         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37198         this.split.el.addClass("roo-layout-split-h");
37199     }
37200     var size = config.initialSize || config.width;
37201     if(typeof size != "undefined"){
37202         this.el.setWidth(size);
37203     }
37204 };
37205 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
37206     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37207     getBox : function(){
37208         if(this.collapsed){
37209             return this.collapsedEl.getBox();
37210         }
37211         var box = this.el.getBox();
37212         if(this.split){
37213             var sw = this.split.el.getWidth();
37214             box.width += sw;
37215             box.x -= sw;
37216         }
37217         return box;
37218     },
37219
37220     updateBox : function(box){
37221         if(this.split && !this.collapsed){
37222             var sw = this.split.el.getWidth();
37223             box.width -= sw;
37224             this.split.el.setLeft(box.x);
37225             this.split.el.setTop(box.y);
37226             this.split.el.setHeight(box.height);
37227             box.x += sw;
37228         }
37229         if(this.collapsed){
37230             this.updateBody(null, box.height);
37231         }
37232         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37233     }
37234 });
37235
37236 Roo.bootstrap.layout.West = function(config){
37237     config.region = "west";
37238     config.cursor = "w-resize";
37239     
37240     Roo.bootstrap.layout.Split.call(this, config);
37241     if(this.split){
37242         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37243         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37244         this.split.el.addClass("roo-layout-split-h");
37245     }
37246     
37247 };
37248 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37249     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37250     
37251     onRender: function(ctr, pos)
37252     {
37253         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37254         var size = this.config.initialSize || this.config.width;
37255         if(typeof size != "undefined"){
37256             this.el.setWidth(size);
37257         }
37258     },
37259     
37260     getBox : function(){
37261         if(this.collapsed){
37262             return this.collapsedEl.getBox();
37263         }
37264         var box = this.el.getBox();
37265         if(this.split){
37266             box.width += this.split.el.getWidth();
37267         }
37268         return box;
37269     },
37270     
37271     updateBox : function(box){
37272         if(this.split && !this.collapsed){
37273             var sw = this.split.el.getWidth();
37274             box.width -= sw;
37275             this.split.el.setLeft(box.x+box.width);
37276             this.split.el.setTop(box.y);
37277             this.split.el.setHeight(box.height);
37278         }
37279         if(this.collapsed){
37280             this.updateBody(null, box.height);
37281         }
37282         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37283     }
37284 });Roo.namespace("Roo.bootstrap.panel");/*
37285  * Based on:
37286  * Ext JS Library 1.1.1
37287  * Copyright(c) 2006-2007, Ext JS, LLC.
37288  *
37289  * Originally Released Under LGPL - original licence link has changed is not relivant.
37290  *
37291  * Fork - LGPL
37292  * <script type="text/javascript">
37293  */
37294 /**
37295  * @class Roo.ContentPanel
37296  * @extends Roo.util.Observable
37297  * A basic ContentPanel element.
37298  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
37299  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
37300  * @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
37301  * @cfg {Boolean}   closable      True if the panel can be closed/removed
37302  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
37303  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37304  * @cfg {Toolbar}   toolbar       A toolbar for this panel
37305  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
37306  * @cfg {String} title          The title for this panel
37307  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37308  * @cfg {String} url            Calls {@link #setUrl} with this value
37309  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37310  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37311  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37312  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37313  * @cfg {Boolean} badges render the badges
37314
37315  * @constructor
37316  * Create a new ContentPanel.
37317  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37318  * @param {String/Object} config A string to set only the title or a config object
37319  * @param {String} content (optional) Set the HTML content for this panel
37320  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37321  */
37322 Roo.bootstrap.panel.Content = function( config){
37323     
37324     this.tpl = config.tpl || false;
37325     
37326     var el = config.el;
37327     var content = config.content;
37328
37329     if(config.autoCreate){ // xtype is available if this is called from factory
37330         el = Roo.id();
37331     }
37332     this.el = Roo.get(el);
37333     if(!this.el && config && config.autoCreate){
37334         if(typeof config.autoCreate == "object"){
37335             if(!config.autoCreate.id){
37336                 config.autoCreate.id = config.id||el;
37337             }
37338             this.el = Roo.DomHelper.append(document.body,
37339                         config.autoCreate, true);
37340         }else{
37341             var elcfg =  {   tag: "div",
37342                             cls: "roo-layout-inactive-content",
37343                             id: config.id||el
37344                             };
37345             if (config.html) {
37346                 elcfg.html = config.html;
37347                 
37348             }
37349                         
37350             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37351         }
37352     } 
37353     this.closable = false;
37354     this.loaded = false;
37355     this.active = false;
37356    
37357       
37358     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37359         
37360         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37361         
37362         this.wrapEl = this.el; //this.el.wrap();
37363         var ti = [];
37364         if (config.toolbar.items) {
37365             ti = config.toolbar.items ;
37366             delete config.toolbar.items ;
37367         }
37368         
37369         var nitems = [];
37370         this.toolbar.render(this.wrapEl, 'before');
37371         for(var i =0;i < ti.length;i++) {
37372           //  Roo.log(['add child', items[i]]);
37373             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37374         }
37375         this.toolbar.items = nitems;
37376         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37377         delete config.toolbar;
37378         
37379     }
37380     /*
37381     // xtype created footer. - not sure if will work as we normally have to render first..
37382     if (this.footer && !this.footer.el && this.footer.xtype) {
37383         if (!this.wrapEl) {
37384             this.wrapEl = this.el.wrap();
37385         }
37386     
37387         this.footer.container = this.wrapEl.createChild();
37388          
37389         this.footer = Roo.factory(this.footer, Roo);
37390         
37391     }
37392     */
37393     
37394      if(typeof config == "string"){
37395         this.title = config;
37396     }else{
37397         Roo.apply(this, config);
37398     }
37399     
37400     if(this.resizeEl){
37401         this.resizeEl = Roo.get(this.resizeEl, true);
37402     }else{
37403         this.resizeEl = this.el;
37404     }
37405     // handle view.xtype
37406     
37407  
37408     
37409     
37410     this.addEvents({
37411         /**
37412          * @event activate
37413          * Fires when this panel is activated. 
37414          * @param {Roo.ContentPanel} this
37415          */
37416         "activate" : true,
37417         /**
37418          * @event deactivate
37419          * Fires when this panel is activated. 
37420          * @param {Roo.ContentPanel} this
37421          */
37422         "deactivate" : true,
37423
37424         /**
37425          * @event resize
37426          * Fires when this panel is resized if fitToFrame is true.
37427          * @param {Roo.ContentPanel} this
37428          * @param {Number} width The width after any component adjustments
37429          * @param {Number} height The height after any component adjustments
37430          */
37431         "resize" : true,
37432         
37433          /**
37434          * @event render
37435          * Fires when this tab is created
37436          * @param {Roo.ContentPanel} this
37437          */
37438         "render" : true
37439         
37440         
37441         
37442     });
37443     
37444
37445     
37446     
37447     if(this.autoScroll){
37448         this.resizeEl.setStyle("overflow", "auto");
37449     } else {
37450         // fix randome scrolling
37451         //this.el.on('scroll', function() {
37452         //    Roo.log('fix random scolling');
37453         //    this.scrollTo('top',0); 
37454         //});
37455     }
37456     content = content || this.content;
37457     if(content){
37458         this.setContent(content);
37459     }
37460     if(config && config.url){
37461         this.setUrl(this.url, this.params, this.loadOnce);
37462     }
37463     
37464     
37465     
37466     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37467     
37468     if (this.view && typeof(this.view.xtype) != 'undefined') {
37469         this.view.el = this.el.appendChild(document.createElement("div"));
37470         this.view = Roo.factory(this.view); 
37471         this.view.render  &&  this.view.render(false, '');  
37472     }
37473     
37474     
37475     this.fireEvent('render', this);
37476 };
37477
37478 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37479     
37480     tabTip : '',
37481     
37482     setRegion : function(region){
37483         this.region = region;
37484         this.setActiveClass(region && !this.background);
37485     },
37486     
37487     
37488     setActiveClass: function(state)
37489     {
37490         if(state){
37491            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37492            this.el.setStyle('position','relative');
37493         }else{
37494            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37495            this.el.setStyle('position', 'absolute');
37496         } 
37497     },
37498     
37499     /**
37500      * Returns the toolbar for this Panel if one was configured. 
37501      * @return {Roo.Toolbar} 
37502      */
37503     getToolbar : function(){
37504         return this.toolbar;
37505     },
37506     
37507     setActiveState : function(active)
37508     {
37509         this.active = active;
37510         this.setActiveClass(active);
37511         if(!active){
37512             if(this.fireEvent("deactivate", this) === false){
37513                 return false;
37514             }
37515             return true;
37516         }
37517         this.fireEvent("activate", this);
37518         return true;
37519     },
37520     /**
37521      * Updates this panel's element
37522      * @param {String} content The new content
37523      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37524     */
37525     setContent : function(content, loadScripts){
37526         this.el.update(content, loadScripts);
37527     },
37528
37529     ignoreResize : function(w, h){
37530         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37531             return true;
37532         }else{
37533             this.lastSize = {width: w, height: h};
37534             return false;
37535         }
37536     },
37537     /**
37538      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37539      * @return {Roo.UpdateManager} The UpdateManager
37540      */
37541     getUpdateManager : function(){
37542         return this.el.getUpdateManager();
37543     },
37544      /**
37545      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37546      * @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:
37547 <pre><code>
37548 panel.load({
37549     url: "your-url.php",
37550     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37551     callback: yourFunction,
37552     scope: yourObject, //(optional scope)
37553     discardUrl: false,
37554     nocache: false,
37555     text: "Loading...",
37556     timeout: 30,
37557     scripts: false
37558 });
37559 </code></pre>
37560      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37561      * 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.
37562      * @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}
37563      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37564      * @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.
37565      * @return {Roo.ContentPanel} this
37566      */
37567     load : function(){
37568         var um = this.el.getUpdateManager();
37569         um.update.apply(um, arguments);
37570         return this;
37571     },
37572
37573
37574     /**
37575      * 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.
37576      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37577      * @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)
37578      * @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)
37579      * @return {Roo.UpdateManager} The UpdateManager
37580      */
37581     setUrl : function(url, params, loadOnce){
37582         if(this.refreshDelegate){
37583             this.removeListener("activate", this.refreshDelegate);
37584         }
37585         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37586         this.on("activate", this.refreshDelegate);
37587         return this.el.getUpdateManager();
37588     },
37589     
37590     _handleRefresh : function(url, params, loadOnce){
37591         if(!loadOnce || !this.loaded){
37592             var updater = this.el.getUpdateManager();
37593             updater.update(url, params, this._setLoaded.createDelegate(this));
37594         }
37595     },
37596     
37597     _setLoaded : function(){
37598         this.loaded = true;
37599     }, 
37600     
37601     /**
37602      * Returns this panel's id
37603      * @return {String} 
37604      */
37605     getId : function(){
37606         return this.el.id;
37607     },
37608     
37609     /** 
37610      * Returns this panel's element - used by regiosn to add.
37611      * @return {Roo.Element} 
37612      */
37613     getEl : function(){
37614         return this.wrapEl || this.el;
37615     },
37616     
37617    
37618     
37619     adjustForComponents : function(width, height)
37620     {
37621         //Roo.log('adjustForComponents ');
37622         if(this.resizeEl != this.el){
37623             width -= this.el.getFrameWidth('lr');
37624             height -= this.el.getFrameWidth('tb');
37625         }
37626         if(this.toolbar){
37627             var te = this.toolbar.getEl();
37628             te.setWidth(width);
37629             height -= te.getHeight();
37630         }
37631         if(this.footer){
37632             var te = this.footer.getEl();
37633             te.setWidth(width);
37634             height -= te.getHeight();
37635         }
37636         
37637         
37638         if(this.adjustments){
37639             width += this.adjustments[0];
37640             height += this.adjustments[1];
37641         }
37642         return {"width": width, "height": height};
37643     },
37644     
37645     setSize : function(width, height){
37646         if(this.fitToFrame && !this.ignoreResize(width, height)){
37647             if(this.fitContainer && this.resizeEl != this.el){
37648                 this.el.setSize(width, height);
37649             }
37650             var size = this.adjustForComponents(width, height);
37651             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37652             this.fireEvent('resize', this, size.width, size.height);
37653         }
37654     },
37655     
37656     /**
37657      * Returns this panel's title
37658      * @return {String} 
37659      */
37660     getTitle : function(){
37661         
37662         if (typeof(this.title) != 'object') {
37663             return this.title;
37664         }
37665         
37666         var t = '';
37667         for (var k in this.title) {
37668             if (!this.title.hasOwnProperty(k)) {
37669                 continue;
37670             }
37671             
37672             if (k.indexOf('-') >= 0) {
37673                 var s = k.split('-');
37674                 for (var i = 0; i<s.length; i++) {
37675                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37676                 }
37677             } else {
37678                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37679             }
37680         }
37681         return t;
37682     },
37683     
37684     /**
37685      * Set this panel's title
37686      * @param {String} title
37687      */
37688     setTitle : function(title){
37689         this.title = title;
37690         if(this.region){
37691             this.region.updatePanelTitle(this, title);
37692         }
37693     },
37694     
37695     /**
37696      * Returns true is this panel was configured to be closable
37697      * @return {Boolean} 
37698      */
37699     isClosable : function(){
37700         return this.closable;
37701     },
37702     
37703     beforeSlide : function(){
37704         this.el.clip();
37705         this.resizeEl.clip();
37706     },
37707     
37708     afterSlide : function(){
37709         this.el.unclip();
37710         this.resizeEl.unclip();
37711     },
37712     
37713     /**
37714      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37715      *   Will fail silently if the {@link #setUrl} method has not been called.
37716      *   This does not activate the panel, just updates its content.
37717      */
37718     refresh : function(){
37719         if(this.refreshDelegate){
37720            this.loaded = false;
37721            this.refreshDelegate();
37722         }
37723     },
37724     
37725     /**
37726      * Destroys this panel
37727      */
37728     destroy : function(){
37729         this.el.removeAllListeners();
37730         var tempEl = document.createElement("span");
37731         tempEl.appendChild(this.el.dom);
37732         tempEl.innerHTML = "";
37733         this.el.remove();
37734         this.el = null;
37735     },
37736     
37737     /**
37738      * form - if the content panel contains a form - this is a reference to it.
37739      * @type {Roo.form.Form}
37740      */
37741     form : false,
37742     /**
37743      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37744      *    This contains a reference to it.
37745      * @type {Roo.View}
37746      */
37747     view : false,
37748     
37749       /**
37750      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37751      * <pre><code>
37752
37753 layout.addxtype({
37754        xtype : 'Form',
37755        items: [ .... ]
37756    }
37757 );
37758
37759 </code></pre>
37760      * @param {Object} cfg Xtype definition of item to add.
37761      */
37762     
37763     
37764     getChildContainer: function () {
37765         return this.getEl();
37766     }
37767     
37768     
37769     /*
37770         var  ret = new Roo.factory(cfg);
37771         return ret;
37772         
37773         
37774         // add form..
37775         if (cfg.xtype.match(/^Form$/)) {
37776             
37777             var el;
37778             //if (this.footer) {
37779             //    el = this.footer.container.insertSibling(false, 'before');
37780             //} else {
37781                 el = this.el.createChild();
37782             //}
37783
37784             this.form = new  Roo.form.Form(cfg);
37785             
37786             
37787             if ( this.form.allItems.length) {
37788                 this.form.render(el.dom);
37789             }
37790             return this.form;
37791         }
37792         // should only have one of theses..
37793         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37794             // views.. should not be just added - used named prop 'view''
37795             
37796             cfg.el = this.el.appendChild(document.createElement("div"));
37797             // factory?
37798             
37799             var ret = new Roo.factory(cfg);
37800              
37801              ret.render && ret.render(false, ''); // render blank..
37802             this.view = ret;
37803             return ret;
37804         }
37805         return false;
37806     }
37807     \*/
37808 });
37809  
37810 /**
37811  * @class Roo.bootstrap.panel.Grid
37812  * @extends Roo.bootstrap.panel.Content
37813  * @constructor
37814  * Create a new GridPanel.
37815  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37816  * @param {Object} config A the config object
37817   
37818  */
37819
37820
37821
37822 Roo.bootstrap.panel.Grid = function(config)
37823 {
37824     
37825       
37826     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37827         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37828
37829     config.el = this.wrapper;
37830     //this.el = this.wrapper;
37831     
37832       if (config.container) {
37833         // ctor'ed from a Border/panel.grid
37834         
37835         
37836         this.wrapper.setStyle("overflow", "hidden");
37837         this.wrapper.addClass('roo-grid-container');
37838
37839     }
37840     
37841     
37842     if(config.toolbar){
37843         var tool_el = this.wrapper.createChild();    
37844         this.toolbar = Roo.factory(config.toolbar);
37845         var ti = [];
37846         if (config.toolbar.items) {
37847             ti = config.toolbar.items ;
37848             delete config.toolbar.items ;
37849         }
37850         
37851         var nitems = [];
37852         this.toolbar.render(tool_el);
37853         for(var i =0;i < ti.length;i++) {
37854           //  Roo.log(['add child', items[i]]);
37855             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37856         }
37857         this.toolbar.items = nitems;
37858         
37859         delete config.toolbar;
37860     }
37861     
37862     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37863     config.grid.scrollBody = true;;
37864     config.grid.monitorWindowResize = false; // turn off autosizing
37865     config.grid.autoHeight = false;
37866     config.grid.autoWidth = false;
37867     
37868     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37869     
37870     if (config.background) {
37871         // render grid on panel activation (if panel background)
37872         this.on('activate', function(gp) {
37873             if (!gp.grid.rendered) {
37874                 gp.grid.render(this.wrapper);
37875                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37876             }
37877         });
37878             
37879     } else {
37880         this.grid.render(this.wrapper);
37881         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37882
37883     }
37884     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37885     // ??? needed ??? config.el = this.wrapper;
37886     
37887     
37888     
37889   
37890     // xtype created footer. - not sure if will work as we normally have to render first..
37891     if (this.footer && !this.footer.el && this.footer.xtype) {
37892         
37893         var ctr = this.grid.getView().getFooterPanel(true);
37894         this.footer.dataSource = this.grid.dataSource;
37895         this.footer = Roo.factory(this.footer, Roo);
37896         this.footer.render(ctr);
37897         
37898     }
37899     
37900     
37901     
37902     
37903      
37904 };
37905
37906 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37907     getId : function(){
37908         return this.grid.id;
37909     },
37910     
37911     /**
37912      * Returns the grid for this panel
37913      * @return {Roo.bootstrap.Table} 
37914      */
37915     getGrid : function(){
37916         return this.grid;    
37917     },
37918     
37919     setSize : function(width, height){
37920         if(!this.ignoreResize(width, height)){
37921             var grid = this.grid;
37922             var size = this.adjustForComponents(width, height);
37923             var gridel = grid.getGridEl();
37924             gridel.setSize(size.width, size.height);
37925             /*
37926             var thd = grid.getGridEl().select('thead',true).first();
37927             var tbd = grid.getGridEl().select('tbody', true).first();
37928             if (tbd) {
37929                 tbd.setSize(width, height - thd.getHeight());
37930             }
37931             */
37932             grid.autoSize();
37933         }
37934     },
37935      
37936     
37937     
37938     beforeSlide : function(){
37939         this.grid.getView().scroller.clip();
37940     },
37941     
37942     afterSlide : function(){
37943         this.grid.getView().scroller.unclip();
37944     },
37945     
37946     destroy : function(){
37947         this.grid.destroy();
37948         delete this.grid;
37949         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37950     }
37951 });
37952
37953 /**
37954  * @class Roo.bootstrap.panel.Nest
37955  * @extends Roo.bootstrap.panel.Content
37956  * @constructor
37957  * Create a new Panel, that can contain a layout.Border.
37958  * 
37959  * 
37960  * @param {Roo.BorderLayout} layout The layout for this panel
37961  * @param {String/Object} config A string to set only the title or a config object
37962  */
37963 Roo.bootstrap.panel.Nest = function(config)
37964 {
37965     // construct with only one argument..
37966     /* FIXME - implement nicer consturctors
37967     if (layout.layout) {
37968         config = layout;
37969         layout = config.layout;
37970         delete config.layout;
37971     }
37972     if (layout.xtype && !layout.getEl) {
37973         // then layout needs constructing..
37974         layout = Roo.factory(layout, Roo);
37975     }
37976     */
37977     
37978     config.el =  config.layout.getEl();
37979     
37980     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37981     
37982     config.layout.monitorWindowResize = false; // turn off autosizing
37983     this.layout = config.layout;
37984     this.layout.getEl().addClass("roo-layout-nested-layout");
37985     this.layout.parent = this;
37986     
37987     
37988     
37989     
37990 };
37991
37992 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37993
37994     setSize : function(width, height){
37995         if(!this.ignoreResize(width, height)){
37996             var size = this.adjustForComponents(width, height);
37997             var el = this.layout.getEl();
37998             if (size.height < 1) {
37999                 el.setWidth(size.width);   
38000             } else {
38001                 el.setSize(size.width, size.height);
38002             }
38003             var touch = el.dom.offsetWidth;
38004             this.layout.layout();
38005             // ie requires a double layout on the first pass
38006             if(Roo.isIE && !this.initialized){
38007                 this.initialized = true;
38008                 this.layout.layout();
38009             }
38010         }
38011     },
38012     
38013     // activate all subpanels if not currently active..
38014     
38015     setActiveState : function(active){
38016         this.active = active;
38017         this.setActiveClass(active);
38018         
38019         if(!active){
38020             this.fireEvent("deactivate", this);
38021             return;
38022         }
38023         
38024         this.fireEvent("activate", this);
38025         // not sure if this should happen before or after..
38026         if (!this.layout) {
38027             return; // should not happen..
38028         }
38029         var reg = false;
38030         for (var r in this.layout.regions) {
38031             reg = this.layout.getRegion(r);
38032             if (reg.getActivePanel()) {
38033                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
38034                 reg.setActivePanel(reg.getActivePanel());
38035                 continue;
38036             }
38037             if (!reg.panels.length) {
38038                 continue;
38039             }
38040             reg.showPanel(reg.getPanel(0));
38041         }
38042         
38043         
38044         
38045         
38046     },
38047     
38048     /**
38049      * Returns the nested BorderLayout for this panel
38050      * @return {Roo.BorderLayout} 
38051      */
38052     getLayout : function(){
38053         return this.layout;
38054     },
38055     
38056      /**
38057      * Adds a xtype elements to the layout of the nested panel
38058      * <pre><code>
38059
38060 panel.addxtype({
38061        xtype : 'ContentPanel',
38062        region: 'west',
38063        items: [ .... ]
38064    }
38065 );
38066
38067 panel.addxtype({
38068         xtype : 'NestedLayoutPanel',
38069         region: 'west',
38070         layout: {
38071            center: { },
38072            west: { }   
38073         },
38074         items : [ ... list of content panels or nested layout panels.. ]
38075    }
38076 );
38077 </code></pre>
38078      * @param {Object} cfg Xtype definition of item to add.
38079      */
38080     addxtype : function(cfg) {
38081         return this.layout.addxtype(cfg);
38082     
38083     }
38084 });/*
38085  * Based on:
38086  * Ext JS Library 1.1.1
38087  * Copyright(c) 2006-2007, Ext JS, LLC.
38088  *
38089  * Originally Released Under LGPL - original licence link has changed is not relivant.
38090  *
38091  * Fork - LGPL
38092  * <script type="text/javascript">
38093  */
38094 /**
38095  * @class Roo.TabPanel
38096  * @extends Roo.util.Observable
38097  * A lightweight tab container.
38098  * <br><br>
38099  * Usage:
38100  * <pre><code>
38101 // basic tabs 1, built from existing content
38102 var tabs = new Roo.TabPanel("tabs1");
38103 tabs.addTab("script", "View Script");
38104 tabs.addTab("markup", "View Markup");
38105 tabs.activate("script");
38106
38107 // more advanced tabs, built from javascript
38108 var jtabs = new Roo.TabPanel("jtabs");
38109 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
38110
38111 // set up the UpdateManager
38112 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
38113 var updater = tab2.getUpdateManager();
38114 updater.setDefaultUrl("ajax1.htm");
38115 tab2.on('activate', updater.refresh, updater, true);
38116
38117 // Use setUrl for Ajax loading
38118 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
38119 tab3.setUrl("ajax2.htm", null, true);
38120
38121 // Disabled tab
38122 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
38123 tab4.disable();
38124
38125 jtabs.activate("jtabs-1");
38126  * </code></pre>
38127  * @constructor
38128  * Create a new TabPanel.
38129  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
38130  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
38131  */
38132 Roo.bootstrap.panel.Tabs = function(config){
38133     /**
38134     * The container element for this TabPanel.
38135     * @type Roo.Element
38136     */
38137     this.el = Roo.get(config.el);
38138     delete config.el;
38139     if(config){
38140         if(typeof config == "boolean"){
38141             this.tabPosition = config ? "bottom" : "top";
38142         }else{
38143             Roo.apply(this, config);
38144         }
38145     }
38146     
38147     if(this.tabPosition == "bottom"){
38148         // if tabs are at the bottom = create the body first.
38149         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38150         this.el.addClass("roo-tabs-bottom");
38151     }
38152     // next create the tabs holders
38153     
38154     if (this.tabPosition == "west"){
38155         
38156         var reg = this.region; // fake it..
38157         while (reg) {
38158             if (!reg.mgr.parent) {
38159                 break;
38160             }
38161             reg = reg.mgr.parent.region;
38162         }
38163         Roo.log("got nest?");
38164         Roo.log(reg);
38165         if (reg.mgr.getRegion('west')) {
38166             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
38167             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
38168             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38169             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38170             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38171         
38172             
38173         }
38174         
38175         
38176     } else {
38177      
38178         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
38179         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38180         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38181         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38182     }
38183     
38184     
38185     if(Roo.isIE){
38186         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
38187     }
38188     
38189     // finally - if tabs are at the top, then create the body last..
38190     if(this.tabPosition != "bottom"){
38191         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
38192          * @type Roo.Element
38193          */
38194         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38195         this.el.addClass("roo-tabs-top");
38196     }
38197     this.items = [];
38198
38199     this.bodyEl.setStyle("position", "relative");
38200
38201     this.active = null;
38202     this.activateDelegate = this.activate.createDelegate(this);
38203
38204     this.addEvents({
38205         /**
38206          * @event tabchange
38207          * Fires when the active tab changes
38208          * @param {Roo.TabPanel} this
38209          * @param {Roo.TabPanelItem} activePanel The new active tab
38210          */
38211         "tabchange": true,
38212         /**
38213          * @event beforetabchange
38214          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
38215          * @param {Roo.TabPanel} this
38216          * @param {Object} e Set cancel to true on this object to cancel the tab change
38217          * @param {Roo.TabPanelItem} tab The tab being changed to
38218          */
38219         "beforetabchange" : true
38220     });
38221
38222     Roo.EventManager.onWindowResize(this.onResize, this);
38223     this.cpad = this.el.getPadding("lr");
38224     this.hiddenCount = 0;
38225
38226
38227     // toolbar on the tabbar support...
38228     if (this.toolbar) {
38229         alert("no toolbar support yet");
38230         this.toolbar  = false;
38231         /*
38232         var tcfg = this.toolbar;
38233         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
38234         this.toolbar = new Roo.Toolbar(tcfg);
38235         if (Roo.isSafari) {
38236             var tbl = tcfg.container.child('table', true);
38237             tbl.setAttribute('width', '100%');
38238         }
38239         */
38240         
38241     }
38242    
38243
38244
38245     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
38246 };
38247
38248 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
38249     /*
38250      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
38251      */
38252     tabPosition : "top",
38253     /*
38254      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
38255      */
38256     currentTabWidth : 0,
38257     /*
38258      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
38259      */
38260     minTabWidth : 40,
38261     /*
38262      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
38263      */
38264     maxTabWidth : 250,
38265     /*
38266      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
38267      */
38268     preferredTabWidth : 175,
38269     /*
38270      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
38271      */
38272     resizeTabs : false,
38273     /*
38274      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38275      */
38276     monitorResize : true,
38277     /*
38278      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
38279      */
38280     toolbar : false,  // set by caller..
38281     
38282     region : false, /// set by caller
38283     
38284     disableTooltips : true, // not used yet...
38285
38286     /**
38287      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38288      * @param {String} id The id of the div to use <b>or create</b>
38289      * @param {String} text The text for the tab
38290      * @param {String} content (optional) Content to put in the TabPanelItem body
38291      * @param {Boolean} closable (optional) True to create a close icon on the tab
38292      * @return {Roo.TabPanelItem} The created TabPanelItem
38293      */
38294     addTab : function(id, text, content, closable, tpl)
38295     {
38296         var item = new Roo.bootstrap.panel.TabItem({
38297             panel: this,
38298             id : id,
38299             text : text,
38300             closable : closable,
38301             tpl : tpl
38302         });
38303         this.addTabItem(item);
38304         if(content){
38305             item.setContent(content);
38306         }
38307         return item;
38308     },
38309
38310     /**
38311      * Returns the {@link Roo.TabPanelItem} with the specified id/index
38312      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38313      * @return {Roo.TabPanelItem}
38314      */
38315     getTab : function(id){
38316         return this.items[id];
38317     },
38318
38319     /**
38320      * Hides the {@link Roo.TabPanelItem} with the specified id/index
38321      * @param {String/Number} id The id or index of the TabPanelItem to hide.
38322      */
38323     hideTab : function(id){
38324         var t = this.items[id];
38325         if(!t.isHidden()){
38326            t.setHidden(true);
38327            this.hiddenCount++;
38328            this.autoSizeTabs();
38329         }
38330     },
38331
38332     /**
38333      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38334      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38335      */
38336     unhideTab : function(id){
38337         var t = this.items[id];
38338         if(t.isHidden()){
38339            t.setHidden(false);
38340            this.hiddenCount--;
38341            this.autoSizeTabs();
38342         }
38343     },
38344
38345     /**
38346      * Adds an existing {@link Roo.TabPanelItem}.
38347      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38348      */
38349     addTabItem : function(item)
38350     {
38351         this.items[item.id] = item;
38352         this.items.push(item);
38353         this.autoSizeTabs();
38354       //  if(this.resizeTabs){
38355     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38356   //         this.autoSizeTabs();
38357 //        }else{
38358 //            item.autoSize();
38359        // }
38360     },
38361
38362     /**
38363      * Removes a {@link Roo.TabPanelItem}.
38364      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38365      */
38366     removeTab : function(id){
38367         var items = this.items;
38368         var tab = items[id];
38369         if(!tab) { return; }
38370         var index = items.indexOf(tab);
38371         if(this.active == tab && items.length > 1){
38372             var newTab = this.getNextAvailable(index);
38373             if(newTab) {
38374                 newTab.activate();
38375             }
38376         }
38377         this.stripEl.dom.removeChild(tab.pnode.dom);
38378         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38379             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38380         }
38381         items.splice(index, 1);
38382         delete this.items[tab.id];
38383         tab.fireEvent("close", tab);
38384         tab.purgeListeners();
38385         this.autoSizeTabs();
38386     },
38387
38388     getNextAvailable : function(start){
38389         var items = this.items;
38390         var index = start;
38391         // look for a next tab that will slide over to
38392         // replace the one being removed
38393         while(index < items.length){
38394             var item = items[++index];
38395             if(item && !item.isHidden()){
38396                 return item;
38397             }
38398         }
38399         // if one isn't found select the previous tab (on the left)
38400         index = start;
38401         while(index >= 0){
38402             var item = items[--index];
38403             if(item && !item.isHidden()){
38404                 return item;
38405             }
38406         }
38407         return null;
38408     },
38409
38410     /**
38411      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38412      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38413      */
38414     disableTab : function(id){
38415         var tab = this.items[id];
38416         if(tab && this.active != tab){
38417             tab.disable();
38418         }
38419     },
38420
38421     /**
38422      * Enables a {@link Roo.TabPanelItem} that is disabled.
38423      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38424      */
38425     enableTab : function(id){
38426         var tab = this.items[id];
38427         tab.enable();
38428     },
38429
38430     /**
38431      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38432      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38433      * @return {Roo.TabPanelItem} The TabPanelItem.
38434      */
38435     activate : function(id)
38436     {
38437         //Roo.log('activite:'  + id);
38438         
38439         var tab = this.items[id];
38440         if(!tab){
38441             return null;
38442         }
38443         if(tab == this.active || tab.disabled){
38444             return tab;
38445         }
38446         var e = {};
38447         this.fireEvent("beforetabchange", this, e, tab);
38448         if(e.cancel !== true && !tab.disabled){
38449             if(this.active){
38450                 this.active.hide();
38451             }
38452             this.active = this.items[id];
38453             this.active.show();
38454             this.fireEvent("tabchange", this, this.active);
38455         }
38456         return tab;
38457     },
38458
38459     /**
38460      * Gets the active {@link Roo.TabPanelItem}.
38461      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38462      */
38463     getActiveTab : function(){
38464         return this.active;
38465     },
38466
38467     /**
38468      * Updates the tab body element to fit the height of the container element
38469      * for overflow scrolling
38470      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38471      */
38472     syncHeight : function(targetHeight){
38473         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38474         var bm = this.bodyEl.getMargins();
38475         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38476         this.bodyEl.setHeight(newHeight);
38477         return newHeight;
38478     },
38479
38480     onResize : function(){
38481         if(this.monitorResize){
38482             this.autoSizeTabs();
38483         }
38484     },
38485
38486     /**
38487      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38488      */
38489     beginUpdate : function(){
38490         this.updating = true;
38491     },
38492
38493     /**
38494      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38495      */
38496     endUpdate : function(){
38497         this.updating = false;
38498         this.autoSizeTabs();
38499     },
38500
38501     /**
38502      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38503      */
38504     autoSizeTabs : function()
38505     {
38506         var count = this.items.length;
38507         var vcount = count - this.hiddenCount;
38508         
38509         if (vcount < 2) {
38510             this.stripEl.hide();
38511         } else {
38512             this.stripEl.show();
38513         }
38514         
38515         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38516             return;
38517         }
38518         
38519         
38520         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38521         var availWidth = Math.floor(w / vcount);
38522         var b = this.stripBody;
38523         if(b.getWidth() > w){
38524             var tabs = this.items;
38525             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38526             if(availWidth < this.minTabWidth){
38527                 /*if(!this.sleft){    // incomplete scrolling code
38528                     this.createScrollButtons();
38529                 }
38530                 this.showScroll();
38531                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38532             }
38533         }else{
38534             if(this.currentTabWidth < this.preferredTabWidth){
38535                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38536             }
38537         }
38538     },
38539
38540     /**
38541      * Returns the number of tabs in this TabPanel.
38542      * @return {Number}
38543      */
38544      getCount : function(){
38545          return this.items.length;
38546      },
38547
38548     /**
38549      * Resizes all the tabs to the passed width
38550      * @param {Number} The new width
38551      */
38552     setTabWidth : function(width){
38553         this.currentTabWidth = width;
38554         for(var i = 0, len = this.items.length; i < len; i++) {
38555                 if(!this.items[i].isHidden()) {
38556                 this.items[i].setWidth(width);
38557             }
38558         }
38559     },
38560
38561     /**
38562      * Destroys this TabPanel
38563      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38564      */
38565     destroy : function(removeEl){
38566         Roo.EventManager.removeResizeListener(this.onResize, this);
38567         for(var i = 0, len = this.items.length; i < len; i++){
38568             this.items[i].purgeListeners();
38569         }
38570         if(removeEl === true){
38571             this.el.update("");
38572             this.el.remove();
38573         }
38574     },
38575     
38576     createStrip : function(container)
38577     {
38578         var strip = document.createElement("nav");
38579         strip.className = Roo.bootstrap.version == 4 ?
38580             "navbar-light bg-light" : 
38581             "navbar navbar-default"; //"x-tabs-wrap";
38582         container.appendChild(strip);
38583         return strip;
38584     },
38585     
38586     createStripList : function(strip)
38587     {
38588         // div wrapper for retard IE
38589         // returns the "tr" element.
38590         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38591         //'<div class="x-tabs-strip-wrap">'+
38592           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38593           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38594         return strip.firstChild; //.firstChild.firstChild.firstChild;
38595     },
38596     createBody : function(container)
38597     {
38598         var body = document.createElement("div");
38599         Roo.id(body, "tab-body");
38600         //Roo.fly(body).addClass("x-tabs-body");
38601         Roo.fly(body).addClass("tab-content");
38602         container.appendChild(body);
38603         return body;
38604     },
38605     createItemBody :function(bodyEl, id){
38606         var body = Roo.getDom(id);
38607         if(!body){
38608             body = document.createElement("div");
38609             body.id = id;
38610         }
38611         //Roo.fly(body).addClass("x-tabs-item-body");
38612         Roo.fly(body).addClass("tab-pane");
38613          bodyEl.insertBefore(body, bodyEl.firstChild);
38614         return body;
38615     },
38616     /** @private */
38617     createStripElements :  function(stripEl, text, closable, tpl)
38618     {
38619         var td = document.createElement("li"); // was td..
38620         td.className = 'nav-item';
38621         
38622         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38623         
38624         
38625         stripEl.appendChild(td);
38626         /*if(closable){
38627             td.className = "x-tabs-closable";
38628             if(!this.closeTpl){
38629                 this.closeTpl = new Roo.Template(
38630                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38631                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38632                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38633                 );
38634             }
38635             var el = this.closeTpl.overwrite(td, {"text": text});
38636             var close = el.getElementsByTagName("div")[0];
38637             var inner = el.getElementsByTagName("em")[0];
38638             return {"el": el, "close": close, "inner": inner};
38639         } else {
38640         */
38641         // not sure what this is..
38642 //            if(!this.tabTpl){
38643                 //this.tabTpl = new Roo.Template(
38644                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38645                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38646                 //);
38647 //                this.tabTpl = new Roo.Template(
38648 //                   '<a href="#">' +
38649 //                   '<span unselectable="on"' +
38650 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38651 //                            ' >{text}</span></a>'
38652 //                );
38653 //                
38654 //            }
38655
38656
38657             var template = tpl || this.tabTpl || false;
38658             
38659             if(!template){
38660                 template =  new Roo.Template(
38661                         Roo.bootstrap.version == 4 ? 
38662                             (
38663                                 '<a class="nav-link" href="#" unselectable="on"' +
38664                                      (this.disableTooltips ? '' : ' title="{text}"') +
38665                                      ' >{text}</a>'
38666                             ) : (
38667                                 '<a class="nav-link" href="#">' +
38668                                 '<span unselectable="on"' +
38669                                          (this.disableTooltips ? '' : ' title="{text}"') +
38670                                     ' >{text}</span></a>'
38671                             )
38672                 );
38673             }
38674             
38675             switch (typeof(template)) {
38676                 case 'object' :
38677                     break;
38678                 case 'string' :
38679                     template = new Roo.Template(template);
38680                     break;
38681                 default :
38682                     break;
38683             }
38684             
38685             var el = template.overwrite(td, {"text": text});
38686             
38687             var inner = el.getElementsByTagName("span")[0];
38688             
38689             return {"el": el, "inner": inner};
38690             
38691     }
38692         
38693     
38694 });
38695
38696 /**
38697  * @class Roo.TabPanelItem
38698  * @extends Roo.util.Observable
38699  * Represents an individual item (tab plus body) in a TabPanel.
38700  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38701  * @param {String} id The id of this TabPanelItem
38702  * @param {String} text The text for the tab of this TabPanelItem
38703  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38704  */
38705 Roo.bootstrap.panel.TabItem = function(config){
38706     /**
38707      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38708      * @type Roo.TabPanel
38709      */
38710     this.tabPanel = config.panel;
38711     /**
38712      * The id for this TabPanelItem
38713      * @type String
38714      */
38715     this.id = config.id;
38716     /** @private */
38717     this.disabled = false;
38718     /** @private */
38719     this.text = config.text;
38720     /** @private */
38721     this.loaded = false;
38722     this.closable = config.closable;
38723
38724     /**
38725      * The body element for this TabPanelItem.
38726      * @type Roo.Element
38727      */
38728     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38729     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38730     this.bodyEl.setStyle("display", "block");
38731     this.bodyEl.setStyle("zoom", "1");
38732     //this.hideAction();
38733
38734     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38735     /** @private */
38736     this.el = Roo.get(els.el);
38737     this.inner = Roo.get(els.inner, true);
38738      this.textEl = Roo.bootstrap.version == 4 ?
38739         this.el : Roo.get(this.el.dom.firstChild, true);
38740
38741     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
38742     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
38743
38744     
38745 //    this.el.on("mousedown", this.onTabMouseDown, this);
38746     this.el.on("click", this.onTabClick, this);
38747     /** @private */
38748     if(config.closable){
38749         var c = Roo.get(els.close, true);
38750         c.dom.title = this.closeText;
38751         c.addClassOnOver("close-over");
38752         c.on("click", this.closeClick, this);
38753      }
38754
38755     this.addEvents({
38756          /**
38757          * @event activate
38758          * Fires when this tab becomes the active tab.
38759          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38760          * @param {Roo.TabPanelItem} this
38761          */
38762         "activate": true,
38763         /**
38764          * @event beforeclose
38765          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38766          * @param {Roo.TabPanelItem} this
38767          * @param {Object} e Set cancel to true on this object to cancel the close.
38768          */
38769         "beforeclose": true,
38770         /**
38771          * @event close
38772          * Fires when this tab is closed.
38773          * @param {Roo.TabPanelItem} this
38774          */
38775          "close": true,
38776         /**
38777          * @event deactivate
38778          * Fires when this tab is no longer the active tab.
38779          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38780          * @param {Roo.TabPanelItem} this
38781          */
38782          "deactivate" : true
38783     });
38784     this.hidden = false;
38785
38786     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38787 };
38788
38789 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38790            {
38791     purgeListeners : function(){
38792        Roo.util.Observable.prototype.purgeListeners.call(this);
38793        this.el.removeAllListeners();
38794     },
38795     /**
38796      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38797      */
38798     show : function(){
38799         this.status_node.addClass("active");
38800         this.showAction();
38801         if(Roo.isOpera){
38802             this.tabPanel.stripWrap.repaint();
38803         }
38804         this.fireEvent("activate", this.tabPanel, this);
38805     },
38806
38807     /**
38808      * Returns true if this tab is the active tab.
38809      * @return {Boolean}
38810      */
38811     isActive : function(){
38812         return this.tabPanel.getActiveTab() == this;
38813     },
38814
38815     /**
38816      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38817      */
38818     hide : function(){
38819         this.status_node.removeClass("active");
38820         this.hideAction();
38821         this.fireEvent("deactivate", this.tabPanel, this);
38822     },
38823
38824     hideAction : function(){
38825         this.bodyEl.hide();
38826         this.bodyEl.setStyle("position", "absolute");
38827         this.bodyEl.setLeft("-20000px");
38828         this.bodyEl.setTop("-20000px");
38829     },
38830
38831     showAction : function(){
38832         this.bodyEl.setStyle("position", "relative");
38833         this.bodyEl.setTop("");
38834         this.bodyEl.setLeft("");
38835         this.bodyEl.show();
38836     },
38837
38838     /**
38839      * Set the tooltip for the tab.
38840      * @param {String} tooltip The tab's tooltip
38841      */
38842     setTooltip : function(text){
38843         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38844             this.textEl.dom.qtip = text;
38845             this.textEl.dom.removeAttribute('title');
38846         }else{
38847             this.textEl.dom.title = text;
38848         }
38849     },
38850
38851     onTabClick : function(e){
38852         e.preventDefault();
38853         this.tabPanel.activate(this.id);
38854     },
38855
38856     onTabMouseDown : function(e){
38857         e.preventDefault();
38858         this.tabPanel.activate(this.id);
38859     },
38860 /*
38861     getWidth : function(){
38862         return this.inner.getWidth();
38863     },
38864
38865     setWidth : function(width){
38866         var iwidth = width - this.linode.getPadding("lr");
38867         this.inner.setWidth(iwidth);
38868         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38869         this.linode.setWidth(width);
38870     },
38871 */
38872     /**
38873      * Show or hide the tab
38874      * @param {Boolean} hidden True to hide or false to show.
38875      */
38876     setHidden : function(hidden){
38877         this.hidden = hidden;
38878         this.linode.setStyle("display", hidden ? "none" : "");
38879     },
38880
38881     /**
38882      * Returns true if this tab is "hidden"
38883      * @return {Boolean}
38884      */
38885     isHidden : function(){
38886         return this.hidden;
38887     },
38888
38889     /**
38890      * Returns the text for this tab
38891      * @return {String}
38892      */
38893     getText : function(){
38894         return this.text;
38895     },
38896     /*
38897     autoSize : function(){
38898         //this.el.beginMeasure();
38899         this.textEl.setWidth(1);
38900         /*
38901          *  #2804 [new] Tabs in Roojs
38902          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38903          */
38904         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38905         //this.el.endMeasure();
38906     //},
38907
38908     /**
38909      * Sets the text for the tab (Note: this also sets the tooltip text)
38910      * @param {String} text The tab's text and tooltip
38911      */
38912     setText : function(text){
38913         this.text = text;
38914         this.textEl.update(text);
38915         this.setTooltip(text);
38916         //if(!this.tabPanel.resizeTabs){
38917         //    this.autoSize();
38918         //}
38919     },
38920     /**
38921      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38922      */
38923     activate : function(){
38924         this.tabPanel.activate(this.id);
38925     },
38926
38927     /**
38928      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38929      */
38930     disable : function(){
38931         if(this.tabPanel.active != this){
38932             this.disabled = true;
38933             this.status_node.addClass("disabled");
38934         }
38935     },
38936
38937     /**
38938      * Enables this TabPanelItem if it was previously disabled.
38939      */
38940     enable : function(){
38941         this.disabled = false;
38942         this.status_node.removeClass("disabled");
38943     },
38944
38945     /**
38946      * Sets the content for this TabPanelItem.
38947      * @param {String} content The content
38948      * @param {Boolean} loadScripts true to look for and load scripts
38949      */
38950     setContent : function(content, loadScripts){
38951         this.bodyEl.update(content, loadScripts);
38952     },
38953
38954     /**
38955      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38956      * @return {Roo.UpdateManager} The UpdateManager
38957      */
38958     getUpdateManager : function(){
38959         return this.bodyEl.getUpdateManager();
38960     },
38961
38962     /**
38963      * Set a URL to be used to load the content for this TabPanelItem.
38964      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38965      * @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)
38966      * @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)
38967      * @return {Roo.UpdateManager} The UpdateManager
38968      */
38969     setUrl : function(url, params, loadOnce){
38970         if(this.refreshDelegate){
38971             this.un('activate', this.refreshDelegate);
38972         }
38973         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38974         this.on("activate", this.refreshDelegate);
38975         return this.bodyEl.getUpdateManager();
38976     },
38977
38978     /** @private */
38979     _handleRefresh : function(url, params, loadOnce){
38980         if(!loadOnce || !this.loaded){
38981             var updater = this.bodyEl.getUpdateManager();
38982             updater.update(url, params, this._setLoaded.createDelegate(this));
38983         }
38984     },
38985
38986     /**
38987      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38988      *   Will fail silently if the setUrl method has not been called.
38989      *   This does not activate the panel, just updates its content.
38990      */
38991     refresh : function(){
38992         if(this.refreshDelegate){
38993            this.loaded = false;
38994            this.refreshDelegate();
38995         }
38996     },
38997
38998     /** @private */
38999     _setLoaded : function(){
39000         this.loaded = true;
39001     },
39002
39003     /** @private */
39004     closeClick : function(e){
39005         var o = {};
39006         e.stopEvent();
39007         this.fireEvent("beforeclose", this, o);
39008         if(o.cancel !== true){
39009             this.tabPanel.removeTab(this.id);
39010         }
39011     },
39012     /**
39013      * The text displayed in the tooltip for the close icon.
39014      * @type String
39015      */
39016     closeText : "Close this tab"
39017 });
39018 /**
39019 *    This script refer to:
39020 *    Title: International Telephone Input
39021 *    Author: Jack O'Connor
39022 *    Code version:  v12.1.12
39023 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39024 **/
39025
39026 Roo.bootstrap.PhoneInputData = function() {
39027     var d = [
39028       [
39029         "Afghanistan (‫افغانستان‬‎)",
39030         "af",
39031         "93"
39032       ],
39033       [
39034         "Albania (Shqipëri)",
39035         "al",
39036         "355"
39037       ],
39038       [
39039         "Algeria (‫الجزائر‬‎)",
39040         "dz",
39041         "213"
39042       ],
39043       [
39044         "American Samoa",
39045         "as",
39046         "1684"
39047       ],
39048       [
39049         "Andorra",
39050         "ad",
39051         "376"
39052       ],
39053       [
39054         "Angola",
39055         "ao",
39056         "244"
39057       ],
39058       [
39059         "Anguilla",
39060         "ai",
39061         "1264"
39062       ],
39063       [
39064         "Antigua and Barbuda",
39065         "ag",
39066         "1268"
39067       ],
39068       [
39069         "Argentina",
39070         "ar",
39071         "54"
39072       ],
39073       [
39074         "Armenia (Հայաստան)",
39075         "am",
39076         "374"
39077       ],
39078       [
39079         "Aruba",
39080         "aw",
39081         "297"
39082       ],
39083       [
39084         "Australia",
39085         "au",
39086         "61",
39087         0
39088       ],
39089       [
39090         "Austria (Österreich)",
39091         "at",
39092         "43"
39093       ],
39094       [
39095         "Azerbaijan (Azərbaycan)",
39096         "az",
39097         "994"
39098       ],
39099       [
39100         "Bahamas",
39101         "bs",
39102         "1242"
39103       ],
39104       [
39105         "Bahrain (‫البحرين‬‎)",
39106         "bh",
39107         "973"
39108       ],
39109       [
39110         "Bangladesh (বাংলাদেশ)",
39111         "bd",
39112         "880"
39113       ],
39114       [
39115         "Barbados",
39116         "bb",
39117         "1246"
39118       ],
39119       [
39120         "Belarus (Беларусь)",
39121         "by",
39122         "375"
39123       ],
39124       [
39125         "Belgium (België)",
39126         "be",
39127         "32"
39128       ],
39129       [
39130         "Belize",
39131         "bz",
39132         "501"
39133       ],
39134       [
39135         "Benin (Bénin)",
39136         "bj",
39137         "229"
39138       ],
39139       [
39140         "Bermuda",
39141         "bm",
39142         "1441"
39143       ],
39144       [
39145         "Bhutan (འབྲུག)",
39146         "bt",
39147         "975"
39148       ],
39149       [
39150         "Bolivia",
39151         "bo",
39152         "591"
39153       ],
39154       [
39155         "Bosnia and Herzegovina (Босна и Херцеговина)",
39156         "ba",
39157         "387"
39158       ],
39159       [
39160         "Botswana",
39161         "bw",
39162         "267"
39163       ],
39164       [
39165         "Brazil (Brasil)",
39166         "br",
39167         "55"
39168       ],
39169       [
39170         "British Indian Ocean Territory",
39171         "io",
39172         "246"
39173       ],
39174       [
39175         "British Virgin Islands",
39176         "vg",
39177         "1284"
39178       ],
39179       [
39180         "Brunei",
39181         "bn",
39182         "673"
39183       ],
39184       [
39185         "Bulgaria (България)",
39186         "bg",
39187         "359"
39188       ],
39189       [
39190         "Burkina Faso",
39191         "bf",
39192         "226"
39193       ],
39194       [
39195         "Burundi (Uburundi)",
39196         "bi",
39197         "257"
39198       ],
39199       [
39200         "Cambodia (កម្ពុជា)",
39201         "kh",
39202         "855"
39203       ],
39204       [
39205         "Cameroon (Cameroun)",
39206         "cm",
39207         "237"
39208       ],
39209       [
39210         "Canada",
39211         "ca",
39212         "1",
39213         1,
39214         ["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"]
39215       ],
39216       [
39217         "Cape Verde (Kabu Verdi)",
39218         "cv",
39219         "238"
39220       ],
39221       [
39222         "Caribbean Netherlands",
39223         "bq",
39224         "599",
39225         1
39226       ],
39227       [
39228         "Cayman Islands",
39229         "ky",
39230         "1345"
39231       ],
39232       [
39233         "Central African Republic (République centrafricaine)",
39234         "cf",
39235         "236"
39236       ],
39237       [
39238         "Chad (Tchad)",
39239         "td",
39240         "235"
39241       ],
39242       [
39243         "Chile",
39244         "cl",
39245         "56"
39246       ],
39247       [
39248         "China (中国)",
39249         "cn",
39250         "86"
39251       ],
39252       [
39253         "Christmas Island",
39254         "cx",
39255         "61",
39256         2
39257       ],
39258       [
39259         "Cocos (Keeling) Islands",
39260         "cc",
39261         "61",
39262         1
39263       ],
39264       [
39265         "Colombia",
39266         "co",
39267         "57"
39268       ],
39269       [
39270         "Comoros (‫جزر القمر‬‎)",
39271         "km",
39272         "269"
39273       ],
39274       [
39275         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
39276         "cd",
39277         "243"
39278       ],
39279       [
39280         "Congo (Republic) (Congo-Brazzaville)",
39281         "cg",
39282         "242"
39283       ],
39284       [
39285         "Cook Islands",
39286         "ck",
39287         "682"
39288       ],
39289       [
39290         "Costa Rica",
39291         "cr",
39292         "506"
39293       ],
39294       [
39295         "Côte d’Ivoire",
39296         "ci",
39297         "225"
39298       ],
39299       [
39300         "Croatia (Hrvatska)",
39301         "hr",
39302         "385"
39303       ],
39304       [
39305         "Cuba",
39306         "cu",
39307         "53"
39308       ],
39309       [
39310         "Curaçao",
39311         "cw",
39312         "599",
39313         0
39314       ],
39315       [
39316         "Cyprus (Κύπρος)",
39317         "cy",
39318         "357"
39319       ],
39320       [
39321         "Czech Republic (Česká republika)",
39322         "cz",
39323         "420"
39324       ],
39325       [
39326         "Denmark (Danmark)",
39327         "dk",
39328         "45"
39329       ],
39330       [
39331         "Djibouti",
39332         "dj",
39333         "253"
39334       ],
39335       [
39336         "Dominica",
39337         "dm",
39338         "1767"
39339       ],
39340       [
39341         "Dominican Republic (República Dominicana)",
39342         "do",
39343         "1",
39344         2,
39345         ["809", "829", "849"]
39346       ],
39347       [
39348         "Ecuador",
39349         "ec",
39350         "593"
39351       ],
39352       [
39353         "Egypt (‫مصر‬‎)",
39354         "eg",
39355         "20"
39356       ],
39357       [
39358         "El Salvador",
39359         "sv",
39360         "503"
39361       ],
39362       [
39363         "Equatorial Guinea (Guinea Ecuatorial)",
39364         "gq",
39365         "240"
39366       ],
39367       [
39368         "Eritrea",
39369         "er",
39370         "291"
39371       ],
39372       [
39373         "Estonia (Eesti)",
39374         "ee",
39375         "372"
39376       ],
39377       [
39378         "Ethiopia",
39379         "et",
39380         "251"
39381       ],
39382       [
39383         "Falkland Islands (Islas Malvinas)",
39384         "fk",
39385         "500"
39386       ],
39387       [
39388         "Faroe Islands (Føroyar)",
39389         "fo",
39390         "298"
39391       ],
39392       [
39393         "Fiji",
39394         "fj",
39395         "679"
39396       ],
39397       [
39398         "Finland (Suomi)",
39399         "fi",
39400         "358",
39401         0
39402       ],
39403       [
39404         "France",
39405         "fr",
39406         "33"
39407       ],
39408       [
39409         "French Guiana (Guyane française)",
39410         "gf",
39411         "594"
39412       ],
39413       [
39414         "French Polynesia (Polynésie française)",
39415         "pf",
39416         "689"
39417       ],
39418       [
39419         "Gabon",
39420         "ga",
39421         "241"
39422       ],
39423       [
39424         "Gambia",
39425         "gm",
39426         "220"
39427       ],
39428       [
39429         "Georgia (საქართველო)",
39430         "ge",
39431         "995"
39432       ],
39433       [
39434         "Germany (Deutschland)",
39435         "de",
39436         "49"
39437       ],
39438       [
39439         "Ghana (Gaana)",
39440         "gh",
39441         "233"
39442       ],
39443       [
39444         "Gibraltar",
39445         "gi",
39446         "350"
39447       ],
39448       [
39449         "Greece (Ελλάδα)",
39450         "gr",
39451         "30"
39452       ],
39453       [
39454         "Greenland (Kalaallit Nunaat)",
39455         "gl",
39456         "299"
39457       ],
39458       [
39459         "Grenada",
39460         "gd",
39461         "1473"
39462       ],
39463       [
39464         "Guadeloupe",
39465         "gp",
39466         "590",
39467         0
39468       ],
39469       [
39470         "Guam",
39471         "gu",
39472         "1671"
39473       ],
39474       [
39475         "Guatemala",
39476         "gt",
39477         "502"
39478       ],
39479       [
39480         "Guernsey",
39481         "gg",
39482         "44",
39483         1
39484       ],
39485       [
39486         "Guinea (Guinée)",
39487         "gn",
39488         "224"
39489       ],
39490       [
39491         "Guinea-Bissau (Guiné Bissau)",
39492         "gw",
39493         "245"
39494       ],
39495       [
39496         "Guyana",
39497         "gy",
39498         "592"
39499       ],
39500       [
39501         "Haiti",
39502         "ht",
39503         "509"
39504       ],
39505       [
39506         "Honduras",
39507         "hn",
39508         "504"
39509       ],
39510       [
39511         "Hong Kong (香港)",
39512         "hk",
39513         "852"
39514       ],
39515       [
39516         "Hungary (Magyarország)",
39517         "hu",
39518         "36"
39519       ],
39520       [
39521         "Iceland (Ísland)",
39522         "is",
39523         "354"
39524       ],
39525       [
39526         "India (भारत)",
39527         "in",
39528         "91"
39529       ],
39530       [
39531         "Indonesia",
39532         "id",
39533         "62"
39534       ],
39535       [
39536         "Iran (‫ایران‬‎)",
39537         "ir",
39538         "98"
39539       ],
39540       [
39541         "Iraq (‫العراق‬‎)",
39542         "iq",
39543         "964"
39544       ],
39545       [
39546         "Ireland",
39547         "ie",
39548         "353"
39549       ],
39550       [
39551         "Isle of Man",
39552         "im",
39553         "44",
39554         2
39555       ],
39556       [
39557         "Israel (‫ישראל‬‎)",
39558         "il",
39559         "972"
39560       ],
39561       [
39562         "Italy (Italia)",
39563         "it",
39564         "39",
39565         0
39566       ],
39567       [
39568         "Jamaica",
39569         "jm",
39570         "1876"
39571       ],
39572       [
39573         "Japan (日本)",
39574         "jp",
39575         "81"
39576       ],
39577       [
39578         "Jersey",
39579         "je",
39580         "44",
39581         3
39582       ],
39583       [
39584         "Jordan (‫الأردن‬‎)",
39585         "jo",
39586         "962"
39587       ],
39588       [
39589         "Kazakhstan (Казахстан)",
39590         "kz",
39591         "7",
39592         1
39593       ],
39594       [
39595         "Kenya",
39596         "ke",
39597         "254"
39598       ],
39599       [
39600         "Kiribati",
39601         "ki",
39602         "686"
39603       ],
39604       [
39605         "Kosovo",
39606         "xk",
39607         "383"
39608       ],
39609       [
39610         "Kuwait (‫الكويت‬‎)",
39611         "kw",
39612         "965"
39613       ],
39614       [
39615         "Kyrgyzstan (Кыргызстан)",
39616         "kg",
39617         "996"
39618       ],
39619       [
39620         "Laos (ລາວ)",
39621         "la",
39622         "856"
39623       ],
39624       [
39625         "Latvia (Latvija)",
39626         "lv",
39627         "371"
39628       ],
39629       [
39630         "Lebanon (‫لبنان‬‎)",
39631         "lb",
39632         "961"
39633       ],
39634       [
39635         "Lesotho",
39636         "ls",
39637         "266"
39638       ],
39639       [
39640         "Liberia",
39641         "lr",
39642         "231"
39643       ],
39644       [
39645         "Libya (‫ليبيا‬‎)",
39646         "ly",
39647         "218"
39648       ],
39649       [
39650         "Liechtenstein",
39651         "li",
39652         "423"
39653       ],
39654       [
39655         "Lithuania (Lietuva)",
39656         "lt",
39657         "370"
39658       ],
39659       [
39660         "Luxembourg",
39661         "lu",
39662         "352"
39663       ],
39664       [
39665         "Macau (澳門)",
39666         "mo",
39667         "853"
39668       ],
39669       [
39670         "Macedonia (FYROM) (Македонија)",
39671         "mk",
39672         "389"
39673       ],
39674       [
39675         "Madagascar (Madagasikara)",
39676         "mg",
39677         "261"
39678       ],
39679       [
39680         "Malawi",
39681         "mw",
39682         "265"
39683       ],
39684       [
39685         "Malaysia",
39686         "my",
39687         "60"
39688       ],
39689       [
39690         "Maldives",
39691         "mv",
39692         "960"
39693       ],
39694       [
39695         "Mali",
39696         "ml",
39697         "223"
39698       ],
39699       [
39700         "Malta",
39701         "mt",
39702         "356"
39703       ],
39704       [
39705         "Marshall Islands",
39706         "mh",
39707         "692"
39708       ],
39709       [
39710         "Martinique",
39711         "mq",
39712         "596"
39713       ],
39714       [
39715         "Mauritania (‫موريتانيا‬‎)",
39716         "mr",
39717         "222"
39718       ],
39719       [
39720         "Mauritius (Moris)",
39721         "mu",
39722         "230"
39723       ],
39724       [
39725         "Mayotte",
39726         "yt",
39727         "262",
39728         1
39729       ],
39730       [
39731         "Mexico (México)",
39732         "mx",
39733         "52"
39734       ],
39735       [
39736         "Micronesia",
39737         "fm",
39738         "691"
39739       ],
39740       [
39741         "Moldova (Republica Moldova)",
39742         "md",
39743         "373"
39744       ],
39745       [
39746         "Monaco",
39747         "mc",
39748         "377"
39749       ],
39750       [
39751         "Mongolia (Монгол)",
39752         "mn",
39753         "976"
39754       ],
39755       [
39756         "Montenegro (Crna Gora)",
39757         "me",
39758         "382"
39759       ],
39760       [
39761         "Montserrat",
39762         "ms",
39763         "1664"
39764       ],
39765       [
39766         "Morocco (‫المغرب‬‎)",
39767         "ma",
39768         "212",
39769         0
39770       ],
39771       [
39772         "Mozambique (Moçambique)",
39773         "mz",
39774         "258"
39775       ],
39776       [
39777         "Myanmar (Burma) (မြန်မာ)",
39778         "mm",
39779         "95"
39780       ],
39781       [
39782         "Namibia (Namibië)",
39783         "na",
39784         "264"
39785       ],
39786       [
39787         "Nauru",
39788         "nr",
39789         "674"
39790       ],
39791       [
39792         "Nepal (नेपाल)",
39793         "np",
39794         "977"
39795       ],
39796       [
39797         "Netherlands (Nederland)",
39798         "nl",
39799         "31"
39800       ],
39801       [
39802         "New Caledonia (Nouvelle-Calédonie)",
39803         "nc",
39804         "687"
39805       ],
39806       [
39807         "New Zealand",
39808         "nz",
39809         "64"
39810       ],
39811       [
39812         "Nicaragua",
39813         "ni",
39814         "505"
39815       ],
39816       [
39817         "Niger (Nijar)",
39818         "ne",
39819         "227"
39820       ],
39821       [
39822         "Nigeria",
39823         "ng",
39824         "234"
39825       ],
39826       [
39827         "Niue",
39828         "nu",
39829         "683"
39830       ],
39831       [
39832         "Norfolk Island",
39833         "nf",
39834         "672"
39835       ],
39836       [
39837         "North Korea (조선 민주주의 인민 공화국)",
39838         "kp",
39839         "850"
39840       ],
39841       [
39842         "Northern Mariana Islands",
39843         "mp",
39844         "1670"
39845       ],
39846       [
39847         "Norway (Norge)",
39848         "no",
39849         "47",
39850         0
39851       ],
39852       [
39853         "Oman (‫عُمان‬‎)",
39854         "om",
39855         "968"
39856       ],
39857       [
39858         "Pakistan (‫پاکستان‬‎)",
39859         "pk",
39860         "92"
39861       ],
39862       [
39863         "Palau",
39864         "pw",
39865         "680"
39866       ],
39867       [
39868         "Palestine (‫فلسطين‬‎)",
39869         "ps",
39870         "970"
39871       ],
39872       [
39873         "Panama (Panamá)",
39874         "pa",
39875         "507"
39876       ],
39877       [
39878         "Papua New Guinea",
39879         "pg",
39880         "675"
39881       ],
39882       [
39883         "Paraguay",
39884         "py",
39885         "595"
39886       ],
39887       [
39888         "Peru (Perú)",
39889         "pe",
39890         "51"
39891       ],
39892       [
39893         "Philippines",
39894         "ph",
39895         "63"
39896       ],
39897       [
39898         "Poland (Polska)",
39899         "pl",
39900         "48"
39901       ],
39902       [
39903         "Portugal",
39904         "pt",
39905         "351"
39906       ],
39907       [
39908         "Puerto Rico",
39909         "pr",
39910         "1",
39911         3,
39912         ["787", "939"]
39913       ],
39914       [
39915         "Qatar (‫قطر‬‎)",
39916         "qa",
39917         "974"
39918       ],
39919       [
39920         "Réunion (La Réunion)",
39921         "re",
39922         "262",
39923         0
39924       ],
39925       [
39926         "Romania (România)",
39927         "ro",
39928         "40"
39929       ],
39930       [
39931         "Russia (Россия)",
39932         "ru",
39933         "7",
39934         0
39935       ],
39936       [
39937         "Rwanda",
39938         "rw",
39939         "250"
39940       ],
39941       [
39942         "Saint Barthélemy",
39943         "bl",
39944         "590",
39945         1
39946       ],
39947       [
39948         "Saint Helena",
39949         "sh",
39950         "290"
39951       ],
39952       [
39953         "Saint Kitts and Nevis",
39954         "kn",
39955         "1869"
39956       ],
39957       [
39958         "Saint Lucia",
39959         "lc",
39960         "1758"
39961       ],
39962       [
39963         "Saint Martin (Saint-Martin (partie française))",
39964         "mf",
39965         "590",
39966         2
39967       ],
39968       [
39969         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39970         "pm",
39971         "508"
39972       ],
39973       [
39974         "Saint Vincent and the Grenadines",
39975         "vc",
39976         "1784"
39977       ],
39978       [
39979         "Samoa",
39980         "ws",
39981         "685"
39982       ],
39983       [
39984         "San Marino",
39985         "sm",
39986         "378"
39987       ],
39988       [
39989         "São Tomé and Príncipe (São Tomé e Príncipe)",
39990         "st",
39991         "239"
39992       ],
39993       [
39994         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39995         "sa",
39996         "966"
39997       ],
39998       [
39999         "Senegal (Sénégal)",
40000         "sn",
40001         "221"
40002       ],
40003       [
40004         "Serbia (Србија)",
40005         "rs",
40006         "381"
40007       ],
40008       [
40009         "Seychelles",
40010         "sc",
40011         "248"
40012       ],
40013       [
40014         "Sierra Leone",
40015         "sl",
40016         "232"
40017       ],
40018       [
40019         "Singapore",
40020         "sg",
40021         "65"
40022       ],
40023       [
40024         "Sint Maarten",
40025         "sx",
40026         "1721"
40027       ],
40028       [
40029         "Slovakia (Slovensko)",
40030         "sk",
40031         "421"
40032       ],
40033       [
40034         "Slovenia (Slovenija)",
40035         "si",
40036         "386"
40037       ],
40038       [
40039         "Solomon Islands",
40040         "sb",
40041         "677"
40042       ],
40043       [
40044         "Somalia (Soomaaliya)",
40045         "so",
40046         "252"
40047       ],
40048       [
40049         "South Africa",
40050         "za",
40051         "27"
40052       ],
40053       [
40054         "South Korea (대한민국)",
40055         "kr",
40056         "82"
40057       ],
40058       [
40059         "South Sudan (‫جنوب السودان‬‎)",
40060         "ss",
40061         "211"
40062       ],
40063       [
40064         "Spain (España)",
40065         "es",
40066         "34"
40067       ],
40068       [
40069         "Sri Lanka (ශ්‍රී ලංකාව)",
40070         "lk",
40071         "94"
40072       ],
40073       [
40074         "Sudan (‫السودان‬‎)",
40075         "sd",
40076         "249"
40077       ],
40078       [
40079         "Suriname",
40080         "sr",
40081         "597"
40082       ],
40083       [
40084         "Svalbard and Jan Mayen",
40085         "sj",
40086         "47",
40087         1
40088       ],
40089       [
40090         "Swaziland",
40091         "sz",
40092         "268"
40093       ],
40094       [
40095         "Sweden (Sverige)",
40096         "se",
40097         "46"
40098       ],
40099       [
40100         "Switzerland (Schweiz)",
40101         "ch",
40102         "41"
40103       ],
40104       [
40105         "Syria (‫سوريا‬‎)",
40106         "sy",
40107         "963"
40108       ],
40109       [
40110         "Taiwan (台灣)",
40111         "tw",
40112         "886"
40113       ],
40114       [
40115         "Tajikistan",
40116         "tj",
40117         "992"
40118       ],
40119       [
40120         "Tanzania",
40121         "tz",
40122         "255"
40123       ],
40124       [
40125         "Thailand (ไทย)",
40126         "th",
40127         "66"
40128       ],
40129       [
40130         "Timor-Leste",
40131         "tl",
40132         "670"
40133       ],
40134       [
40135         "Togo",
40136         "tg",
40137         "228"
40138       ],
40139       [
40140         "Tokelau",
40141         "tk",
40142         "690"
40143       ],
40144       [
40145         "Tonga",
40146         "to",
40147         "676"
40148       ],
40149       [
40150         "Trinidad and Tobago",
40151         "tt",
40152         "1868"
40153       ],
40154       [
40155         "Tunisia (‫تونس‬‎)",
40156         "tn",
40157         "216"
40158       ],
40159       [
40160         "Turkey (Türkiye)",
40161         "tr",
40162         "90"
40163       ],
40164       [
40165         "Turkmenistan",
40166         "tm",
40167         "993"
40168       ],
40169       [
40170         "Turks and Caicos Islands",
40171         "tc",
40172         "1649"
40173       ],
40174       [
40175         "Tuvalu",
40176         "tv",
40177         "688"
40178       ],
40179       [
40180         "U.S. Virgin Islands",
40181         "vi",
40182         "1340"
40183       ],
40184       [
40185         "Uganda",
40186         "ug",
40187         "256"
40188       ],
40189       [
40190         "Ukraine (Україна)",
40191         "ua",
40192         "380"
40193       ],
40194       [
40195         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
40196         "ae",
40197         "971"
40198       ],
40199       [
40200         "United Kingdom",
40201         "gb",
40202         "44",
40203         0
40204       ],
40205       [
40206         "United States",
40207         "us",
40208         "1",
40209         0
40210       ],
40211       [
40212         "Uruguay",
40213         "uy",
40214         "598"
40215       ],
40216       [
40217         "Uzbekistan (Oʻzbekiston)",
40218         "uz",
40219         "998"
40220       ],
40221       [
40222         "Vanuatu",
40223         "vu",
40224         "678"
40225       ],
40226       [
40227         "Vatican City (Città del Vaticano)",
40228         "va",
40229         "39",
40230         1
40231       ],
40232       [
40233         "Venezuela",
40234         "ve",
40235         "58"
40236       ],
40237       [
40238         "Vietnam (Việt Nam)",
40239         "vn",
40240         "84"
40241       ],
40242       [
40243         "Wallis and Futuna (Wallis-et-Futuna)",
40244         "wf",
40245         "681"
40246       ],
40247       [
40248         "Western Sahara (‫الصحراء الغربية‬‎)",
40249         "eh",
40250         "212",
40251         1
40252       ],
40253       [
40254         "Yemen (‫اليمن‬‎)",
40255         "ye",
40256         "967"
40257       ],
40258       [
40259         "Zambia",
40260         "zm",
40261         "260"
40262       ],
40263       [
40264         "Zimbabwe",
40265         "zw",
40266         "263"
40267       ],
40268       [
40269         "Åland Islands",
40270         "ax",
40271         "358",
40272         1
40273       ]
40274   ];
40275   
40276   return d;
40277 }/**
40278 *    This script refer to:
40279 *    Title: International Telephone Input
40280 *    Author: Jack O'Connor
40281 *    Code version:  v12.1.12
40282 *    Availability: https://github.com/jackocnr/intl-tel-input.git
40283 **/
40284
40285 /**
40286  * @class Roo.bootstrap.PhoneInput
40287  * @extends Roo.bootstrap.TriggerField
40288  * An input with International dial-code selection
40289  
40290  * @cfg {String} defaultDialCode default '+852'
40291  * @cfg {Array} preferedCountries default []
40292   
40293  * @constructor
40294  * Create a new PhoneInput.
40295  * @param {Object} config Configuration options
40296  */
40297
40298 Roo.bootstrap.PhoneInput = function(config) {
40299     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
40300 };
40301
40302 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
40303         
40304         listWidth: undefined,
40305         
40306         selectedClass: 'active',
40307         
40308         invalidClass : "has-warning",
40309         
40310         validClass: 'has-success',
40311         
40312         allowed: '0123456789',
40313         
40314         max_length: 15,
40315         
40316         /**
40317          * @cfg {String} defaultDialCode The default dial code when initializing the input
40318          */
40319         defaultDialCode: '+852',
40320         
40321         /**
40322          * @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
40323          */
40324         preferedCountries: false,
40325         
40326         getAutoCreate : function()
40327         {
40328             var data = Roo.bootstrap.PhoneInputData();
40329             var align = this.labelAlign || this.parentLabelAlign();
40330             var id = Roo.id();
40331             
40332             this.allCountries = [];
40333             this.dialCodeMapping = [];
40334             
40335             for (var i = 0; i < data.length; i++) {
40336               var c = data[i];
40337               this.allCountries[i] = {
40338                 name: c[0],
40339                 iso2: c[1],
40340                 dialCode: c[2],
40341                 priority: c[3] || 0,
40342                 areaCodes: c[4] || null
40343               };
40344               this.dialCodeMapping[c[2]] = {
40345                   name: c[0],
40346                   iso2: c[1],
40347                   priority: c[3] || 0,
40348                   areaCodes: c[4] || null
40349               };
40350             }
40351             
40352             var cfg = {
40353                 cls: 'form-group',
40354                 cn: []
40355             };
40356             
40357             var input =  {
40358                 tag: 'input',
40359                 id : id,
40360                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40361                 maxlength: this.max_length,
40362                 cls : 'form-control tel-input',
40363                 autocomplete: 'new-password'
40364             };
40365             
40366             var hiddenInput = {
40367                 tag: 'input',
40368                 type: 'hidden',
40369                 cls: 'hidden-tel-input'
40370             };
40371             
40372             if (this.name) {
40373                 hiddenInput.name = this.name;
40374             }
40375             
40376             if (this.disabled) {
40377                 input.disabled = true;
40378             }
40379             
40380             var flag_container = {
40381                 tag: 'div',
40382                 cls: 'flag-box',
40383                 cn: [
40384                     {
40385                         tag: 'div',
40386                         cls: 'flag'
40387                     },
40388                     {
40389                         tag: 'div',
40390                         cls: 'caret'
40391                     }
40392                 ]
40393             };
40394             
40395             var box = {
40396                 tag: 'div',
40397                 cls: this.hasFeedback ? 'has-feedback' : '',
40398                 cn: [
40399                     hiddenInput,
40400                     input,
40401                     {
40402                         tag: 'input',
40403                         cls: 'dial-code-holder',
40404                         disabled: true
40405                     }
40406                 ]
40407             };
40408             
40409             var container = {
40410                 cls: 'roo-select2-container input-group',
40411                 cn: [
40412                     flag_container,
40413                     box
40414                 ]
40415             };
40416             
40417             if (this.fieldLabel.length) {
40418                 var indicator = {
40419                     tag: 'i',
40420                     tooltip: 'This field is required'
40421                 };
40422                 
40423                 var label = {
40424                     tag: 'label',
40425                     'for':  id,
40426                     cls: 'control-label',
40427                     cn: []
40428                 };
40429                 
40430                 var label_text = {
40431                     tag: 'span',
40432                     html: this.fieldLabel
40433                 };
40434                 
40435                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40436                 label.cn = [
40437                     indicator,
40438                     label_text
40439                 ];
40440                 
40441                 if(this.indicatorpos == 'right') {
40442                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40443                     label.cn = [
40444                         label_text,
40445                         indicator
40446                     ];
40447                 }
40448                 
40449                 if(align == 'left') {
40450                     container = {
40451                         tag: 'div',
40452                         cn: [
40453                             container
40454                         ]
40455                     };
40456                     
40457                     if(this.labelWidth > 12){
40458                         label.style = "width: " + this.labelWidth + 'px';
40459                     }
40460                     if(this.labelWidth < 13 && this.labelmd == 0){
40461                         this.labelmd = this.labelWidth;
40462                     }
40463                     if(this.labellg > 0){
40464                         label.cls += ' col-lg-' + this.labellg;
40465                         input.cls += ' col-lg-' + (12 - this.labellg);
40466                     }
40467                     if(this.labelmd > 0){
40468                         label.cls += ' col-md-' + this.labelmd;
40469                         container.cls += ' col-md-' + (12 - this.labelmd);
40470                     }
40471                     if(this.labelsm > 0){
40472                         label.cls += ' col-sm-' + this.labelsm;
40473                         container.cls += ' col-sm-' + (12 - this.labelsm);
40474                     }
40475                     if(this.labelxs > 0){
40476                         label.cls += ' col-xs-' + this.labelxs;
40477                         container.cls += ' col-xs-' + (12 - this.labelxs);
40478                     }
40479                 }
40480             }
40481             
40482             cfg.cn = [
40483                 label,
40484                 container
40485             ];
40486             
40487             var settings = this;
40488             
40489             ['xs','sm','md','lg'].map(function(size){
40490                 if (settings[size]) {
40491                     cfg.cls += ' col-' + size + '-' + settings[size];
40492                 }
40493             });
40494             
40495             this.store = new Roo.data.Store({
40496                 proxy : new Roo.data.MemoryProxy({}),
40497                 reader : new Roo.data.JsonReader({
40498                     fields : [
40499                         {
40500                             'name' : 'name',
40501                             'type' : 'string'
40502                         },
40503                         {
40504                             'name' : 'iso2',
40505                             'type' : 'string'
40506                         },
40507                         {
40508                             'name' : 'dialCode',
40509                             'type' : 'string'
40510                         },
40511                         {
40512                             'name' : 'priority',
40513                             'type' : 'string'
40514                         },
40515                         {
40516                             'name' : 'areaCodes',
40517                             'type' : 'string'
40518                         }
40519                     ]
40520                 })
40521             });
40522             
40523             if(!this.preferedCountries) {
40524                 this.preferedCountries = [
40525                     'hk',
40526                     'gb',
40527                     'us'
40528                 ];
40529             }
40530             
40531             var p = this.preferedCountries.reverse();
40532             
40533             if(p) {
40534                 for (var i = 0; i < p.length; i++) {
40535                     for (var j = 0; j < this.allCountries.length; j++) {
40536                         if(this.allCountries[j].iso2 == p[i]) {
40537                             var t = this.allCountries[j];
40538                             this.allCountries.splice(j,1);
40539                             this.allCountries.unshift(t);
40540                         }
40541                     } 
40542                 }
40543             }
40544             
40545             this.store.proxy.data = {
40546                 success: true,
40547                 data: this.allCountries
40548             };
40549             
40550             return cfg;
40551         },
40552         
40553         initEvents : function()
40554         {
40555             this.createList();
40556             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40557             
40558             this.indicator = this.indicatorEl();
40559             this.flag = this.flagEl();
40560             this.dialCodeHolder = this.dialCodeHolderEl();
40561             
40562             this.trigger = this.el.select('div.flag-box',true).first();
40563             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40564             
40565             var _this = this;
40566             
40567             (function(){
40568                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40569                 _this.list.setWidth(lw);
40570             }).defer(100);
40571             
40572             this.list.on('mouseover', this.onViewOver, this);
40573             this.list.on('mousemove', this.onViewMove, this);
40574             this.inputEl().on("keyup", this.onKeyUp, this);
40575             this.inputEl().on("keypress", this.onKeyPress, this);
40576             
40577             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40578
40579             this.view = new Roo.View(this.list, this.tpl, {
40580                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40581             });
40582             
40583             this.view.on('click', this.onViewClick, this);
40584             this.setValue(this.defaultDialCode);
40585         },
40586         
40587         onTriggerClick : function(e)
40588         {
40589             Roo.log('trigger click');
40590             if(this.disabled){
40591                 return;
40592             }
40593             
40594             if(this.isExpanded()){
40595                 this.collapse();
40596                 this.hasFocus = false;
40597             }else {
40598                 this.store.load({});
40599                 this.hasFocus = true;
40600                 this.expand();
40601             }
40602         },
40603         
40604         isExpanded : function()
40605         {
40606             return this.list.isVisible();
40607         },
40608         
40609         collapse : function()
40610         {
40611             if(!this.isExpanded()){
40612                 return;
40613             }
40614             this.list.hide();
40615             Roo.get(document).un('mousedown', this.collapseIf, this);
40616             Roo.get(document).un('mousewheel', this.collapseIf, this);
40617             this.fireEvent('collapse', this);
40618             this.validate();
40619         },
40620         
40621         expand : function()
40622         {
40623             Roo.log('expand');
40624
40625             if(this.isExpanded() || !this.hasFocus){
40626                 return;
40627             }
40628             
40629             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40630             this.list.setWidth(lw);
40631             
40632             this.list.show();
40633             this.restrictHeight();
40634             
40635             Roo.get(document).on('mousedown', this.collapseIf, this);
40636             Roo.get(document).on('mousewheel', this.collapseIf, this);
40637             
40638             this.fireEvent('expand', this);
40639         },
40640         
40641         restrictHeight : function()
40642         {
40643             this.list.alignTo(this.inputEl(), this.listAlign);
40644             this.list.alignTo(this.inputEl(), this.listAlign);
40645         },
40646         
40647         onViewOver : function(e, t)
40648         {
40649             if(this.inKeyMode){
40650                 return;
40651             }
40652             var item = this.view.findItemFromChild(t);
40653             
40654             if(item){
40655                 var index = this.view.indexOf(item);
40656                 this.select(index, false);
40657             }
40658         },
40659
40660         // private
40661         onViewClick : function(view, doFocus, el, e)
40662         {
40663             var index = this.view.getSelectedIndexes()[0];
40664             
40665             var r = this.store.getAt(index);
40666             
40667             if(r){
40668                 this.onSelect(r, index);
40669             }
40670             if(doFocus !== false && !this.blockFocus){
40671                 this.inputEl().focus();
40672             }
40673         },
40674         
40675         onViewMove : function(e, t)
40676         {
40677             this.inKeyMode = false;
40678         },
40679         
40680         select : function(index, scrollIntoView)
40681         {
40682             this.selectedIndex = index;
40683             this.view.select(index);
40684             if(scrollIntoView !== false){
40685                 var el = this.view.getNode(index);
40686                 if(el){
40687                     this.list.scrollChildIntoView(el, false);
40688                 }
40689             }
40690         },
40691         
40692         createList : function()
40693         {
40694             this.list = Roo.get(document.body).createChild({
40695                 tag: 'ul',
40696                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40697                 style: 'display:none'
40698             });
40699             
40700             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40701         },
40702         
40703         collapseIf : function(e)
40704         {
40705             var in_combo  = e.within(this.el);
40706             var in_list =  e.within(this.list);
40707             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40708             
40709             if (in_combo || in_list || is_list) {
40710                 return;
40711             }
40712             this.collapse();
40713         },
40714         
40715         onSelect : function(record, index)
40716         {
40717             if(this.fireEvent('beforeselect', this, record, index) !== false){
40718                 
40719                 this.setFlagClass(record.data.iso2);
40720                 this.setDialCode(record.data.dialCode);
40721                 this.hasFocus = false;
40722                 this.collapse();
40723                 this.fireEvent('select', this, record, index);
40724             }
40725         },
40726         
40727         flagEl : function()
40728         {
40729             var flag = this.el.select('div.flag',true).first();
40730             if(!flag){
40731                 return false;
40732             }
40733             return flag;
40734         },
40735         
40736         dialCodeHolderEl : function()
40737         {
40738             var d = this.el.select('input.dial-code-holder',true).first();
40739             if(!d){
40740                 return false;
40741             }
40742             return d;
40743         },
40744         
40745         setDialCode : function(v)
40746         {
40747             this.dialCodeHolder.dom.value = '+'+v;
40748         },
40749         
40750         setFlagClass : function(n)
40751         {
40752             this.flag.dom.className = 'flag '+n;
40753         },
40754         
40755         getValue : function()
40756         {
40757             var v = this.inputEl().getValue();
40758             if(this.dialCodeHolder) {
40759                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40760             }
40761             return v;
40762         },
40763         
40764         setValue : function(v)
40765         {
40766             var d = this.getDialCode(v);
40767             
40768             //invalid dial code
40769             if(v.length == 0 || !d || d.length == 0) {
40770                 if(this.rendered){
40771                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40772                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40773                 }
40774                 return;
40775             }
40776             
40777             //valid dial code
40778             this.setFlagClass(this.dialCodeMapping[d].iso2);
40779             this.setDialCode(d);
40780             this.inputEl().dom.value = v.replace('+'+d,'');
40781             this.hiddenEl().dom.value = this.getValue();
40782             
40783             this.validate();
40784         },
40785         
40786         getDialCode : function(v)
40787         {
40788             v = v ||  '';
40789             
40790             if (v.length == 0) {
40791                 return this.dialCodeHolder.dom.value;
40792             }
40793             
40794             var dialCode = "";
40795             if (v.charAt(0) != "+") {
40796                 return false;
40797             }
40798             var numericChars = "";
40799             for (var i = 1; i < v.length; i++) {
40800               var c = v.charAt(i);
40801               if (!isNaN(c)) {
40802                 numericChars += c;
40803                 if (this.dialCodeMapping[numericChars]) {
40804                   dialCode = v.substr(1, i);
40805                 }
40806                 if (numericChars.length == 4) {
40807                   break;
40808                 }
40809               }
40810             }
40811             return dialCode;
40812         },
40813         
40814         reset : function()
40815         {
40816             this.setValue(this.defaultDialCode);
40817             this.validate();
40818         },
40819         
40820         hiddenEl : function()
40821         {
40822             return this.el.select('input.hidden-tel-input',true).first();
40823         },
40824         
40825         // after setting val
40826         onKeyUp : function(e){
40827             this.setValue(this.getValue());
40828         },
40829         
40830         onKeyPress : function(e){
40831             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40832                 e.stopEvent();
40833             }
40834         }
40835         
40836 });
40837 /**
40838  * @class Roo.bootstrap.MoneyField
40839  * @extends Roo.bootstrap.ComboBox
40840  * Bootstrap MoneyField class
40841  * 
40842  * @constructor
40843  * Create a new MoneyField.
40844  * @param {Object} config Configuration options
40845  */
40846
40847 Roo.bootstrap.MoneyField = function(config) {
40848     
40849     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40850     
40851 };
40852
40853 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40854     
40855     /**
40856      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40857      */
40858     allowDecimals : true,
40859     /**
40860      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40861      */
40862     decimalSeparator : ".",
40863     /**
40864      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40865      */
40866     decimalPrecision : 0,
40867     /**
40868      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40869      */
40870     allowNegative : true,
40871     /**
40872      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40873      */
40874     allowZero: true,
40875     /**
40876      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40877      */
40878     minValue : Number.NEGATIVE_INFINITY,
40879     /**
40880      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40881      */
40882     maxValue : Number.MAX_VALUE,
40883     /**
40884      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40885      */
40886     minText : "The minimum value for this field is {0}",
40887     /**
40888      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40889      */
40890     maxText : "The maximum value for this field is {0}",
40891     /**
40892      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40893      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40894      */
40895     nanText : "{0} is not a valid number",
40896     /**
40897      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40898      */
40899     castInt : true,
40900     /**
40901      * @cfg {String} defaults currency of the MoneyField
40902      * value should be in lkey
40903      */
40904     defaultCurrency : false,
40905     /**
40906      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40907      */
40908     thousandsDelimiter : false,
40909     /**
40910      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40911      */
40912     max_length: false,
40913     
40914     inputlg : 9,
40915     inputmd : 9,
40916     inputsm : 9,
40917     inputxs : 6,
40918     
40919     store : false,
40920     
40921     getAutoCreate : function()
40922     {
40923         var align = this.labelAlign || this.parentLabelAlign();
40924         
40925         var id = Roo.id();
40926
40927         var cfg = {
40928             cls: 'form-group',
40929             cn: []
40930         };
40931
40932         var input =  {
40933             tag: 'input',
40934             id : id,
40935             cls : 'form-control roo-money-amount-input',
40936             autocomplete: 'new-password'
40937         };
40938         
40939         var hiddenInput = {
40940             tag: 'input',
40941             type: 'hidden',
40942             id: Roo.id(),
40943             cls: 'hidden-number-input'
40944         };
40945         
40946         if(this.max_length) {
40947             input.maxlength = this.max_length; 
40948         }
40949         
40950         if (this.name) {
40951             hiddenInput.name = this.name;
40952         }
40953
40954         if (this.disabled) {
40955             input.disabled = true;
40956         }
40957
40958         var clg = 12 - this.inputlg;
40959         var cmd = 12 - this.inputmd;
40960         var csm = 12 - this.inputsm;
40961         var cxs = 12 - this.inputxs;
40962         
40963         var container = {
40964             tag : 'div',
40965             cls : 'row roo-money-field',
40966             cn : [
40967                 {
40968                     tag : 'div',
40969                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40970                     cn : [
40971                         {
40972                             tag : 'div',
40973                             cls: 'roo-select2-container input-group',
40974                             cn: [
40975                                 {
40976                                     tag : 'input',
40977                                     cls : 'form-control roo-money-currency-input',
40978                                     autocomplete: 'new-password',
40979                                     readOnly : 1,
40980                                     name : this.currencyName
40981                                 },
40982                                 {
40983                                     tag :'span',
40984                                     cls : 'input-group-addon',
40985                                     cn : [
40986                                         {
40987                                             tag: 'span',
40988                                             cls: 'caret'
40989                                         }
40990                                     ]
40991                                 }
40992                             ]
40993                         }
40994                     ]
40995                 },
40996                 {
40997                     tag : 'div',
40998                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40999                     cn : [
41000                         {
41001                             tag: 'div',
41002                             cls: this.hasFeedback ? 'has-feedback' : '',
41003                             cn: [
41004                                 input
41005                             ]
41006                         }
41007                     ]
41008                 }
41009             ]
41010             
41011         };
41012         
41013         if (this.fieldLabel.length) {
41014             var indicator = {
41015                 tag: 'i',
41016                 tooltip: 'This field is required'
41017             };
41018
41019             var label = {
41020                 tag: 'label',
41021                 'for':  id,
41022                 cls: 'control-label',
41023                 cn: []
41024             };
41025
41026             var label_text = {
41027                 tag: 'span',
41028                 html: this.fieldLabel
41029             };
41030
41031             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
41032             label.cn = [
41033                 indicator,
41034                 label_text
41035             ];
41036
41037             if(this.indicatorpos == 'right') {
41038                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
41039                 label.cn = [
41040                     label_text,
41041                     indicator
41042                 ];
41043             }
41044
41045             if(align == 'left') {
41046                 container = {
41047                     tag: 'div',
41048                     cn: [
41049                         container
41050                     ]
41051                 };
41052
41053                 if(this.labelWidth > 12){
41054                     label.style = "width: " + this.labelWidth + 'px';
41055                 }
41056                 if(this.labelWidth < 13 && this.labelmd == 0){
41057                     this.labelmd = this.labelWidth;
41058                 }
41059                 if(this.labellg > 0){
41060                     label.cls += ' col-lg-' + this.labellg;
41061                     input.cls += ' col-lg-' + (12 - this.labellg);
41062                 }
41063                 if(this.labelmd > 0){
41064                     label.cls += ' col-md-' + this.labelmd;
41065                     container.cls += ' col-md-' + (12 - this.labelmd);
41066                 }
41067                 if(this.labelsm > 0){
41068                     label.cls += ' col-sm-' + this.labelsm;
41069                     container.cls += ' col-sm-' + (12 - this.labelsm);
41070                 }
41071                 if(this.labelxs > 0){
41072                     label.cls += ' col-xs-' + this.labelxs;
41073                     container.cls += ' col-xs-' + (12 - this.labelxs);
41074                 }
41075             }
41076         }
41077
41078         cfg.cn = [
41079             label,
41080             container,
41081             hiddenInput
41082         ];
41083         
41084         var settings = this;
41085
41086         ['xs','sm','md','lg'].map(function(size){
41087             if (settings[size]) {
41088                 cfg.cls += ' col-' + size + '-' + settings[size];
41089             }
41090         });
41091         
41092         return cfg;
41093     },
41094     
41095     initEvents : function()
41096     {
41097         this.indicator = this.indicatorEl();
41098         
41099         this.initCurrencyEvent();
41100         
41101         this.initNumberEvent();
41102     },
41103     
41104     initCurrencyEvent : function()
41105     {
41106         if (!this.store) {
41107             throw "can not find store for combo";
41108         }
41109         
41110         this.store = Roo.factory(this.store, Roo.data);
41111         this.store.parent = this;
41112         
41113         this.createList();
41114         
41115         this.triggerEl = this.el.select('.input-group-addon', true).first();
41116         
41117         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
41118         
41119         var _this = this;
41120         
41121         (function(){
41122             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
41123             _this.list.setWidth(lw);
41124         }).defer(100);
41125         
41126         this.list.on('mouseover', this.onViewOver, this);
41127         this.list.on('mousemove', this.onViewMove, this);
41128         this.list.on('scroll', this.onViewScroll, this);
41129         
41130         if(!this.tpl){
41131             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
41132         }
41133         
41134         this.view = new Roo.View(this.list, this.tpl, {
41135             singleSelect:true, store: this.store, selectedClass: this.selectedClass
41136         });
41137         
41138         this.view.on('click', this.onViewClick, this);
41139         
41140         this.store.on('beforeload', this.onBeforeLoad, this);
41141         this.store.on('load', this.onLoad, this);
41142         this.store.on('loadexception', this.onLoadException, this);
41143         
41144         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
41145             "up" : function(e){
41146                 this.inKeyMode = true;
41147                 this.selectPrev();
41148             },
41149
41150             "down" : function(e){
41151                 if(!this.isExpanded()){
41152                     this.onTriggerClick();
41153                 }else{
41154                     this.inKeyMode = true;
41155                     this.selectNext();
41156                 }
41157             },
41158
41159             "enter" : function(e){
41160                 this.collapse();
41161                 
41162                 if(this.fireEvent("specialkey", this, e)){
41163                     this.onViewClick(false);
41164                 }
41165                 
41166                 return true;
41167             },
41168
41169             "esc" : function(e){
41170                 this.collapse();
41171             },
41172
41173             "tab" : function(e){
41174                 this.collapse();
41175                 
41176                 if(this.fireEvent("specialkey", this, e)){
41177                     this.onViewClick(false);
41178                 }
41179                 
41180                 return true;
41181             },
41182
41183             scope : this,
41184
41185             doRelay : function(foo, bar, hname){
41186                 if(hname == 'down' || this.scope.isExpanded()){
41187                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41188                 }
41189                 return true;
41190             },
41191
41192             forceKeyDown: true
41193         });
41194         
41195         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
41196         
41197     },
41198     
41199     initNumberEvent : function(e)
41200     {
41201         this.inputEl().on("keydown" , this.fireKey,  this);
41202         this.inputEl().on("focus", this.onFocus,  this);
41203         this.inputEl().on("blur", this.onBlur,  this);
41204         
41205         this.inputEl().relayEvent('keyup', this);
41206         
41207         if(this.indicator){
41208             this.indicator.addClass('invisible');
41209         }
41210  
41211         this.originalValue = this.getValue();
41212         
41213         if(this.validationEvent == 'keyup'){
41214             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
41215             this.inputEl().on('keyup', this.filterValidation, this);
41216         }
41217         else if(this.validationEvent !== false){
41218             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
41219         }
41220         
41221         if(this.selectOnFocus){
41222             this.on("focus", this.preFocus, this);
41223             
41224         }
41225         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41226             this.inputEl().on("keypress", this.filterKeys, this);
41227         } else {
41228             this.inputEl().relayEvent('keypress', this);
41229         }
41230         
41231         var allowed = "0123456789";
41232         
41233         if(this.allowDecimals){
41234             allowed += this.decimalSeparator;
41235         }
41236         
41237         if(this.allowNegative){
41238             allowed += "-";
41239         }
41240         
41241         if(this.thousandsDelimiter) {
41242             allowed += ",";
41243         }
41244         
41245         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41246         
41247         var keyPress = function(e){
41248             
41249             var k = e.getKey();
41250             
41251             var c = e.getCharCode();
41252             
41253             if(
41254                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41255                     allowed.indexOf(String.fromCharCode(c)) === -1
41256             ){
41257                 e.stopEvent();
41258                 return;
41259             }
41260             
41261             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41262                 return;
41263             }
41264             
41265             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41266                 e.stopEvent();
41267             }
41268         };
41269         
41270         this.inputEl().on("keypress", keyPress, this);
41271         
41272     },
41273     
41274     onTriggerClick : function(e)
41275     {   
41276         if(this.disabled){
41277             return;
41278         }
41279         
41280         this.page = 0;
41281         this.loadNext = false;
41282         
41283         if(this.isExpanded()){
41284             this.collapse();
41285             return;
41286         }
41287         
41288         this.hasFocus = true;
41289         
41290         if(this.triggerAction == 'all') {
41291             this.doQuery(this.allQuery, true);
41292             return;
41293         }
41294         
41295         this.doQuery(this.getRawValue());
41296     },
41297     
41298     getCurrency : function()
41299     {   
41300         var v = this.currencyEl().getValue();
41301         
41302         return v;
41303     },
41304     
41305     restrictHeight : function()
41306     {
41307         this.list.alignTo(this.currencyEl(), this.listAlign);
41308         this.list.alignTo(this.currencyEl(), this.listAlign);
41309     },
41310     
41311     onViewClick : function(view, doFocus, el, e)
41312     {
41313         var index = this.view.getSelectedIndexes()[0];
41314         
41315         var r = this.store.getAt(index);
41316         
41317         if(r){
41318             this.onSelect(r, index);
41319         }
41320     },
41321     
41322     onSelect : function(record, index){
41323         
41324         if(this.fireEvent('beforeselect', this, record, index) !== false){
41325         
41326             this.setFromCurrencyData(index > -1 ? record.data : false);
41327             
41328             this.collapse();
41329             
41330             this.fireEvent('select', this, record, index);
41331         }
41332     },
41333     
41334     setFromCurrencyData : function(o)
41335     {
41336         var currency = '';
41337         
41338         this.lastCurrency = o;
41339         
41340         if (this.currencyField) {
41341             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41342         } else {
41343             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
41344         }
41345         
41346         this.lastSelectionText = currency;
41347         
41348         //setting default currency
41349         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41350             this.setCurrency(this.defaultCurrency);
41351             return;
41352         }
41353         
41354         this.setCurrency(currency);
41355     },
41356     
41357     setFromData : function(o)
41358     {
41359         var c = {};
41360         
41361         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41362         
41363         this.setFromCurrencyData(c);
41364         
41365         var value = '';
41366         
41367         if (this.name) {
41368             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41369         } else {
41370             Roo.log('no value set for '+ (this.name ? this.name : this.id));
41371         }
41372         
41373         this.setValue(value);
41374         
41375     },
41376     
41377     setCurrency : function(v)
41378     {   
41379         this.currencyValue = v;
41380         
41381         if(this.rendered){
41382             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41383             this.validate();
41384         }
41385     },
41386     
41387     setValue : function(v)
41388     {
41389         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41390         
41391         this.value = v;
41392         
41393         if(this.rendered){
41394             
41395             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41396             
41397             this.inputEl().dom.value = (v == '') ? '' :
41398                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41399             
41400             if(!this.allowZero && v === '0') {
41401                 this.hiddenEl().dom.value = '';
41402                 this.inputEl().dom.value = '';
41403             }
41404             
41405             this.validate();
41406         }
41407     },
41408     
41409     getRawValue : function()
41410     {
41411         var v = this.inputEl().getValue();
41412         
41413         return v;
41414     },
41415     
41416     getValue : function()
41417     {
41418         return this.fixPrecision(this.parseValue(this.getRawValue()));
41419     },
41420     
41421     parseValue : function(value)
41422     {
41423         if(this.thousandsDelimiter) {
41424             value += "";
41425             r = new RegExp(",", "g");
41426             value = value.replace(r, "");
41427         }
41428         
41429         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41430         return isNaN(value) ? '' : value;
41431         
41432     },
41433     
41434     fixPrecision : function(value)
41435     {
41436         if(this.thousandsDelimiter) {
41437             value += "";
41438             r = new RegExp(",", "g");
41439             value = value.replace(r, "");
41440         }
41441         
41442         var nan = isNaN(value);
41443         
41444         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41445             return nan ? '' : value;
41446         }
41447         return parseFloat(value).toFixed(this.decimalPrecision);
41448     },
41449     
41450     decimalPrecisionFcn : function(v)
41451     {
41452         return Math.floor(v);
41453     },
41454     
41455     validateValue : function(value)
41456     {
41457         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41458             return false;
41459         }
41460         
41461         var num = this.parseValue(value);
41462         
41463         if(isNaN(num)){
41464             this.markInvalid(String.format(this.nanText, value));
41465             return false;
41466         }
41467         
41468         if(num < this.minValue){
41469             this.markInvalid(String.format(this.minText, this.minValue));
41470             return false;
41471         }
41472         
41473         if(num > this.maxValue){
41474             this.markInvalid(String.format(this.maxText, this.maxValue));
41475             return false;
41476         }
41477         
41478         return true;
41479     },
41480     
41481     validate : function()
41482     {
41483         if(this.disabled || this.allowBlank){
41484             this.markValid();
41485             return true;
41486         }
41487         
41488         var currency = this.getCurrency();
41489         
41490         if(this.validateValue(this.getRawValue()) && currency.length){
41491             this.markValid();
41492             return true;
41493         }
41494         
41495         this.markInvalid();
41496         return false;
41497     },
41498     
41499     getName: function()
41500     {
41501         return this.name;
41502     },
41503     
41504     beforeBlur : function()
41505     {
41506         if(!this.castInt){
41507             return;
41508         }
41509         
41510         var v = this.parseValue(this.getRawValue());
41511         
41512         if(v || v == 0){
41513             this.setValue(v);
41514         }
41515     },
41516     
41517     onBlur : function()
41518     {
41519         this.beforeBlur();
41520         
41521         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41522             //this.el.removeClass(this.focusClass);
41523         }
41524         
41525         this.hasFocus = false;
41526         
41527         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41528             this.validate();
41529         }
41530         
41531         var v = this.getValue();
41532         
41533         if(String(v) !== String(this.startValue)){
41534             this.fireEvent('change', this, v, this.startValue);
41535         }
41536         
41537         this.fireEvent("blur", this);
41538     },
41539     
41540     inputEl : function()
41541     {
41542         return this.el.select('.roo-money-amount-input', true).first();
41543     },
41544     
41545     currencyEl : function()
41546     {
41547         return this.el.select('.roo-money-currency-input', true).first();
41548     },
41549     
41550     hiddenEl : function()
41551     {
41552         return this.el.select('input.hidden-number-input',true).first();
41553     }
41554     
41555 });/**
41556  * @class Roo.bootstrap.BezierSignature
41557  * @extends Roo.bootstrap.Component
41558  * Bootstrap BezierSignature class
41559  * This script refer to:
41560  *    Title: Signature Pad
41561  *    Author: szimek
41562  *    Availability: https://github.com/szimek/signature_pad
41563  *
41564  * @constructor
41565  * Create a new BezierSignature
41566  * @param {Object} config The config object
41567  */
41568
41569 Roo.bootstrap.BezierSignature = function(config){
41570     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
41571     this.addEvents({
41572         "resize" : true
41573     });
41574 };
41575
41576 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
41577 {
41578      
41579     curve_data: [],
41580     
41581     is_empty: true,
41582     
41583     mouse_btn_down: true,
41584     
41585     /**
41586      * @cfg {int} canvas height
41587      */
41588     canvas_height: '200px',
41589     
41590     /**
41591      * @cfg {float|function} Radius of a single dot.
41592      */ 
41593     dot_size: false,
41594     
41595     /**
41596      * @cfg {float} Minimum width of a line. Defaults to 0.5.
41597      */
41598     min_width: 0.5,
41599     
41600     /**
41601      * @cfg {float} Maximum width of a line. Defaults to 2.5.
41602      */
41603     max_width: 2.5,
41604     
41605     /**
41606      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
41607      */
41608     throttle: 16,
41609     
41610     /**
41611      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
41612      */
41613     min_distance: 5,
41614     
41615     /**
41616      * @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.
41617      */
41618     bg_color: 'rgba(0, 0, 0, 0)',
41619     
41620     /**
41621      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
41622      */
41623     dot_color: 'black',
41624     
41625     /**
41626      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
41627      */ 
41628     velocity_filter_weight: 0.7,
41629     
41630     /**
41631      * @cfg {function} Callback when stroke begin. 
41632      */
41633     onBegin: false,
41634     
41635     /**
41636      * @cfg {function} Callback when stroke end.
41637      */
41638     onEnd: false,
41639     
41640     getAutoCreate : function()
41641     {
41642         var cls = 'roo-signature column';
41643         
41644         if(this.cls){
41645             cls += ' ' + this.cls;
41646         }
41647         
41648         var col_sizes = [
41649             'lg',
41650             'md',
41651             'sm',
41652             'xs'
41653         ];
41654         
41655         for(var i = 0; i < col_sizes.length; i++) {
41656             if(this[col_sizes[i]]) {
41657                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
41658             }
41659         }
41660         
41661         var cfg = {
41662             tag: 'div',
41663             cls: cls,
41664             cn: [
41665                 {
41666                     tag: 'div',
41667                     cls: 'roo-signature-body',
41668                     cn: [
41669                         {
41670                             tag: 'canvas',
41671                             cls: 'roo-signature-body-canvas',
41672                             height: this.canvas_height,
41673                             width: this.canvas_width
41674                         }
41675                     ]
41676                 },
41677                 {
41678                     tag: 'input',
41679                     type: 'file',
41680                     style: 'display: none'
41681                 }
41682             ]
41683         };
41684         
41685         return cfg;
41686     },
41687     
41688     initEvents: function() 
41689     {
41690         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
41691         
41692         var canvas = this.canvasEl();
41693         
41694         // mouse && touch event swapping...
41695         canvas.dom.style.touchAction = 'none';
41696         canvas.dom.style.msTouchAction = 'none';
41697         
41698         this.mouse_btn_down = false;
41699         canvas.on('mousedown', this._handleMouseDown, this);
41700         canvas.on('mousemove', this._handleMouseMove, this);
41701         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
41702         
41703         if (window.PointerEvent) {
41704             canvas.on('pointerdown', this._handleMouseDown, this);
41705             canvas.on('pointermove', this._handleMouseMove, this);
41706             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
41707         }
41708         
41709         if ('ontouchstart' in window) {
41710             canvas.on('touchstart', this._handleTouchStart, this);
41711             canvas.on('touchmove', this._handleTouchMove, this);
41712             canvas.on('touchend', this._handleTouchEnd, this);
41713         }
41714         
41715         Roo.EventManager.onWindowResize(this.resize, this, true);
41716         
41717         // file input event
41718         this.fileEl().on('change', this.uploadImage, this);
41719         
41720         this.clear();
41721         
41722         this.resize();
41723     },
41724     
41725     resize: function(){
41726         
41727         var canvas = this.canvasEl().dom;
41728         var ctx = this.canvasElCtx();
41729         var img_data = false;
41730         
41731         if(canvas.width > 0) {
41732             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
41733         }
41734         // setting canvas width will clean img data
41735         canvas.width = 0;
41736         
41737         var style = window.getComputedStyle ? 
41738             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
41739             
41740         var padding_left = parseInt(style.paddingLeft) || 0;
41741         var padding_right = parseInt(style.paddingRight) || 0;
41742         
41743         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
41744         
41745         if(img_data) {
41746             ctx.putImageData(img_data, 0, 0);
41747         }
41748     },
41749     
41750     _handleMouseDown: function(e)
41751     {
41752         if (e.browserEvent.which === 1) {
41753             this.mouse_btn_down = true;
41754             this.strokeBegin(e);
41755         }
41756     },
41757     
41758     _handleMouseMove: function (e)
41759     {
41760         if (this.mouse_btn_down) {
41761             this.strokeMoveUpdate(e);
41762         }
41763     },
41764     
41765     _handleMouseUp: function (e)
41766     {
41767         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
41768             this.mouse_btn_down = false;
41769             this.strokeEnd(e);
41770         }
41771     },
41772     
41773     _handleTouchStart: function (e) {
41774         
41775         e.preventDefault();
41776         if (e.browserEvent.targetTouches.length === 1) {
41777             // var touch = e.browserEvent.changedTouches[0];
41778             // this.strokeBegin(touch);
41779             
41780              this.strokeBegin(e); // assume e catching the correct xy...
41781         }
41782     },
41783     
41784     _handleTouchMove: function (e) {
41785         e.preventDefault();
41786         // var touch = event.targetTouches[0];
41787         // _this._strokeMoveUpdate(touch);
41788         this.strokeMoveUpdate(e);
41789     },
41790     
41791     _handleTouchEnd: function (e) {
41792         var wasCanvasTouched = e.target === this.canvasEl().dom;
41793         if (wasCanvasTouched) {
41794             e.preventDefault();
41795             // var touch = event.changedTouches[0];
41796             // _this._strokeEnd(touch);
41797             this.strokeEnd(e);
41798         }
41799     },
41800     
41801     reset: function () {
41802         this._lastPoints = [];
41803         this._lastVelocity = 0;
41804         this._lastWidth = (this.min_width + this.max_width) / 2;
41805         this.canvasElCtx().fillStyle = this.dot_color;
41806     },
41807     
41808     strokeMoveUpdate: function(e)
41809     {
41810         this.strokeUpdate(e);
41811         
41812         if (this.throttle) {
41813             this.throttleStroke(this.strokeUpdate, this.throttle);
41814         }
41815         else {
41816             this.strokeUpdate(e);
41817         }
41818     },
41819     
41820     strokeBegin: function(e)
41821     {
41822         var newPointGroup = {
41823             color: this.dot_color,
41824             points: []
41825         };
41826         
41827         if (typeof this.onBegin === 'function') {
41828             this.onBegin(e);
41829         }
41830         
41831         this.curve_data.push(newPointGroup);
41832         this.reset();
41833         this.strokeUpdate(e);
41834     },
41835     
41836     strokeUpdate: function(e)
41837     {
41838         var rect = this.canvasEl().dom.getBoundingClientRect();
41839         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
41840         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
41841         var lastPoints = lastPointGroup.points;
41842         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
41843         var isLastPointTooClose = lastPoint
41844             ? point.distanceTo(lastPoint) <= this.min_distance
41845             : false;
41846         var color = lastPointGroup.color;
41847         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
41848             var curve = this.addPoint(point);
41849             if (!lastPoint) {
41850                 this.drawDot({color: color, point: point});
41851             }
41852             else if (curve) {
41853                 this.drawCurve({color: color, curve: curve});
41854             }
41855             lastPoints.push({
41856                 time: point.time,
41857                 x: point.x,
41858                 y: point.y
41859             });
41860         }
41861     },
41862     
41863     strokeEnd: function(e)
41864     {
41865         this.strokeUpdate(e);
41866         if (typeof this.onEnd === 'function') {
41867             this.onEnd(e);
41868         }
41869     },
41870     
41871     addPoint:  function (point) {
41872         var _lastPoints = this._lastPoints;
41873         _lastPoints.push(point);
41874         if (_lastPoints.length > 2) {
41875             if (_lastPoints.length === 3) {
41876                 _lastPoints.unshift(_lastPoints[0]);
41877             }
41878             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
41879             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
41880             _lastPoints.shift();
41881             return curve;
41882         }
41883         return null;
41884     },
41885     
41886     calculateCurveWidths: function (startPoint, endPoint) {
41887         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
41888             (1 - this.velocity_filter_weight) * this._lastVelocity;
41889
41890         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
41891         var widths = {
41892             end: newWidth,
41893             start: this._lastWidth
41894         };
41895         
41896         this._lastVelocity = velocity;
41897         this._lastWidth = newWidth;
41898         return widths;
41899     },
41900     
41901     drawDot: function (_a) {
41902         var color = _a.color, point = _a.point;
41903         var ctx = this.canvasElCtx();
41904         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
41905         ctx.beginPath();
41906         this.drawCurveSegment(point.x, point.y, width);
41907         ctx.closePath();
41908         ctx.fillStyle = color;
41909         ctx.fill();
41910     },
41911     
41912     drawCurve: function (_a) {
41913         var color = _a.color, curve = _a.curve;
41914         var ctx = this.canvasElCtx();
41915         var widthDelta = curve.endWidth - curve.startWidth;
41916         var drawSteps = Math.floor(curve.length()) * 2;
41917         ctx.beginPath();
41918         ctx.fillStyle = color;
41919         for (var i = 0; i < drawSteps; i += 1) {
41920         var t = i / drawSteps;
41921         var tt = t * t;
41922         var ttt = tt * t;
41923         var u = 1 - t;
41924         var uu = u * u;
41925         var uuu = uu * u;
41926         var x = uuu * curve.startPoint.x;
41927         x += 3 * uu * t * curve.control1.x;
41928         x += 3 * u * tt * curve.control2.x;
41929         x += ttt * curve.endPoint.x;
41930         var y = uuu * curve.startPoint.y;
41931         y += 3 * uu * t * curve.control1.y;
41932         y += 3 * u * tt * curve.control2.y;
41933         y += ttt * curve.endPoint.y;
41934         var width = curve.startWidth + ttt * widthDelta;
41935         this.drawCurveSegment(x, y, width);
41936         }
41937         ctx.closePath();
41938         ctx.fill();
41939     },
41940     
41941     drawCurveSegment: function (x, y, width) {
41942         var ctx = this.canvasElCtx();
41943         ctx.moveTo(x, y);
41944         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
41945         this.is_empty = false;
41946     },
41947     
41948     clear: function()
41949     {
41950         var ctx = this.canvasElCtx();
41951         var canvas = this.canvasEl().dom;
41952         ctx.fillStyle = this.bg_color;
41953         ctx.clearRect(0, 0, canvas.width, canvas.height);
41954         ctx.fillRect(0, 0, canvas.width, canvas.height);
41955         this.curve_data = [];
41956         this.reset();
41957         this.is_empty = true;
41958     },
41959     
41960     fileEl: function()
41961     {
41962         return  this.el.select('input',true).first();
41963     },
41964     
41965     canvasEl: function()
41966     {
41967         return this.el.select('canvas',true).first();
41968     },
41969     
41970     canvasElCtx: function()
41971     {
41972         return this.el.select('canvas',true).first().dom.getContext('2d');
41973     },
41974     
41975     getImage: function(type)
41976     {
41977         if(this.is_empty) {
41978             return false;
41979         }
41980         
41981         // encryption ?
41982         return this.canvasEl().dom.toDataURL('image/'+type, 1);
41983     },
41984     
41985     drawFromImage: function(img_src)
41986     {
41987         var img = new Image();
41988         
41989         img.onload = function(){
41990             this.canvasElCtx().drawImage(img, 0, 0);
41991         }.bind(this);
41992         
41993         img.src = img_src;
41994         
41995         this.is_empty = false;
41996     },
41997     
41998     selectImage: function()
41999     {
42000         this.fileEl().dom.click();
42001     },
42002     
42003     uploadImage: function(e)
42004     {
42005         var reader = new FileReader();
42006         
42007         reader.onload = function(e){
42008             var img = new Image();
42009             img.onload = function(){
42010                 this.reset();
42011                 this.canvasElCtx().drawImage(img, 0, 0);
42012             }.bind(this);
42013             img.src = e.target.result;
42014         }.bind(this);
42015         
42016         reader.readAsDataURL(e.target.files[0]);
42017     },
42018     
42019     // Bezier Point Constructor
42020     Point: (function () {
42021         function Point(x, y, time) {
42022             this.x = x;
42023             this.y = y;
42024             this.time = time || Date.now();
42025         }
42026         Point.prototype.distanceTo = function (start) {
42027             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
42028         };
42029         Point.prototype.equals = function (other) {
42030             return this.x === other.x && this.y === other.y && this.time === other.time;
42031         };
42032         Point.prototype.velocityFrom = function (start) {
42033             return this.time !== start.time
42034             ? this.distanceTo(start) / (this.time - start.time)
42035             : 0;
42036         };
42037         return Point;
42038     }()),
42039     
42040     
42041     // Bezier Constructor
42042     Bezier: (function () {
42043         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
42044             this.startPoint = startPoint;
42045             this.control2 = control2;
42046             this.control1 = control1;
42047             this.endPoint = endPoint;
42048             this.startWidth = startWidth;
42049             this.endWidth = endWidth;
42050         }
42051         Bezier.fromPoints = function (points, widths, scope) {
42052             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
42053             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
42054             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
42055         };
42056         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
42057             var dx1 = s1.x - s2.x;
42058             var dy1 = s1.y - s2.y;
42059             var dx2 = s2.x - s3.x;
42060             var dy2 = s2.y - s3.y;
42061             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
42062             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
42063             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
42064             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
42065             var dxm = m1.x - m2.x;
42066             var dym = m1.y - m2.y;
42067             var k = l2 / (l1 + l2);
42068             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
42069             var tx = s2.x - cm.x;
42070             var ty = s2.y - cm.y;
42071             return {
42072                 c1: new scope.Point(m1.x + tx, m1.y + ty),
42073                 c2: new scope.Point(m2.x + tx, m2.y + ty)
42074             };
42075         };
42076         Bezier.prototype.length = function () {
42077             var steps = 10;
42078             var length = 0;
42079             var px;
42080             var py;
42081             for (var i = 0; i <= steps; i += 1) {
42082                 var t = i / steps;
42083                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
42084                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
42085                 if (i > 0) {
42086                     var xdiff = cx - px;
42087                     var ydiff = cy - py;
42088                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
42089                 }
42090                 px = cx;
42091                 py = cy;
42092             }
42093             return length;
42094         };
42095         Bezier.prototype.point = function (t, start, c1, c2, end) {
42096             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
42097             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
42098             + (3.0 * c2 * (1.0 - t) * t * t)
42099             + (end * t * t * t);
42100         };
42101         return Bezier;
42102     }()),
42103     
42104     throttleStroke: function(fn, wait) {
42105       if (wait === void 0) { wait = 250; }
42106       var previous = 0;
42107       var timeout = null;
42108       var result;
42109       var storedContext;
42110       var storedArgs;
42111       var later = function () {
42112           previous = Date.now();
42113           timeout = null;
42114           result = fn.apply(storedContext, storedArgs);
42115           if (!timeout) {
42116               storedContext = null;
42117               storedArgs = [];
42118           }
42119       };
42120       return function wrapper() {
42121           var args = [];
42122           for (var _i = 0; _i < arguments.length; _i++) {
42123               args[_i] = arguments[_i];
42124           }
42125           var now = Date.now();
42126           var remaining = wait - (now - previous);
42127           storedContext = this;
42128           storedArgs = args;
42129           if (remaining <= 0 || remaining > wait) {
42130               if (timeout) {
42131                   clearTimeout(timeout);
42132                   timeout = null;
42133               }
42134               previous = now;
42135               result = fn.apply(storedContext, storedArgs);
42136               if (!timeout) {
42137                   storedContext = null;
42138                   storedArgs = [];
42139               }
42140           }
42141           else if (!timeout) {
42142               timeout = window.setTimeout(later, remaining);
42143           }
42144           return result;
42145       };
42146   }
42147   
42148 });
42149
42150  
42151
42152