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             // could use BS4 hidden-..-down 
6812             
6813             if(typeof(config.lgHeader) != 'undefined'){
6814                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
6815             }
6816             
6817             if(typeof(config.mdHeader) != 'undefined'){
6818                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6819             }
6820             
6821             if(typeof(config.smHeader) != 'undefined'){
6822                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6823             }
6824             
6825             if(typeof(config.xsHeader) != 'undefined'){
6826                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6827             }
6828             
6829             if(hh.length){
6830                 c.html = hh;
6831             }
6832             
6833             if(typeof(config.tooltip) != 'undefined'){
6834                 c.tooltip = config.tooltip;
6835             }
6836             
6837             if(typeof(config.colspan) != 'undefined'){
6838                 c.colspan = config.colspan;
6839             }
6840             
6841             if(typeof(config.hidden) != 'undefined' && config.hidden){
6842                 c.style += ' display:none;';
6843             }
6844             
6845             if(typeof(config.dataIndex) != 'undefined'){
6846                 c.sort = config.dataIndex;
6847             }
6848             
6849            
6850             
6851             if(typeof(config.align) != 'undefined' && config.align.length){
6852                 c.style += ' text-align:' + config.align + ';';
6853             }
6854             
6855             if(typeof(config.width) != 'undefined'){
6856                 c.style += ' width:' + config.width + 'px;';
6857                 this.totalWidth += config.width;
6858             } else {
6859                 this.totalWidth += 100; // assume minimum of 100 per column?
6860             }
6861             
6862             if(typeof(config.cls) != 'undefined'){
6863                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6864             }
6865             
6866             ['xs','sm','md','lg'].map(function(size){
6867                 
6868                 if(typeof(config[size]) == 'undefined'){
6869                     return;
6870                 }
6871                  
6872                 if (!config[size]) { // 0 = hidden
6873                     // BS 4 '0' is treated as hide that column and below.
6874                     c.cls += ' hidden-' + size + ' hidden' + size + 'down';
6875                     return;
6876                 }
6877                 
6878                 c.cls += ' col-' + size + '-' + config[size] + (
6879                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
6880                 );
6881                 
6882                 
6883             });
6884             
6885             header.cn.push(c)
6886         }
6887         
6888         return header;
6889     },
6890     
6891     renderBody : function()
6892     {
6893         var body = {
6894             tag: 'tbody',
6895             cn : [
6896                 {
6897                     tag: 'tr',
6898                     cn : [
6899                         {
6900                             tag : 'td',
6901                             colspan :  this.cm.getColumnCount()
6902                         }
6903                     ]
6904                 }
6905             ]
6906         };
6907         
6908         return body;
6909     },
6910     
6911     renderFooter : function()
6912     {
6913         var footer = {
6914             tag: 'tfoot',
6915             cn : [
6916                 {
6917                     tag: 'tr',
6918                     cn : [
6919                         {
6920                             tag : 'td',
6921                             colspan :  this.cm.getColumnCount()
6922                         }
6923                     ]
6924                 }
6925             ]
6926         };
6927         
6928         return footer;
6929     },
6930     
6931     
6932     
6933     onLoad : function()
6934     {
6935 //        Roo.log('ds onload');
6936         this.clear();
6937         
6938         var _this = this;
6939         var cm = this.cm;
6940         var ds = this.store;
6941         
6942         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6943             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6944             if (_this.store.sortInfo) {
6945                     
6946                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6947                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6948                 }
6949                 
6950                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6951                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6952                 }
6953             }
6954         });
6955         
6956         var tbody =  this.mainBody;
6957               
6958         if(ds.getCount() > 0){
6959             ds.data.each(function(d,rowIndex){
6960                 var row =  this.renderRow(cm, ds, rowIndex);
6961                 
6962                 tbody.createChild(row);
6963                 
6964                 var _this = this;
6965                 
6966                 if(row.cellObjects.length){
6967                     Roo.each(row.cellObjects, function(r){
6968                         _this.renderCellObject(r);
6969                     })
6970                 }
6971                 
6972             }, this);
6973         }
6974         
6975         var tfoot = this.el.select('tfoot', true).first();
6976         
6977         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6978             
6979             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6980             
6981             var total = this.ds.getTotalCount();
6982             
6983             if(this.footer.pageSize < total){
6984                 this.mainFoot.show();
6985             }
6986         }
6987         
6988         Roo.each(this.el.select('tbody td', true).elements, function(e){
6989             e.on('mouseover', _this.onMouseover, _this);
6990         });
6991         
6992         Roo.each(this.el.select('tbody td', true).elements, function(e){
6993             e.on('mouseout', _this.onMouseout, _this);
6994         });
6995         this.fireEvent('rowsrendered', this);
6996         
6997         this.autoSize();
6998     },
6999     
7000     
7001     onUpdate : function(ds,record)
7002     {
7003         this.refreshRow(record);
7004         this.autoSize();
7005     },
7006     
7007     onRemove : function(ds, record, index, isUpdate){
7008         if(isUpdate !== true){
7009             this.fireEvent("beforerowremoved", this, index, record);
7010         }
7011         var bt = this.mainBody.dom;
7012         
7013         var rows = this.el.select('tbody > tr', true).elements;
7014         
7015         if(typeof(rows[index]) != 'undefined'){
7016             bt.removeChild(rows[index].dom);
7017         }
7018         
7019 //        if(bt.rows[index]){
7020 //            bt.removeChild(bt.rows[index]);
7021 //        }
7022         
7023         if(isUpdate !== true){
7024             //this.stripeRows(index);
7025             //this.syncRowHeights(index, index);
7026             //this.layout();
7027             this.fireEvent("rowremoved", this, index, record);
7028         }
7029     },
7030     
7031     onAdd : function(ds, records, rowIndex)
7032     {
7033         //Roo.log('on Add called');
7034         // - note this does not handle multiple adding very well..
7035         var bt = this.mainBody.dom;
7036         for (var i =0 ; i < records.length;i++) {
7037             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
7038             //Roo.log(records[i]);
7039             //Roo.log(this.store.getAt(rowIndex+i));
7040             this.insertRow(this.store, rowIndex + i, false);
7041             return;
7042         }
7043         
7044     },
7045     
7046     
7047     refreshRow : function(record){
7048         var ds = this.store, index;
7049         if(typeof record == 'number'){
7050             index = record;
7051             record = ds.getAt(index);
7052         }else{
7053             index = ds.indexOf(record);
7054         }
7055         this.insertRow(ds, index, true);
7056         this.autoSize();
7057         this.onRemove(ds, record, index+1, true);
7058         this.autoSize();
7059         //this.syncRowHeights(index, index);
7060         //this.layout();
7061         this.fireEvent("rowupdated", this, index, record);
7062     },
7063     
7064     insertRow : function(dm, rowIndex, isUpdate){
7065         
7066         if(!isUpdate){
7067             this.fireEvent("beforerowsinserted", this, rowIndex);
7068         }
7069             //var s = this.getScrollState();
7070         var row = this.renderRow(this.cm, this.store, rowIndex);
7071         // insert before rowIndex..
7072         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
7073         
7074         var _this = this;
7075                 
7076         if(row.cellObjects.length){
7077             Roo.each(row.cellObjects, function(r){
7078                 _this.renderCellObject(r);
7079             })
7080         }
7081             
7082         if(!isUpdate){
7083             this.fireEvent("rowsinserted", this, rowIndex);
7084             //this.syncRowHeights(firstRow, lastRow);
7085             //this.stripeRows(firstRow);
7086             //this.layout();
7087         }
7088         
7089     },
7090     
7091     
7092     getRowDom : function(rowIndex)
7093     {
7094         var rows = this.el.select('tbody > tr', true).elements;
7095         
7096         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
7097         
7098     },
7099     // returns the object tree for a tr..
7100   
7101     
7102     renderRow : function(cm, ds, rowIndex) 
7103     {
7104         var d = ds.getAt(rowIndex);
7105         
7106         var row = {
7107             tag : 'tr',
7108             cls : 'x-row-' + rowIndex,
7109             cn : []
7110         };
7111             
7112         var cellObjects = [];
7113         
7114         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7115             var config = cm.config[i];
7116             
7117             var renderer = cm.getRenderer(i);
7118             var value = '';
7119             var id = false;
7120             
7121             if(typeof(renderer) !== 'undefined'){
7122                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
7123             }
7124             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
7125             // and are rendered into the cells after the row is rendered - using the id for the element.
7126             
7127             if(typeof(value) === 'object'){
7128                 id = Roo.id();
7129                 cellObjects.push({
7130                     container : id,
7131                     cfg : value 
7132                 })
7133             }
7134             
7135             var rowcfg = {
7136                 record: d,
7137                 rowIndex : rowIndex,
7138                 colIndex : i,
7139                 rowClass : ''
7140             };
7141
7142             this.fireEvent('rowclass', this, rowcfg);
7143             
7144             var td = {
7145                 tag: 'td',
7146                 cls : rowcfg.rowClass + ' x-col-' + i,
7147                 style: '',
7148                 html: (typeof(value) === 'object') ? '' : value
7149             };
7150             
7151             if (id) {
7152                 td.id = id;
7153             }
7154             
7155             if(typeof(config.colspan) != 'undefined'){
7156                 td.colspan = config.colspan;
7157             }
7158             
7159             if(typeof(config.hidden) != 'undefined' && config.hidden){
7160                 td.style += ' display:none;';
7161             }
7162             
7163             if(typeof(config.align) != 'undefined' && config.align.length){
7164                 td.style += ' text-align:' + config.align + ';';
7165             }
7166             if(typeof(config.valign) != 'undefined' && config.valign.length){
7167                 td.style += ' vertical-align:' + config.valign + ';';
7168             }
7169             
7170             if(typeof(config.width) != 'undefined'){
7171                 td.style += ' width:' +  config.width + 'px;';
7172             }
7173             
7174             if(typeof(config.cursor) != 'undefined'){
7175                 td.style += ' cursor:' +  config.cursor + ';';
7176             }
7177             
7178             if(typeof(config.cls) != 'undefined'){
7179                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7180             }
7181             
7182             ['xs','sm','md','lg'].map(function(size){
7183                 
7184                 if(typeof(config[size]) == 'undefined'){
7185                     return;
7186                 }
7187                 
7188                 
7189                   
7190                 if (!config[size]) { // 0 = hidden
7191                     // BS 4 '0' is treated as hide that column and below.
7192                     td.cls += ' hidden-' + size + ' hidden' + size + 'down';
7193                     return;
7194                 }
7195                 
7196                 td.cls += ' col-' + size + '-' + config[size] + (
7197                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
7198                 );
7199                  
7200
7201             });
7202             
7203             row.cn.push(td);
7204            
7205         }
7206         
7207         row.cellObjects = cellObjects;
7208         
7209         return row;
7210           
7211     },
7212     
7213     
7214     
7215     onBeforeLoad : function()
7216     {
7217         
7218     },
7219      /**
7220      * Remove all rows
7221      */
7222     clear : function()
7223     {
7224         this.el.select('tbody', true).first().dom.innerHTML = '';
7225     },
7226     /**
7227      * Show or hide a row.
7228      * @param {Number} rowIndex to show or hide
7229      * @param {Boolean} state hide
7230      */
7231     setRowVisibility : function(rowIndex, state)
7232     {
7233         var bt = this.mainBody.dom;
7234         
7235         var rows = this.el.select('tbody > tr', true).elements;
7236         
7237         if(typeof(rows[rowIndex]) == 'undefined'){
7238             return;
7239         }
7240         rows[rowIndex].dom.style.display = state ? '' : 'none';
7241     },
7242     
7243     
7244     getSelectionModel : function(){
7245         if(!this.selModel){
7246             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7247         }
7248         return this.selModel;
7249     },
7250     /*
7251      * Render the Roo.bootstrap object from renderder
7252      */
7253     renderCellObject : function(r)
7254     {
7255         var _this = this;
7256         
7257         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7258         
7259         var t = r.cfg.render(r.container);
7260         
7261         if(r.cfg.cn){
7262             Roo.each(r.cfg.cn, function(c){
7263                 var child = {
7264                     container: t.getChildContainer(),
7265                     cfg: c
7266                 };
7267                 _this.renderCellObject(child);
7268             })
7269         }
7270     },
7271     
7272     getRowIndex : function(row)
7273     {
7274         var rowIndex = -1;
7275         
7276         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7277             if(el != row){
7278                 return;
7279             }
7280             
7281             rowIndex = index;
7282         });
7283         
7284         return rowIndex;
7285     },
7286      /**
7287      * Returns the grid's underlying element = used by panel.Grid
7288      * @return {Element} The element
7289      */
7290     getGridEl : function(){
7291         return this.el;
7292     },
7293      /**
7294      * Forces a resize - used by panel.Grid
7295      * @return {Element} The element
7296      */
7297     autoSize : function()
7298     {
7299         //var ctr = Roo.get(this.container.dom.parentElement);
7300         var ctr = Roo.get(this.el.dom);
7301         
7302         var thd = this.getGridEl().select('thead',true).first();
7303         var tbd = this.getGridEl().select('tbody', true).first();
7304         var tfd = this.getGridEl().select('tfoot', true).first();
7305         
7306         var cw = ctr.getWidth();
7307         
7308         if (tbd) {
7309             
7310             tbd.setSize(ctr.getWidth(),
7311                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7312             );
7313             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7314             cw -= barsize;
7315         }
7316         cw = Math.max(cw, this.totalWidth);
7317         this.getGridEl().select('tr',true).setWidth(cw);
7318         // resize 'expandable coloumn?
7319         
7320         return; // we doe not have a view in this design..
7321         
7322     },
7323     onBodyScroll: function()
7324     {
7325         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7326         if(this.mainHead){
7327             this.mainHead.setStyle({
7328                 'position' : 'relative',
7329                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7330             });
7331         }
7332         
7333         if(this.lazyLoad){
7334             
7335             var scrollHeight = this.mainBody.dom.scrollHeight;
7336             
7337             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7338             
7339             var height = this.mainBody.getHeight();
7340             
7341             if(scrollHeight - height == scrollTop) {
7342                 
7343                 var total = this.ds.getTotalCount();
7344                 
7345                 if(this.footer.cursor + this.footer.pageSize < total){
7346                     
7347                     this.footer.ds.load({
7348                         params : {
7349                             start : this.footer.cursor + this.footer.pageSize,
7350                             limit : this.footer.pageSize
7351                         },
7352                         add : true
7353                     });
7354                 }
7355             }
7356             
7357         }
7358     },
7359     
7360     onHeaderChange : function()
7361     {
7362         var header = this.renderHeader();
7363         var table = this.el.select('table', true).first();
7364         
7365         this.mainHead.remove();
7366         this.mainHead = table.createChild(header, this.mainBody, false);
7367     },
7368     
7369     onHiddenChange : function(colModel, colIndex, hidden)
7370     {
7371         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7372         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7373         
7374         this.CSS.updateRule(thSelector, "display", "");
7375         this.CSS.updateRule(tdSelector, "display", "");
7376         
7377         if(hidden){
7378             this.CSS.updateRule(thSelector, "display", "none");
7379             this.CSS.updateRule(tdSelector, "display", "none");
7380         }
7381         
7382         this.onHeaderChange();
7383         this.onLoad();
7384     },
7385     
7386     setColumnWidth: function(col_index, width)
7387     {
7388         // width = "md-2 xs-2..."
7389         if(!this.colModel.config[col_index]) {
7390             return;
7391         }
7392         
7393         var w = width.split(" ");
7394         
7395         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7396         
7397         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7398         
7399         
7400         for(var j = 0; j < w.length; j++) {
7401             
7402             if(!w[j]) {
7403                 continue;
7404             }
7405             
7406             var size_cls = w[j].split("-");
7407             
7408             if(!Number.isInteger(size_cls[1] * 1)) {
7409                 continue;
7410             }
7411             
7412             if(!this.colModel.config[col_index][size_cls[0]]) {
7413                 continue;
7414             }
7415             
7416             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7417                 continue;
7418             }
7419             
7420             h_row[0].classList.replace(
7421                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7422                 "col-"+size_cls[0]+"-"+size_cls[1]
7423             );
7424             
7425             for(var i = 0; i < rows.length; i++) {
7426                 
7427                 var size_cls = w[j].split("-");
7428                 
7429                 if(!Number.isInteger(size_cls[1] * 1)) {
7430                     continue;
7431                 }
7432                 
7433                 if(!this.colModel.config[col_index][size_cls[0]]) {
7434                     continue;
7435                 }
7436                 
7437                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7438                     continue;
7439                 }
7440                 
7441                 rows[i].classList.replace(
7442                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7443                     "col-"+size_cls[0]+"-"+size_cls[1]
7444                 );
7445             }
7446             
7447             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7448         }
7449     }
7450 });
7451
7452  
7453
7454  /*
7455  * - LGPL
7456  *
7457  * table cell
7458  * 
7459  */
7460
7461 /**
7462  * @class Roo.bootstrap.TableCell
7463  * @extends Roo.bootstrap.Component
7464  * Bootstrap TableCell class
7465  * @cfg {String} html cell contain text
7466  * @cfg {String} cls cell class
7467  * @cfg {String} tag cell tag (td|th) default td
7468  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7469  * @cfg {String} align Aligns the content in a cell
7470  * @cfg {String} axis Categorizes cells
7471  * @cfg {String} bgcolor Specifies the background color of a cell
7472  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7473  * @cfg {Number} colspan Specifies the number of columns a cell should span
7474  * @cfg {String} headers Specifies one or more header cells a cell is related to
7475  * @cfg {Number} height Sets the height of a cell
7476  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7477  * @cfg {Number} rowspan Sets the number of rows a cell should span
7478  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7479  * @cfg {String} valign Vertical aligns the content in a cell
7480  * @cfg {Number} width Specifies the width of a cell
7481  * 
7482  * @constructor
7483  * Create a new TableCell
7484  * @param {Object} config The config object
7485  */
7486
7487 Roo.bootstrap.TableCell = function(config){
7488     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7489 };
7490
7491 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7492     
7493     html: false,
7494     cls: false,
7495     tag: false,
7496     abbr: false,
7497     align: false,
7498     axis: false,
7499     bgcolor: false,
7500     charoff: false,
7501     colspan: false,
7502     headers: false,
7503     height: false,
7504     nowrap: false,
7505     rowspan: false,
7506     scope: false,
7507     valign: false,
7508     width: false,
7509     
7510     
7511     getAutoCreate : function(){
7512         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7513         
7514         cfg = {
7515             tag: 'td'
7516         };
7517         
7518         if(this.tag){
7519             cfg.tag = this.tag;
7520         }
7521         
7522         if (this.html) {
7523             cfg.html=this.html
7524         }
7525         if (this.cls) {
7526             cfg.cls=this.cls
7527         }
7528         if (this.abbr) {
7529             cfg.abbr=this.abbr
7530         }
7531         if (this.align) {
7532             cfg.align=this.align
7533         }
7534         if (this.axis) {
7535             cfg.axis=this.axis
7536         }
7537         if (this.bgcolor) {
7538             cfg.bgcolor=this.bgcolor
7539         }
7540         if (this.charoff) {
7541             cfg.charoff=this.charoff
7542         }
7543         if (this.colspan) {
7544             cfg.colspan=this.colspan
7545         }
7546         if (this.headers) {
7547             cfg.headers=this.headers
7548         }
7549         if (this.height) {
7550             cfg.height=this.height
7551         }
7552         if (this.nowrap) {
7553             cfg.nowrap=this.nowrap
7554         }
7555         if (this.rowspan) {
7556             cfg.rowspan=this.rowspan
7557         }
7558         if (this.scope) {
7559             cfg.scope=this.scope
7560         }
7561         if (this.valign) {
7562             cfg.valign=this.valign
7563         }
7564         if (this.width) {
7565             cfg.width=this.width
7566         }
7567         
7568         
7569         return cfg;
7570     }
7571    
7572 });
7573
7574  
7575
7576  /*
7577  * - LGPL
7578  *
7579  * table row
7580  * 
7581  */
7582
7583 /**
7584  * @class Roo.bootstrap.TableRow
7585  * @extends Roo.bootstrap.Component
7586  * Bootstrap TableRow class
7587  * @cfg {String} cls row class
7588  * @cfg {String} align Aligns the content in a table row
7589  * @cfg {String} bgcolor Specifies a background color for a table row
7590  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7591  * @cfg {String} valign Vertical aligns the content in a table row
7592  * 
7593  * @constructor
7594  * Create a new TableRow
7595  * @param {Object} config The config object
7596  */
7597
7598 Roo.bootstrap.TableRow = function(config){
7599     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7600 };
7601
7602 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7603     
7604     cls: false,
7605     align: false,
7606     bgcolor: false,
7607     charoff: false,
7608     valign: false,
7609     
7610     getAutoCreate : function(){
7611         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7612         
7613         cfg = {
7614             tag: 'tr'
7615         };
7616             
7617         if(this.cls){
7618             cfg.cls = this.cls;
7619         }
7620         if(this.align){
7621             cfg.align = this.align;
7622         }
7623         if(this.bgcolor){
7624             cfg.bgcolor = this.bgcolor;
7625         }
7626         if(this.charoff){
7627             cfg.charoff = this.charoff;
7628         }
7629         if(this.valign){
7630             cfg.valign = this.valign;
7631         }
7632         
7633         return cfg;
7634     }
7635    
7636 });
7637
7638  
7639
7640  /*
7641  * - LGPL
7642  *
7643  * table body
7644  * 
7645  */
7646
7647 /**
7648  * @class Roo.bootstrap.TableBody
7649  * @extends Roo.bootstrap.Component
7650  * Bootstrap TableBody class
7651  * @cfg {String} cls element class
7652  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7653  * @cfg {String} align Aligns the content inside the element
7654  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7655  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7656  * 
7657  * @constructor
7658  * Create a new TableBody
7659  * @param {Object} config The config object
7660  */
7661
7662 Roo.bootstrap.TableBody = function(config){
7663     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7664 };
7665
7666 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7667     
7668     cls: false,
7669     tag: false,
7670     align: false,
7671     charoff: false,
7672     valign: false,
7673     
7674     getAutoCreate : function(){
7675         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7676         
7677         cfg = {
7678             tag: 'tbody'
7679         };
7680             
7681         if (this.cls) {
7682             cfg.cls=this.cls
7683         }
7684         if(this.tag){
7685             cfg.tag = this.tag;
7686         }
7687         
7688         if(this.align){
7689             cfg.align = this.align;
7690         }
7691         if(this.charoff){
7692             cfg.charoff = this.charoff;
7693         }
7694         if(this.valign){
7695             cfg.valign = this.valign;
7696         }
7697         
7698         return cfg;
7699     }
7700     
7701     
7702 //    initEvents : function()
7703 //    {
7704 //        
7705 //        if(!this.store){
7706 //            return;
7707 //        }
7708 //        
7709 //        this.store = Roo.factory(this.store, Roo.data);
7710 //        this.store.on('load', this.onLoad, this);
7711 //        
7712 //        this.store.load();
7713 //        
7714 //    },
7715 //    
7716 //    onLoad: function () 
7717 //    {   
7718 //        this.fireEvent('load', this);
7719 //    }
7720 //    
7721 //   
7722 });
7723
7724  
7725
7726  /*
7727  * Based on:
7728  * Ext JS Library 1.1.1
7729  * Copyright(c) 2006-2007, Ext JS, LLC.
7730  *
7731  * Originally Released Under LGPL - original licence link has changed is not relivant.
7732  *
7733  * Fork - LGPL
7734  * <script type="text/javascript">
7735  */
7736
7737 // as we use this in bootstrap.
7738 Roo.namespace('Roo.form');
7739  /**
7740  * @class Roo.form.Action
7741  * Internal Class used to handle form actions
7742  * @constructor
7743  * @param {Roo.form.BasicForm} el The form element or its id
7744  * @param {Object} config Configuration options
7745  */
7746
7747  
7748  
7749 // define the action interface
7750 Roo.form.Action = function(form, options){
7751     this.form = form;
7752     this.options = options || {};
7753 };
7754 /**
7755  * Client Validation Failed
7756  * @const 
7757  */
7758 Roo.form.Action.CLIENT_INVALID = 'client';
7759 /**
7760  * Server Validation Failed
7761  * @const 
7762  */
7763 Roo.form.Action.SERVER_INVALID = 'server';
7764  /**
7765  * Connect to Server Failed
7766  * @const 
7767  */
7768 Roo.form.Action.CONNECT_FAILURE = 'connect';
7769 /**
7770  * Reading Data from Server Failed
7771  * @const 
7772  */
7773 Roo.form.Action.LOAD_FAILURE = 'load';
7774
7775 Roo.form.Action.prototype = {
7776     type : 'default',
7777     failureType : undefined,
7778     response : undefined,
7779     result : undefined,
7780
7781     // interface method
7782     run : function(options){
7783
7784     },
7785
7786     // interface method
7787     success : function(response){
7788
7789     },
7790
7791     // interface method
7792     handleResponse : function(response){
7793
7794     },
7795
7796     // default connection failure
7797     failure : function(response){
7798         
7799         this.response = response;
7800         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7801         this.form.afterAction(this, false);
7802     },
7803
7804     processResponse : function(response){
7805         this.response = response;
7806         if(!response.responseText){
7807             return true;
7808         }
7809         this.result = this.handleResponse(response);
7810         return this.result;
7811     },
7812
7813     // utility functions used internally
7814     getUrl : function(appendParams){
7815         var url = this.options.url || this.form.url || this.form.el.dom.action;
7816         if(appendParams){
7817             var p = this.getParams();
7818             if(p){
7819                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7820             }
7821         }
7822         return url;
7823     },
7824
7825     getMethod : function(){
7826         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7827     },
7828
7829     getParams : function(){
7830         var bp = this.form.baseParams;
7831         var p = this.options.params;
7832         if(p){
7833             if(typeof p == "object"){
7834                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7835             }else if(typeof p == 'string' && bp){
7836                 p += '&' + Roo.urlEncode(bp);
7837             }
7838         }else if(bp){
7839             p = Roo.urlEncode(bp);
7840         }
7841         return p;
7842     },
7843
7844     createCallback : function(){
7845         return {
7846             success: this.success,
7847             failure: this.failure,
7848             scope: this,
7849             timeout: (this.form.timeout*1000),
7850             upload: this.form.fileUpload ? this.success : undefined
7851         };
7852     }
7853 };
7854
7855 Roo.form.Action.Submit = function(form, options){
7856     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7857 };
7858
7859 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7860     type : 'submit',
7861
7862     haveProgress : false,
7863     uploadComplete : false,
7864     
7865     // uploadProgress indicator.
7866     uploadProgress : function()
7867     {
7868         if (!this.form.progressUrl) {
7869             return;
7870         }
7871         
7872         if (!this.haveProgress) {
7873             Roo.MessageBox.progress("Uploading", "Uploading");
7874         }
7875         if (this.uploadComplete) {
7876            Roo.MessageBox.hide();
7877            return;
7878         }
7879         
7880         this.haveProgress = true;
7881    
7882         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7883         
7884         var c = new Roo.data.Connection();
7885         c.request({
7886             url : this.form.progressUrl,
7887             params: {
7888                 id : uid
7889             },
7890             method: 'GET',
7891             success : function(req){
7892                //console.log(data);
7893                 var rdata = false;
7894                 var edata;
7895                 try  {
7896                    rdata = Roo.decode(req.responseText)
7897                 } catch (e) {
7898                     Roo.log("Invalid data from server..");
7899                     Roo.log(edata);
7900                     return;
7901                 }
7902                 if (!rdata || !rdata.success) {
7903                     Roo.log(rdata);
7904                     Roo.MessageBox.alert(Roo.encode(rdata));
7905                     return;
7906                 }
7907                 var data = rdata.data;
7908                 
7909                 if (this.uploadComplete) {
7910                    Roo.MessageBox.hide();
7911                    return;
7912                 }
7913                    
7914                 if (data){
7915                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7916                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7917                     );
7918                 }
7919                 this.uploadProgress.defer(2000,this);
7920             },
7921        
7922             failure: function(data) {
7923                 Roo.log('progress url failed ');
7924                 Roo.log(data);
7925             },
7926             scope : this
7927         });
7928            
7929     },
7930     
7931     
7932     run : function()
7933     {
7934         // run get Values on the form, so it syncs any secondary forms.
7935         this.form.getValues();
7936         
7937         var o = this.options;
7938         var method = this.getMethod();
7939         var isPost = method == 'POST';
7940         if(o.clientValidation === false || this.form.isValid()){
7941             
7942             if (this.form.progressUrl) {
7943                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7944                     (new Date() * 1) + '' + Math.random());
7945                     
7946             } 
7947             
7948             
7949             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7950                 form:this.form.el.dom,
7951                 url:this.getUrl(!isPost),
7952                 method: method,
7953                 params:isPost ? this.getParams() : null,
7954                 isUpload: this.form.fileUpload,
7955                 formData : this.form.formData
7956             }));
7957             
7958             this.uploadProgress();
7959
7960         }else if (o.clientValidation !== false){ // client validation failed
7961             this.failureType = Roo.form.Action.CLIENT_INVALID;
7962             this.form.afterAction(this, false);
7963         }
7964     },
7965
7966     success : function(response)
7967     {
7968         this.uploadComplete= true;
7969         if (this.haveProgress) {
7970             Roo.MessageBox.hide();
7971         }
7972         
7973         
7974         var result = this.processResponse(response);
7975         if(result === true || result.success){
7976             this.form.afterAction(this, true);
7977             return;
7978         }
7979         if(result.errors){
7980             this.form.markInvalid(result.errors);
7981             this.failureType = Roo.form.Action.SERVER_INVALID;
7982         }
7983         this.form.afterAction(this, false);
7984     },
7985     failure : function(response)
7986     {
7987         this.uploadComplete= true;
7988         if (this.haveProgress) {
7989             Roo.MessageBox.hide();
7990         }
7991         
7992         this.response = response;
7993         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7994         this.form.afterAction(this, false);
7995     },
7996     
7997     handleResponse : function(response){
7998         if(this.form.errorReader){
7999             var rs = this.form.errorReader.read(response);
8000             var errors = [];
8001             if(rs.records){
8002                 for(var i = 0, len = rs.records.length; i < len; i++) {
8003                     var r = rs.records[i];
8004                     errors[i] = r.data;
8005                 }
8006             }
8007             if(errors.length < 1){
8008                 errors = null;
8009             }
8010             return {
8011                 success : rs.success,
8012                 errors : errors
8013             };
8014         }
8015         var ret = false;
8016         try {
8017             ret = Roo.decode(response.responseText);
8018         } catch (e) {
8019             ret = {
8020                 success: false,
8021                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
8022                 errors : []
8023             };
8024         }
8025         return ret;
8026         
8027     }
8028 });
8029
8030
8031 Roo.form.Action.Load = function(form, options){
8032     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
8033     this.reader = this.form.reader;
8034 };
8035
8036 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
8037     type : 'load',
8038
8039     run : function(){
8040         
8041         Roo.Ajax.request(Roo.apply(
8042                 this.createCallback(), {
8043                     method:this.getMethod(),
8044                     url:this.getUrl(false),
8045                     params:this.getParams()
8046         }));
8047     },
8048
8049     success : function(response){
8050         
8051         var result = this.processResponse(response);
8052         if(result === true || !result.success || !result.data){
8053             this.failureType = Roo.form.Action.LOAD_FAILURE;
8054             this.form.afterAction(this, false);
8055             return;
8056         }
8057         this.form.clearInvalid();
8058         this.form.setValues(result.data);
8059         this.form.afterAction(this, true);
8060     },
8061
8062     handleResponse : function(response){
8063         if(this.form.reader){
8064             var rs = this.form.reader.read(response);
8065             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
8066             return {
8067                 success : rs.success,
8068                 data : data
8069             };
8070         }
8071         return Roo.decode(response.responseText);
8072     }
8073 });
8074
8075 Roo.form.Action.ACTION_TYPES = {
8076     'load' : Roo.form.Action.Load,
8077     'submit' : Roo.form.Action.Submit
8078 };/*
8079  * - LGPL
8080  *
8081  * form
8082  *
8083  */
8084
8085 /**
8086  * @class Roo.bootstrap.Form
8087  * @extends Roo.bootstrap.Component
8088  * Bootstrap Form class
8089  * @cfg {String} method  GET | POST (default POST)
8090  * @cfg {String} labelAlign top | left (default top)
8091  * @cfg {String} align left  | right - for navbars
8092  * @cfg {Boolean} loadMask load mask when submit (default true)
8093
8094  *
8095  * @constructor
8096  * Create a new Form
8097  * @param {Object} config The config object
8098  */
8099
8100
8101 Roo.bootstrap.Form = function(config){
8102     
8103     Roo.bootstrap.Form.superclass.constructor.call(this, config);
8104     
8105     Roo.bootstrap.Form.popover.apply();
8106     
8107     this.addEvents({
8108         /**
8109          * @event clientvalidation
8110          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
8111          * @param {Form} this
8112          * @param {Boolean} valid true if the form has passed client-side validation
8113          */
8114         clientvalidation: true,
8115         /**
8116          * @event beforeaction
8117          * Fires before any action is performed. Return false to cancel the action.
8118          * @param {Form} this
8119          * @param {Action} action The action to be performed
8120          */
8121         beforeaction: true,
8122         /**
8123          * @event actionfailed
8124          * Fires when an action fails.
8125          * @param {Form} this
8126          * @param {Action} action The action that failed
8127          */
8128         actionfailed : true,
8129         /**
8130          * @event actioncomplete
8131          * Fires when an action is completed.
8132          * @param {Form} this
8133          * @param {Action} action The action that completed
8134          */
8135         actioncomplete : true
8136     });
8137 };
8138
8139 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
8140
8141      /**
8142      * @cfg {String} method
8143      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8144      */
8145     method : 'POST',
8146     /**
8147      * @cfg {String} url
8148      * The URL to use for form actions if one isn't supplied in the action options.
8149      */
8150     /**
8151      * @cfg {Boolean} fileUpload
8152      * Set to true if this form is a file upload.
8153      */
8154
8155     /**
8156      * @cfg {Object} baseParams
8157      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8158      */
8159
8160     /**
8161      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8162      */
8163     timeout: 30,
8164     /**
8165      * @cfg {Sting} align (left|right) for navbar forms
8166      */
8167     align : 'left',
8168
8169     // private
8170     activeAction : null,
8171
8172     /**
8173      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8174      * element by passing it or its id or mask the form itself by passing in true.
8175      * @type Mixed
8176      */
8177     waitMsgTarget : false,
8178
8179     loadMask : true,
8180     
8181     /**
8182      * @cfg {Boolean} errorMask (true|false) default false
8183      */
8184     errorMask : false,
8185     
8186     /**
8187      * @cfg {Number} maskOffset Default 100
8188      */
8189     maskOffset : 100,
8190     
8191     /**
8192      * @cfg {Boolean} maskBody
8193      */
8194     maskBody : false,
8195
8196     getAutoCreate : function(){
8197
8198         var cfg = {
8199             tag: 'form',
8200             method : this.method || 'POST',
8201             id : this.id || Roo.id(),
8202             cls : ''
8203         };
8204         if (this.parent().xtype.match(/^Nav/)) {
8205             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8206
8207         }
8208
8209         if (this.labelAlign == 'left' ) {
8210             cfg.cls += ' form-horizontal';
8211         }
8212
8213
8214         return cfg;
8215     },
8216     initEvents : function()
8217     {
8218         this.el.on('submit', this.onSubmit, this);
8219         // this was added as random key presses on the form where triggering form submit.
8220         this.el.on('keypress', function(e) {
8221             if (e.getCharCode() != 13) {
8222                 return true;
8223             }
8224             // we might need to allow it for textareas.. and some other items.
8225             // check e.getTarget().
8226
8227             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8228                 return true;
8229             }
8230
8231             Roo.log("keypress blocked");
8232
8233             e.preventDefault();
8234             return false;
8235         });
8236         
8237     },
8238     // private
8239     onSubmit : function(e){
8240         e.stopEvent();
8241     },
8242
8243      /**
8244      * Returns true if client-side validation on the form is successful.
8245      * @return Boolean
8246      */
8247     isValid : function(){
8248         var items = this.getItems();
8249         var valid = true;
8250         var target = false;
8251         
8252         items.each(function(f){
8253             
8254             if(f.validate()){
8255                 return;
8256             }
8257             
8258             Roo.log('invalid field: ' + f.name);
8259             
8260             valid = false;
8261
8262             if(!target && f.el.isVisible(true)){
8263                 target = f;
8264             }
8265            
8266         });
8267         
8268         if(this.errorMask && !valid){
8269             Roo.bootstrap.Form.popover.mask(this, target);
8270         }
8271         
8272         return valid;
8273     },
8274     
8275     /**
8276      * Returns true if any fields in this form have changed since their original load.
8277      * @return Boolean
8278      */
8279     isDirty : function(){
8280         var dirty = false;
8281         var items = this.getItems();
8282         items.each(function(f){
8283            if(f.isDirty()){
8284                dirty = true;
8285                return false;
8286            }
8287            return true;
8288         });
8289         return dirty;
8290     },
8291      /**
8292      * Performs a predefined action (submit or load) or custom actions you define on this form.
8293      * @param {String} actionName The name of the action type
8294      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8295      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8296      * accept other config options):
8297      * <pre>
8298 Property          Type             Description
8299 ----------------  ---------------  ----------------------------------------------------------------------------------
8300 url               String           The url for the action (defaults to the form's url)
8301 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8302 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8303 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8304                                    validate the form on the client (defaults to false)
8305      * </pre>
8306      * @return {BasicForm} this
8307      */
8308     doAction : function(action, options){
8309         if(typeof action == 'string'){
8310             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8311         }
8312         if(this.fireEvent('beforeaction', this, action) !== false){
8313             this.beforeAction(action);
8314             action.run.defer(100, action);
8315         }
8316         return this;
8317     },
8318
8319     // private
8320     beforeAction : function(action){
8321         var o = action.options;
8322         
8323         if(this.loadMask){
8324             
8325             if(this.maskBody){
8326                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8327             } else {
8328                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8329             }
8330         }
8331         // not really supported yet.. ??
8332
8333         //if(this.waitMsgTarget === true){
8334         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8335         //}else if(this.waitMsgTarget){
8336         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8337         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8338         //}else {
8339         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8340        // }
8341
8342     },
8343
8344     // private
8345     afterAction : function(action, success){
8346         this.activeAction = null;
8347         var o = action.options;
8348
8349         if(this.loadMask){
8350             
8351             if(this.maskBody){
8352                 Roo.get(document.body).unmask();
8353             } else {
8354                 this.el.unmask();
8355             }
8356         }
8357         
8358         //if(this.waitMsgTarget === true){
8359 //            this.el.unmask();
8360         //}else if(this.waitMsgTarget){
8361         //    this.waitMsgTarget.unmask();
8362         //}else{
8363         //    Roo.MessageBox.updateProgress(1);
8364         //    Roo.MessageBox.hide();
8365        // }
8366         //
8367         if(success){
8368             if(o.reset){
8369                 this.reset();
8370             }
8371             Roo.callback(o.success, o.scope, [this, action]);
8372             this.fireEvent('actioncomplete', this, action);
8373
8374         }else{
8375
8376             // failure condition..
8377             // we have a scenario where updates need confirming.
8378             // eg. if a locking scenario exists..
8379             // we look for { errors : { needs_confirm : true }} in the response.
8380             if (
8381                 (typeof(action.result) != 'undefined')  &&
8382                 (typeof(action.result.errors) != 'undefined')  &&
8383                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8384            ){
8385                 var _t = this;
8386                 Roo.log("not supported yet");
8387                  /*
8388
8389                 Roo.MessageBox.confirm(
8390                     "Change requires confirmation",
8391                     action.result.errorMsg,
8392                     function(r) {
8393                         if (r != 'yes') {
8394                             return;
8395                         }
8396                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8397                     }
8398
8399                 );
8400                 */
8401
8402
8403                 return;
8404             }
8405
8406             Roo.callback(o.failure, o.scope, [this, action]);
8407             // show an error message if no failed handler is set..
8408             if (!this.hasListener('actionfailed')) {
8409                 Roo.log("need to add dialog support");
8410                 /*
8411                 Roo.MessageBox.alert("Error",
8412                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8413                         action.result.errorMsg :
8414                         "Saving Failed, please check your entries or try again"
8415                 );
8416                 */
8417             }
8418
8419             this.fireEvent('actionfailed', this, action);
8420         }
8421
8422     },
8423     /**
8424      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8425      * @param {String} id The value to search for
8426      * @return Field
8427      */
8428     findField : function(id){
8429         var items = this.getItems();
8430         var field = items.get(id);
8431         if(!field){
8432              items.each(function(f){
8433                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8434                     field = f;
8435                     return false;
8436                 }
8437                 return true;
8438             });
8439         }
8440         return field || null;
8441     },
8442      /**
8443      * Mark fields in this form invalid in bulk.
8444      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8445      * @return {BasicForm} this
8446      */
8447     markInvalid : function(errors){
8448         if(errors instanceof Array){
8449             for(var i = 0, len = errors.length; i < len; i++){
8450                 var fieldError = errors[i];
8451                 var f = this.findField(fieldError.id);
8452                 if(f){
8453                     f.markInvalid(fieldError.msg);
8454                 }
8455             }
8456         }else{
8457             var field, id;
8458             for(id in errors){
8459                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8460                     field.markInvalid(errors[id]);
8461                 }
8462             }
8463         }
8464         //Roo.each(this.childForms || [], function (f) {
8465         //    f.markInvalid(errors);
8466         //});
8467
8468         return this;
8469     },
8470
8471     /**
8472      * Set values for fields in this form in bulk.
8473      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8474      * @return {BasicForm} this
8475      */
8476     setValues : function(values){
8477         if(values instanceof Array){ // array of objects
8478             for(var i = 0, len = values.length; i < len; i++){
8479                 var v = values[i];
8480                 var f = this.findField(v.id);
8481                 if(f){
8482                     f.setValue(v.value);
8483                     if(this.trackResetOnLoad){
8484                         f.originalValue = f.getValue();
8485                     }
8486                 }
8487             }
8488         }else{ // object hash
8489             var field, id;
8490             for(id in values){
8491                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8492
8493                     if (field.setFromData &&
8494                         field.valueField &&
8495                         field.displayField &&
8496                         // combos' with local stores can
8497                         // be queried via setValue()
8498                         // to set their value..
8499                         (field.store && !field.store.isLocal)
8500                         ) {
8501                         // it's a combo
8502                         var sd = { };
8503                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8504                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8505                         field.setFromData(sd);
8506
8507                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8508                         
8509                         field.setFromData(values);
8510                         
8511                     } else {
8512                         field.setValue(values[id]);
8513                     }
8514
8515
8516                     if(this.trackResetOnLoad){
8517                         field.originalValue = field.getValue();
8518                     }
8519                 }
8520             }
8521         }
8522
8523         //Roo.each(this.childForms || [], function (f) {
8524         //    f.setValues(values);
8525         //});
8526
8527         return this;
8528     },
8529
8530     /**
8531      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8532      * they are returned as an array.
8533      * @param {Boolean} asString
8534      * @return {Object}
8535      */
8536     getValues : function(asString){
8537         //if (this.childForms) {
8538             // copy values from the child forms
8539         //    Roo.each(this.childForms, function (f) {
8540         //        this.setValues(f.getValues());
8541         //    }, this);
8542         //}
8543
8544
8545
8546         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8547         if(asString === true){
8548             return fs;
8549         }
8550         return Roo.urlDecode(fs);
8551     },
8552
8553     /**
8554      * Returns the fields in this form as an object with key/value pairs.
8555      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8556      * @return {Object}
8557      */
8558     getFieldValues : function(with_hidden)
8559     {
8560         var items = this.getItems();
8561         var ret = {};
8562         items.each(function(f){
8563             
8564             if (!f.getName()) {
8565                 return;
8566             }
8567             
8568             var v = f.getValue();
8569             
8570             if (f.inputType =='radio') {
8571                 if (typeof(ret[f.getName()]) == 'undefined') {
8572                     ret[f.getName()] = ''; // empty..
8573                 }
8574
8575                 if (!f.el.dom.checked) {
8576                     return;
8577
8578                 }
8579                 v = f.el.dom.value;
8580
8581             }
8582             
8583             if(f.xtype == 'MoneyField'){
8584                 ret[f.currencyName] = f.getCurrency();
8585             }
8586
8587             // not sure if this supported any more..
8588             if ((typeof(v) == 'object') && f.getRawValue) {
8589                 v = f.getRawValue() ; // dates..
8590             }
8591             // combo boxes where name != hiddenName...
8592             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8593                 ret[f.name] = f.getRawValue();
8594             }
8595             ret[f.getName()] = v;
8596         });
8597
8598         return ret;
8599     },
8600
8601     /**
8602      * Clears all invalid messages in this form.
8603      * @return {BasicForm} this
8604      */
8605     clearInvalid : function(){
8606         var items = this.getItems();
8607
8608         items.each(function(f){
8609            f.clearInvalid();
8610         });
8611
8612         return this;
8613     },
8614
8615     /**
8616      * Resets this form.
8617      * @return {BasicForm} this
8618      */
8619     reset : function(){
8620         var items = this.getItems();
8621         items.each(function(f){
8622             f.reset();
8623         });
8624
8625         Roo.each(this.childForms || [], function (f) {
8626             f.reset();
8627         });
8628
8629
8630         return this;
8631     },
8632     
8633     getItems : function()
8634     {
8635         var r=new Roo.util.MixedCollection(false, function(o){
8636             return o.id || (o.id = Roo.id());
8637         });
8638         var iter = function(el) {
8639             if (el.inputEl) {
8640                 r.add(el);
8641             }
8642             if (!el.items) {
8643                 return;
8644             }
8645             Roo.each(el.items,function(e) {
8646                 iter(e);
8647             });
8648         };
8649
8650         iter(this);
8651         return r;
8652     },
8653     
8654     hideFields : function(items)
8655     {
8656         Roo.each(items, function(i){
8657             
8658             var f = this.findField(i);
8659             
8660             if(!f){
8661                 return;
8662             }
8663             
8664             f.hide();
8665             
8666         }, this);
8667     },
8668     
8669     showFields : function(items)
8670     {
8671         Roo.each(items, function(i){
8672             
8673             var f = this.findField(i);
8674             
8675             if(!f){
8676                 return;
8677             }
8678             
8679             f.show();
8680             
8681         }, this);
8682     }
8683
8684 });
8685
8686 Roo.apply(Roo.bootstrap.Form, {
8687     
8688     popover : {
8689         
8690         padding : 5,
8691         
8692         isApplied : false,
8693         
8694         isMasked : false,
8695         
8696         form : false,
8697         
8698         target : false,
8699         
8700         toolTip : false,
8701         
8702         intervalID : false,
8703         
8704         maskEl : false,
8705         
8706         apply : function()
8707         {
8708             if(this.isApplied){
8709                 return;
8710             }
8711             
8712             this.maskEl = {
8713                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8714                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8715                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8716                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8717             };
8718             
8719             this.maskEl.top.enableDisplayMode("block");
8720             this.maskEl.left.enableDisplayMode("block");
8721             this.maskEl.bottom.enableDisplayMode("block");
8722             this.maskEl.right.enableDisplayMode("block");
8723             
8724             this.toolTip = new Roo.bootstrap.Tooltip({
8725                 cls : 'roo-form-error-popover',
8726                 alignment : {
8727                     'left' : ['r-l', [-2,0], 'right'],
8728                     'right' : ['l-r', [2,0], 'left'],
8729                     'bottom' : ['tl-bl', [0,2], 'top'],
8730                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8731                 }
8732             });
8733             
8734             this.toolTip.render(Roo.get(document.body));
8735
8736             this.toolTip.el.enableDisplayMode("block");
8737             
8738             Roo.get(document.body).on('click', function(){
8739                 this.unmask();
8740             }, this);
8741             
8742             Roo.get(document.body).on('touchstart', function(){
8743                 this.unmask();
8744             }, this);
8745             
8746             this.isApplied = true
8747         },
8748         
8749         mask : function(form, target)
8750         {
8751             this.form = form;
8752             
8753             this.target = target;
8754             
8755             if(!this.form.errorMask || !target.el){
8756                 return;
8757             }
8758             
8759             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8760             
8761             Roo.log(scrollable);
8762             
8763             var ot = this.target.el.calcOffsetsTo(scrollable);
8764             
8765             var scrollTo = ot[1] - this.form.maskOffset;
8766             
8767             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8768             
8769             scrollable.scrollTo('top', scrollTo);
8770             
8771             var box = this.target.el.getBox();
8772             Roo.log(box);
8773             var zIndex = Roo.bootstrap.Modal.zIndex++;
8774
8775             
8776             this.maskEl.top.setStyle('position', 'absolute');
8777             this.maskEl.top.setStyle('z-index', zIndex);
8778             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8779             this.maskEl.top.setLeft(0);
8780             this.maskEl.top.setTop(0);
8781             this.maskEl.top.show();
8782             
8783             this.maskEl.left.setStyle('position', 'absolute');
8784             this.maskEl.left.setStyle('z-index', zIndex);
8785             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8786             this.maskEl.left.setLeft(0);
8787             this.maskEl.left.setTop(box.y - this.padding);
8788             this.maskEl.left.show();
8789
8790             this.maskEl.bottom.setStyle('position', 'absolute');
8791             this.maskEl.bottom.setStyle('z-index', zIndex);
8792             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8793             this.maskEl.bottom.setLeft(0);
8794             this.maskEl.bottom.setTop(box.bottom + this.padding);
8795             this.maskEl.bottom.show();
8796
8797             this.maskEl.right.setStyle('position', 'absolute');
8798             this.maskEl.right.setStyle('z-index', zIndex);
8799             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8800             this.maskEl.right.setLeft(box.right + this.padding);
8801             this.maskEl.right.setTop(box.y - this.padding);
8802             this.maskEl.right.show();
8803
8804             this.toolTip.bindEl = this.target.el;
8805
8806             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8807
8808             var tip = this.target.blankText;
8809
8810             if(this.target.getValue() !== '' ) {
8811                 
8812                 if (this.target.invalidText.length) {
8813                     tip = this.target.invalidText;
8814                 } else if (this.target.regexText.length){
8815                     tip = this.target.regexText;
8816                 }
8817             }
8818
8819             this.toolTip.show(tip);
8820
8821             this.intervalID = window.setInterval(function() {
8822                 Roo.bootstrap.Form.popover.unmask();
8823             }, 10000);
8824
8825             window.onwheel = function(){ return false;};
8826             
8827             (function(){ this.isMasked = true; }).defer(500, this);
8828             
8829         },
8830         
8831         unmask : function()
8832         {
8833             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8834                 return;
8835             }
8836             
8837             this.maskEl.top.setStyle('position', 'absolute');
8838             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8839             this.maskEl.top.hide();
8840
8841             this.maskEl.left.setStyle('position', 'absolute');
8842             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8843             this.maskEl.left.hide();
8844
8845             this.maskEl.bottom.setStyle('position', 'absolute');
8846             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8847             this.maskEl.bottom.hide();
8848
8849             this.maskEl.right.setStyle('position', 'absolute');
8850             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8851             this.maskEl.right.hide();
8852             
8853             this.toolTip.hide();
8854             
8855             this.toolTip.el.hide();
8856             
8857             window.onwheel = function(){ return true;};
8858             
8859             if(this.intervalID){
8860                 window.clearInterval(this.intervalID);
8861                 this.intervalID = false;
8862             }
8863             
8864             this.isMasked = false;
8865             
8866         }
8867         
8868     }
8869     
8870 });
8871
8872 /*
8873  * Based on:
8874  * Ext JS Library 1.1.1
8875  * Copyright(c) 2006-2007, Ext JS, LLC.
8876  *
8877  * Originally Released Under LGPL - original licence link has changed is not relivant.
8878  *
8879  * Fork - LGPL
8880  * <script type="text/javascript">
8881  */
8882 /**
8883  * @class Roo.form.VTypes
8884  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8885  * @singleton
8886  */
8887 Roo.form.VTypes = function(){
8888     // closure these in so they are only created once.
8889     var alpha = /^[a-zA-Z_]+$/;
8890     var alphanum = /^[a-zA-Z0-9_]+$/;
8891     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8892     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8893
8894     // All these messages and functions are configurable
8895     return {
8896         /**
8897          * The function used to validate email addresses
8898          * @param {String} value The email address
8899          */
8900         'email' : function(v){
8901             return email.test(v);
8902         },
8903         /**
8904          * The error text to display when the email validation function returns false
8905          * @type String
8906          */
8907         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8908         /**
8909          * The keystroke filter mask to be applied on email input
8910          * @type RegExp
8911          */
8912         'emailMask' : /[a-z0-9_\.\-@]/i,
8913
8914         /**
8915          * The function used to validate URLs
8916          * @param {String} value The URL
8917          */
8918         'url' : function(v){
8919             return url.test(v);
8920         },
8921         /**
8922          * The error text to display when the url validation function returns false
8923          * @type String
8924          */
8925         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8926         
8927         /**
8928          * The function used to validate alpha values
8929          * @param {String} value The value
8930          */
8931         'alpha' : function(v){
8932             return alpha.test(v);
8933         },
8934         /**
8935          * The error text to display when the alpha validation function returns false
8936          * @type String
8937          */
8938         'alphaText' : 'This field should only contain letters and _',
8939         /**
8940          * The keystroke filter mask to be applied on alpha input
8941          * @type RegExp
8942          */
8943         'alphaMask' : /[a-z_]/i,
8944
8945         /**
8946          * The function used to validate alphanumeric values
8947          * @param {String} value The value
8948          */
8949         'alphanum' : function(v){
8950             return alphanum.test(v);
8951         },
8952         /**
8953          * The error text to display when the alphanumeric validation function returns false
8954          * @type String
8955          */
8956         'alphanumText' : 'This field should only contain letters, numbers and _',
8957         /**
8958          * The keystroke filter mask to be applied on alphanumeric input
8959          * @type RegExp
8960          */
8961         'alphanumMask' : /[a-z0-9_]/i
8962     };
8963 }();/*
8964  * - LGPL
8965  *
8966  * Input
8967  * 
8968  */
8969
8970 /**
8971  * @class Roo.bootstrap.Input
8972  * @extends Roo.bootstrap.Component
8973  * Bootstrap Input class
8974  * @cfg {Boolean} disabled is it disabled
8975  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8976  * @cfg {String} name name of the input
8977  * @cfg {string} fieldLabel - the label associated
8978  * @cfg {string} placeholder - placeholder to put in text.
8979  * @cfg {string}  before - input group add on before
8980  * @cfg {string} after - input group add on after
8981  * @cfg {string} size - (lg|sm) or leave empty..
8982  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8983  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8984  * @cfg {Number} md colspan out of 12 for computer-sized screens
8985  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8986  * @cfg {string} value default value of the input
8987  * @cfg {Number} labelWidth set the width of label 
8988  * @cfg {Number} labellg set the width of label (1-12)
8989  * @cfg {Number} labelmd set the width of label (1-12)
8990  * @cfg {Number} labelsm set the width of label (1-12)
8991  * @cfg {Number} labelxs set the width of label (1-12)
8992  * @cfg {String} labelAlign (top|left)
8993  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8994  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8995  * @cfg {String} indicatorpos (left|right) default left
8996  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8997  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8998
8999  * @cfg {String} align (left|center|right) Default left
9000  * @cfg {Boolean} forceFeedback (true|false) Default false
9001  * 
9002  * @constructor
9003  * Create a new Input
9004  * @param {Object} config The config object
9005  */
9006
9007 Roo.bootstrap.Input = function(config){
9008     
9009     Roo.bootstrap.Input.superclass.constructor.call(this, config);
9010     
9011     this.addEvents({
9012         /**
9013          * @event focus
9014          * Fires when this field receives input focus.
9015          * @param {Roo.form.Field} this
9016          */
9017         focus : true,
9018         /**
9019          * @event blur
9020          * Fires when this field loses input focus.
9021          * @param {Roo.form.Field} this
9022          */
9023         blur : true,
9024         /**
9025          * @event specialkey
9026          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
9027          * {@link Roo.EventObject#getKey} to determine which key was pressed.
9028          * @param {Roo.form.Field} this
9029          * @param {Roo.EventObject} e The event object
9030          */
9031         specialkey : true,
9032         /**
9033          * @event change
9034          * Fires just before the field blurs if the field value has changed.
9035          * @param {Roo.form.Field} this
9036          * @param {Mixed} newValue The new value
9037          * @param {Mixed} oldValue The original value
9038          */
9039         change : true,
9040         /**
9041          * @event invalid
9042          * Fires after the field has been marked as invalid.
9043          * @param {Roo.form.Field} this
9044          * @param {String} msg The validation message
9045          */
9046         invalid : true,
9047         /**
9048          * @event valid
9049          * Fires after the field has been validated with no errors.
9050          * @param {Roo.form.Field} this
9051          */
9052         valid : true,
9053          /**
9054          * @event keyup
9055          * Fires after the key up
9056          * @param {Roo.form.Field} this
9057          * @param {Roo.EventObject}  e The event Object
9058          */
9059         keyup : true
9060     });
9061 };
9062
9063 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
9064      /**
9065      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
9066       automatic validation (defaults to "keyup").
9067      */
9068     validationEvent : "keyup",
9069      /**
9070      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
9071      */
9072     validateOnBlur : true,
9073     /**
9074      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
9075      */
9076     validationDelay : 250,
9077      /**
9078      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
9079      */
9080     focusClass : "x-form-focus",  // not needed???
9081     
9082        
9083     /**
9084      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9085      */
9086     invalidClass : "has-warning",
9087     
9088     /**
9089      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9090      */
9091     validClass : "has-success",
9092     
9093     /**
9094      * @cfg {Boolean} hasFeedback (true|false) default true
9095      */
9096     hasFeedback : true,
9097     
9098     /**
9099      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9100      */
9101     invalidFeedbackClass : "glyphicon-warning-sign",
9102     
9103     /**
9104      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9105      */
9106     validFeedbackClass : "glyphicon-ok",
9107     
9108     /**
9109      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
9110      */
9111     selectOnFocus : false,
9112     
9113      /**
9114      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
9115      */
9116     maskRe : null,
9117        /**
9118      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
9119      */
9120     vtype : null,
9121     
9122       /**
9123      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
9124      */
9125     disableKeyFilter : false,
9126     
9127        /**
9128      * @cfg {Boolean} disabled True to disable the field (defaults to false).
9129      */
9130     disabled : false,
9131      /**
9132      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
9133      */
9134     allowBlank : true,
9135     /**
9136      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9137      */
9138     blankText : "Please complete this mandatory field",
9139     
9140      /**
9141      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9142      */
9143     minLength : 0,
9144     /**
9145      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9146      */
9147     maxLength : Number.MAX_VALUE,
9148     /**
9149      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9150      */
9151     minLengthText : "The minimum length for this field is {0}",
9152     /**
9153      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9154      */
9155     maxLengthText : "The maximum length for this field is {0}",
9156   
9157     
9158     /**
9159      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9160      * If available, this function will be called only after the basic validators all return true, and will be passed the
9161      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9162      */
9163     validator : null,
9164     /**
9165      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9166      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9167      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
9168      */
9169     regex : null,
9170     /**
9171      * @cfg {String} regexText -- Depricated - use Invalid Text
9172      */
9173     regexText : "",
9174     
9175     /**
9176      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9177      */
9178     invalidText : "",
9179     
9180     
9181     
9182     autocomplete: false,
9183     
9184     
9185     fieldLabel : '',
9186     inputType : 'text',
9187     
9188     name : false,
9189     placeholder: false,
9190     before : false,
9191     after : false,
9192     size : false,
9193     hasFocus : false,
9194     preventMark: false,
9195     isFormField : true,
9196     value : '',
9197     labelWidth : 2,
9198     labelAlign : false,
9199     readOnly : false,
9200     align : false,
9201     formatedValue : false,
9202     forceFeedback : false,
9203     
9204     indicatorpos : 'left',
9205     
9206     labellg : 0,
9207     labelmd : 0,
9208     labelsm : 0,
9209     labelxs : 0,
9210     
9211     capture : '',
9212     accept : '',
9213     
9214     parentLabelAlign : function()
9215     {
9216         var parent = this;
9217         while (parent.parent()) {
9218             parent = parent.parent();
9219             if (typeof(parent.labelAlign) !='undefined') {
9220                 return parent.labelAlign;
9221             }
9222         }
9223         return 'left';
9224         
9225     },
9226     
9227     getAutoCreate : function()
9228     {
9229         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9230         
9231         var id = Roo.id();
9232         
9233         var cfg = {};
9234         
9235         if(this.inputType != 'hidden'){
9236             cfg.cls = 'form-group' //input-group
9237         }
9238         
9239         var input =  {
9240             tag: 'input',
9241             id : id,
9242             type : this.inputType,
9243             value : this.value,
9244             cls : 'form-control',
9245             placeholder : this.placeholder || '',
9246             autocomplete : this.autocomplete || 'new-password'
9247         };
9248         
9249         if(this.capture.length){
9250             input.capture = this.capture;
9251         }
9252         
9253         if(this.accept.length){
9254             input.accept = this.accept + "/*";
9255         }
9256         
9257         if(this.align){
9258             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9259         }
9260         
9261         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9262             input.maxLength = this.maxLength;
9263         }
9264         
9265         if (this.disabled) {
9266             input.disabled=true;
9267         }
9268         
9269         if (this.readOnly) {
9270             input.readonly=true;
9271         }
9272         
9273         if (this.name) {
9274             input.name = this.name;
9275         }
9276         
9277         if (this.size) {
9278             input.cls += ' input-' + this.size;
9279         }
9280         
9281         var settings=this;
9282         ['xs','sm','md','lg'].map(function(size){
9283             if (settings[size]) {
9284                 cfg.cls += ' col-' + size + '-' + settings[size];
9285             }
9286         });
9287         
9288         var inputblock = input;
9289         
9290         var feedback = {
9291             tag: 'span',
9292             cls: 'glyphicon form-control-feedback'
9293         };
9294             
9295         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9296             
9297             inputblock = {
9298                 cls : 'has-feedback',
9299                 cn :  [
9300                     input,
9301                     feedback
9302                 ] 
9303             };  
9304         }
9305         
9306         if (this.before || this.after) {
9307             
9308             inputblock = {
9309                 cls : 'input-group',
9310                 cn :  [] 
9311             };
9312             
9313             if (this.before && typeof(this.before) == 'string') {
9314                 
9315                 inputblock.cn.push({
9316                     tag :'span',
9317                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9318                     html : this.before
9319                 });
9320             }
9321             if (this.before && typeof(this.before) == 'object') {
9322                 this.before = Roo.factory(this.before);
9323                 
9324                 inputblock.cn.push({
9325                     tag :'span',
9326                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9327                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9328                 });
9329             }
9330             
9331             inputblock.cn.push(input);
9332             
9333             if (this.after && typeof(this.after) == 'string') {
9334                 inputblock.cn.push({
9335                     tag :'span',
9336                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9337                     html : this.after
9338                 });
9339             }
9340             if (this.after && typeof(this.after) == 'object') {
9341                 this.after = Roo.factory(this.after);
9342                 
9343                 inputblock.cn.push({
9344                     tag :'span',
9345                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
9346                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9347                 });
9348             }
9349             
9350             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9351                 inputblock.cls += ' has-feedback';
9352                 inputblock.cn.push(feedback);
9353             }
9354         };
9355         var indicator = {
9356             tag : 'i',
9357             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9358             tooltip : 'This field is required'
9359         };
9360         if (Roo.bootstrap.version == 4) {
9361             indicator = {
9362                 tag : 'i',
9363                 style : 'display-none'
9364             };
9365         }
9366         if (align ==='left' && this.fieldLabel.length) {
9367             
9368             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
9369             
9370             cfg.cn = [
9371                 indicator,
9372                 {
9373                     tag: 'label',
9374                     'for' :  id,
9375                     cls : 'control-label col-form-label',
9376                     html : this.fieldLabel
9377
9378                 },
9379                 {
9380                     cls : "", 
9381                     cn: [
9382                         inputblock
9383                     ]
9384                 }
9385             ];
9386             
9387             var labelCfg = cfg.cn[1];
9388             var contentCfg = cfg.cn[2];
9389             
9390             if(this.indicatorpos == 'right'){
9391                 cfg.cn = [
9392                     {
9393                         tag: 'label',
9394                         'for' :  id,
9395                         cls : 'control-label col-form-label',
9396                         cn : [
9397                             {
9398                                 tag : 'span',
9399                                 html : this.fieldLabel
9400                             },
9401                             indicator
9402                         ]
9403                     },
9404                     {
9405                         cls : "",
9406                         cn: [
9407                             inputblock
9408                         ]
9409                     }
9410
9411                 ];
9412                 
9413                 labelCfg = cfg.cn[0];
9414                 contentCfg = cfg.cn[1];
9415             
9416             }
9417             
9418             if(this.labelWidth > 12){
9419                 labelCfg.style = "width: " + this.labelWidth + 'px';
9420             }
9421             
9422             if(this.labelWidth < 13 && this.labelmd == 0){
9423                 this.labelmd = this.labelWidth;
9424             }
9425             
9426             if(this.labellg > 0){
9427                 labelCfg.cls += ' col-lg-' + this.labellg;
9428                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9429             }
9430             
9431             if(this.labelmd > 0){
9432                 labelCfg.cls += ' col-md-' + this.labelmd;
9433                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9434             }
9435             
9436             if(this.labelsm > 0){
9437                 labelCfg.cls += ' col-sm-' + this.labelsm;
9438                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9439             }
9440             
9441             if(this.labelxs > 0){
9442                 labelCfg.cls += ' col-xs-' + this.labelxs;
9443                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9444             }
9445             
9446             
9447         } else if ( this.fieldLabel.length) {
9448                 
9449             cfg.cn = [
9450                 {
9451                     tag : 'i',
9452                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9453                     tooltip : 'This field is required'
9454                 },
9455                 {
9456                     tag: 'label',
9457                    //cls : 'input-group-addon',
9458                     html : this.fieldLabel
9459
9460                 },
9461
9462                inputblock
9463
9464            ];
9465            
9466            if(this.indicatorpos == 'right'){
9467                 
9468                 cfg.cn = [
9469                     {
9470                         tag: 'label',
9471                        //cls : 'input-group-addon',
9472                         html : this.fieldLabel
9473
9474                     },
9475                     {
9476                         tag : 'i',
9477                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9478                         tooltip : 'This field is required'
9479                     },
9480
9481                    inputblock
9482
9483                ];
9484
9485             }
9486
9487         } else {
9488             
9489             cfg.cn = [
9490
9491                     inputblock
9492
9493             ];
9494                 
9495                 
9496         };
9497         
9498         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9499            cfg.cls += ' navbar-form';
9500         }
9501         
9502         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
9503             // on BS4 we do this only if not form 
9504             cfg.cls += ' navbar-form';
9505             cfg.tag = 'li';
9506         }
9507         
9508         return cfg;
9509         
9510     },
9511     /**
9512      * return the real input element.
9513      */
9514     inputEl: function ()
9515     {
9516         return this.el.select('input.form-control',true).first();
9517     },
9518     
9519     tooltipEl : function()
9520     {
9521         return this.inputEl();
9522     },
9523     
9524     indicatorEl : function()
9525     {
9526         if (Roo.bootstrap.version == 4) {
9527             return false; // not enabled in v4 yet.
9528         }
9529         
9530         var indicator = this.el.select('i.roo-required-indicator',true).first();
9531         
9532         if(!indicator){
9533             return false;
9534         }
9535         
9536         return indicator;
9537         
9538     },
9539     
9540     setDisabled : function(v)
9541     {
9542         var i  = this.inputEl().dom;
9543         if (!v) {
9544             i.removeAttribute('disabled');
9545             return;
9546             
9547         }
9548         i.setAttribute('disabled','true');
9549     },
9550     initEvents : function()
9551     {
9552           
9553         this.inputEl().on("keydown" , this.fireKey,  this);
9554         this.inputEl().on("focus", this.onFocus,  this);
9555         this.inputEl().on("blur", this.onBlur,  this);
9556         
9557         this.inputEl().relayEvent('keyup', this);
9558         
9559         this.indicator = this.indicatorEl();
9560         
9561         if(this.indicator){
9562             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9563         }
9564  
9565         // reference to original value for reset
9566         this.originalValue = this.getValue();
9567         //Roo.form.TextField.superclass.initEvents.call(this);
9568         if(this.validationEvent == 'keyup'){
9569             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9570             this.inputEl().on('keyup', this.filterValidation, this);
9571         }
9572         else if(this.validationEvent !== false){
9573             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9574         }
9575         
9576         if(this.selectOnFocus){
9577             this.on("focus", this.preFocus, this);
9578             
9579         }
9580         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9581             this.inputEl().on("keypress", this.filterKeys, this);
9582         } else {
9583             this.inputEl().relayEvent('keypress', this);
9584         }
9585        /* if(this.grow){
9586             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9587             this.el.on("click", this.autoSize,  this);
9588         }
9589         */
9590         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9591             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9592         }
9593         
9594         if (typeof(this.before) == 'object') {
9595             this.before.render(this.el.select('.roo-input-before',true).first());
9596         }
9597         if (typeof(this.after) == 'object') {
9598             this.after.render(this.el.select('.roo-input-after',true).first());
9599         }
9600         
9601         this.inputEl().on('change', this.onChange, this);
9602         
9603     },
9604     filterValidation : function(e){
9605         if(!e.isNavKeyPress()){
9606             this.validationTask.delay(this.validationDelay);
9607         }
9608     },
9609      /**
9610      * Validates the field value
9611      * @return {Boolean} True if the value is valid, else false
9612      */
9613     validate : function(){
9614         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9615         if(this.disabled || this.validateValue(this.getRawValue())){
9616             this.markValid();
9617             return true;
9618         }
9619         
9620         this.markInvalid();
9621         return false;
9622     },
9623     
9624     
9625     /**
9626      * Validates a value according to the field's validation rules and marks the field as invalid
9627      * if the validation fails
9628      * @param {Mixed} value The value to validate
9629      * @return {Boolean} True if the value is valid, else false
9630      */
9631     validateValue : function(value)
9632     {
9633         if(this.getVisibilityEl().hasClass('hidden')){
9634             return true;
9635         }
9636         
9637         if(value.length < 1)  { // if it's blank
9638             if(this.allowBlank){
9639                 return true;
9640             }
9641             return false;
9642         }
9643         
9644         if(value.length < this.minLength){
9645             return false;
9646         }
9647         if(value.length > this.maxLength){
9648             return false;
9649         }
9650         if(this.vtype){
9651             var vt = Roo.form.VTypes;
9652             if(!vt[this.vtype](value, this)){
9653                 return false;
9654             }
9655         }
9656         if(typeof this.validator == "function"){
9657             var msg = this.validator(value);
9658             if(msg !== true){
9659                 return false;
9660             }
9661             if (typeof(msg) == 'string') {
9662                 this.invalidText = msg;
9663             }
9664         }
9665         
9666         if(this.regex && !this.regex.test(value)){
9667             return false;
9668         }
9669         
9670         return true;
9671     },
9672     
9673      // private
9674     fireKey : function(e){
9675         //Roo.log('field ' + e.getKey());
9676         if(e.isNavKeyPress()){
9677             this.fireEvent("specialkey", this, e);
9678         }
9679     },
9680     focus : function (selectText){
9681         if(this.rendered){
9682             this.inputEl().focus();
9683             if(selectText === true){
9684                 this.inputEl().dom.select();
9685             }
9686         }
9687         return this;
9688     } ,
9689     
9690     onFocus : function(){
9691         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9692            // this.el.addClass(this.focusClass);
9693         }
9694         if(!this.hasFocus){
9695             this.hasFocus = true;
9696             this.startValue = this.getValue();
9697             this.fireEvent("focus", this);
9698         }
9699     },
9700     
9701     beforeBlur : Roo.emptyFn,
9702
9703     
9704     // private
9705     onBlur : function(){
9706         this.beforeBlur();
9707         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9708             //this.el.removeClass(this.focusClass);
9709         }
9710         this.hasFocus = false;
9711         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9712             this.validate();
9713         }
9714         var v = this.getValue();
9715         if(String(v) !== String(this.startValue)){
9716             this.fireEvent('change', this, v, this.startValue);
9717         }
9718         this.fireEvent("blur", this);
9719     },
9720     
9721     onChange : function(e)
9722     {
9723         var v = this.getValue();
9724         if(String(v) !== String(this.startValue)){
9725             this.fireEvent('change', this, v, this.startValue);
9726         }
9727         
9728     },
9729     
9730     /**
9731      * Resets the current field value to the originally loaded value and clears any validation messages
9732      */
9733     reset : function(){
9734         this.setValue(this.originalValue);
9735         this.validate();
9736     },
9737      /**
9738      * Returns the name of the field
9739      * @return {Mixed} name The name field
9740      */
9741     getName: function(){
9742         return this.name;
9743     },
9744      /**
9745      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9746      * @return {Mixed} value The field value
9747      */
9748     getValue : function(){
9749         
9750         var v = this.inputEl().getValue();
9751         
9752         return v;
9753     },
9754     /**
9755      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9756      * @return {Mixed} value The field value
9757      */
9758     getRawValue : function(){
9759         var v = this.inputEl().getValue();
9760         
9761         return v;
9762     },
9763     
9764     /**
9765      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9766      * @param {Mixed} value The value to set
9767      */
9768     setRawValue : function(v){
9769         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9770     },
9771     
9772     selectText : function(start, end){
9773         var v = this.getRawValue();
9774         if(v.length > 0){
9775             start = start === undefined ? 0 : start;
9776             end = end === undefined ? v.length : end;
9777             var d = this.inputEl().dom;
9778             if(d.setSelectionRange){
9779                 d.setSelectionRange(start, end);
9780             }else if(d.createTextRange){
9781                 var range = d.createTextRange();
9782                 range.moveStart("character", start);
9783                 range.moveEnd("character", v.length-end);
9784                 range.select();
9785             }
9786         }
9787     },
9788     
9789     /**
9790      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9791      * @param {Mixed} value The value to set
9792      */
9793     setValue : function(v){
9794         this.value = v;
9795         if(this.rendered){
9796             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9797             this.validate();
9798         }
9799     },
9800     
9801     /*
9802     processValue : function(value){
9803         if(this.stripCharsRe){
9804             var newValue = value.replace(this.stripCharsRe, '');
9805             if(newValue !== value){
9806                 this.setRawValue(newValue);
9807                 return newValue;
9808             }
9809         }
9810         return value;
9811     },
9812   */
9813     preFocus : function(){
9814         
9815         if(this.selectOnFocus){
9816             this.inputEl().dom.select();
9817         }
9818     },
9819     filterKeys : function(e){
9820         var k = e.getKey();
9821         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9822             return;
9823         }
9824         var c = e.getCharCode(), cc = String.fromCharCode(c);
9825         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9826             return;
9827         }
9828         if(!this.maskRe.test(cc)){
9829             e.stopEvent();
9830         }
9831     },
9832      /**
9833      * Clear any invalid styles/messages for this field
9834      */
9835     clearInvalid : function(){
9836         
9837         if(!this.el || this.preventMark){ // not rendered
9838             return;
9839         }
9840         
9841         
9842         this.el.removeClass([this.invalidClass, 'is-invalid']);
9843         
9844         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9845             
9846             var feedback = this.el.select('.form-control-feedback', true).first();
9847             
9848             if(feedback){
9849                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9850             }
9851             
9852         }
9853         
9854         if(this.indicator){
9855             this.indicator.removeClass('visible');
9856             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9857         }
9858         
9859         this.fireEvent('valid', this);
9860     },
9861     
9862      /**
9863      * Mark this field as valid
9864      */
9865     markValid : function()
9866     {
9867         if(!this.el  || this.preventMark){ // not rendered...
9868             return;
9869         }
9870         
9871         this.el.removeClass([this.invalidClass, this.validClass]);
9872         this.inputEl().removeClass(['is-valid', 'is-invalid']);
9873
9874         var feedback = this.el.select('.form-control-feedback', true).first();
9875             
9876         if(feedback){
9877             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9878         }
9879         
9880         if(this.indicator){
9881             this.indicator.removeClass('visible');
9882             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9883         }
9884         
9885         if(this.disabled){
9886             return;
9887         }
9888         
9889         if(this.allowBlank && !this.getRawValue().length){
9890             return;
9891         }
9892         if (Roo.bootstrap.version == 3) {
9893             this.el.addClass(this.validClass);
9894         } else {
9895             this.inputEl().addClass('is-valid');
9896         }
9897
9898         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9899             
9900             var feedback = this.el.select('.form-control-feedback', true).first();
9901             
9902             if(feedback){
9903                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9904                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9905             }
9906             
9907         }
9908         
9909         this.fireEvent('valid', this);
9910     },
9911     
9912      /**
9913      * Mark this field as invalid
9914      * @param {String} msg The validation message
9915      */
9916     markInvalid : function(msg)
9917     {
9918         if(!this.el  || this.preventMark){ // not rendered
9919             return;
9920         }
9921         
9922         this.el.removeClass([this.invalidClass, this.validClass]);
9923         this.inputEl().removeClass(['is-valid', 'is-invalid']);
9924         
9925         var feedback = this.el.select('.form-control-feedback', true).first();
9926             
9927         if(feedback){
9928             this.el.select('.form-control-feedback', true).first().removeClass(
9929                     [this.invalidFeedbackClass, this.validFeedbackClass]);
9930         }
9931
9932         if(this.disabled){
9933             return;
9934         }
9935         
9936         if(this.allowBlank && !this.getRawValue().length){
9937             return;
9938         }
9939         
9940         if(this.indicator){
9941             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9942             this.indicator.addClass('visible');
9943         }
9944         if (Roo.bootstrap.version == 3) {
9945             this.el.addClass(this.invalidClass);
9946         } else {
9947             this.inputEl().addClass('is-invalid');
9948         }
9949         
9950         
9951         
9952         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9953             
9954             var feedback = this.el.select('.form-control-feedback', true).first();
9955             
9956             if(feedback){
9957                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9958                 
9959                 if(this.getValue().length || this.forceFeedback){
9960                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9961                 }
9962                 
9963             }
9964             
9965         }
9966         
9967         this.fireEvent('invalid', this, msg);
9968     },
9969     // private
9970     SafariOnKeyDown : function(event)
9971     {
9972         // this is a workaround for a password hang bug on chrome/ webkit.
9973         if (this.inputEl().dom.type != 'password') {
9974             return;
9975         }
9976         
9977         var isSelectAll = false;
9978         
9979         if(this.inputEl().dom.selectionEnd > 0){
9980             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9981         }
9982         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9983             event.preventDefault();
9984             this.setValue('');
9985             return;
9986         }
9987         
9988         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9989             
9990             event.preventDefault();
9991             // this is very hacky as keydown always get's upper case.
9992             //
9993             var cc = String.fromCharCode(event.getCharCode());
9994             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9995             
9996         }
9997     },
9998     adjustWidth : function(tag, w){
9999         tag = tag.toLowerCase();
10000         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
10001             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
10002                 if(tag == 'input'){
10003                     return w + 2;
10004                 }
10005                 if(tag == 'textarea'){
10006                     return w-2;
10007                 }
10008             }else if(Roo.isOpera){
10009                 if(tag == 'input'){
10010                     return w + 2;
10011                 }
10012                 if(tag == 'textarea'){
10013                     return w-2;
10014                 }
10015             }
10016         }
10017         return w;
10018     },
10019     
10020     setFieldLabel : function(v)
10021     {
10022         if(!this.rendered){
10023             return;
10024         }
10025         
10026         if(this.indicatorEl()){
10027             var ar = this.el.select('label > span',true);
10028             
10029             if (ar.elements.length) {
10030                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10031                 this.fieldLabel = v;
10032                 return;
10033             }
10034             
10035             var br = this.el.select('label',true);
10036             
10037             if(br.elements.length) {
10038                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10039                 this.fieldLabel = v;
10040                 return;
10041             }
10042             
10043             Roo.log('Cannot Found any of label > span || label in input');
10044             return;
10045         }
10046         
10047         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10048         this.fieldLabel = v;
10049         
10050         
10051     }
10052 });
10053
10054  
10055 /*
10056  * - LGPL
10057  *
10058  * Input
10059  * 
10060  */
10061
10062 /**
10063  * @class Roo.bootstrap.TextArea
10064  * @extends Roo.bootstrap.Input
10065  * Bootstrap TextArea class
10066  * @cfg {Number} cols Specifies the visible width of a text area
10067  * @cfg {Number} rows Specifies the visible number of lines in a text area
10068  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
10069  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
10070  * @cfg {string} html text
10071  * 
10072  * @constructor
10073  * Create a new TextArea
10074  * @param {Object} config The config object
10075  */
10076
10077 Roo.bootstrap.TextArea = function(config){
10078     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
10079    
10080 };
10081
10082 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
10083      
10084     cols : false,
10085     rows : 5,
10086     readOnly : false,
10087     warp : 'soft',
10088     resize : false,
10089     value: false,
10090     html: false,
10091     
10092     getAutoCreate : function(){
10093         
10094         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10095         
10096         var id = Roo.id();
10097         
10098         var cfg = {};
10099         
10100         if(this.inputType != 'hidden'){
10101             cfg.cls = 'form-group' //input-group
10102         }
10103         
10104         var input =  {
10105             tag: 'textarea',
10106             id : id,
10107             warp : this.warp,
10108             rows : this.rows,
10109             value : this.value || '',
10110             html: this.html || '',
10111             cls : 'form-control',
10112             placeholder : this.placeholder || '' 
10113             
10114         };
10115         
10116         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10117             input.maxLength = this.maxLength;
10118         }
10119         
10120         if(this.resize){
10121             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
10122         }
10123         
10124         if(this.cols){
10125             input.cols = this.cols;
10126         }
10127         
10128         if (this.readOnly) {
10129             input.readonly = true;
10130         }
10131         
10132         if (this.name) {
10133             input.name = this.name;
10134         }
10135         
10136         if (this.size) {
10137             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
10138         }
10139         
10140         var settings=this;
10141         ['xs','sm','md','lg'].map(function(size){
10142             if (settings[size]) {
10143                 cfg.cls += ' col-' + size + '-' + settings[size];
10144             }
10145         });
10146         
10147         var inputblock = input;
10148         
10149         if(this.hasFeedback && !this.allowBlank){
10150             
10151             var feedback = {
10152                 tag: 'span',
10153                 cls: 'glyphicon form-control-feedback'
10154             };
10155
10156             inputblock = {
10157                 cls : 'has-feedback',
10158                 cn :  [
10159                     input,
10160                     feedback
10161                 ] 
10162             };  
10163         }
10164         
10165         
10166         if (this.before || this.after) {
10167             
10168             inputblock = {
10169                 cls : 'input-group',
10170                 cn :  [] 
10171             };
10172             if (this.before) {
10173                 inputblock.cn.push({
10174                     tag :'span',
10175                     cls : 'input-group-addon',
10176                     html : this.before
10177                 });
10178             }
10179             
10180             inputblock.cn.push(input);
10181             
10182             if(this.hasFeedback && !this.allowBlank){
10183                 inputblock.cls += ' has-feedback';
10184                 inputblock.cn.push(feedback);
10185             }
10186             
10187             if (this.after) {
10188                 inputblock.cn.push({
10189                     tag :'span',
10190                     cls : 'input-group-addon',
10191                     html : this.after
10192                 });
10193             }
10194             
10195         }
10196         
10197         if (align ==='left' && this.fieldLabel.length) {
10198             cfg.cn = [
10199                 {
10200                     tag: 'label',
10201                     'for' :  id,
10202                     cls : 'control-label',
10203                     html : this.fieldLabel
10204                 },
10205                 {
10206                     cls : "",
10207                     cn: [
10208                         inputblock
10209                     ]
10210                 }
10211
10212             ];
10213             
10214             if(this.labelWidth > 12){
10215                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10216             }
10217
10218             if(this.labelWidth < 13 && this.labelmd == 0){
10219                 this.labelmd = this.labelWidth;
10220             }
10221
10222             if(this.labellg > 0){
10223                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10224                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10225             }
10226
10227             if(this.labelmd > 0){
10228                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10229                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10230             }
10231
10232             if(this.labelsm > 0){
10233                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10234                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10235             }
10236
10237             if(this.labelxs > 0){
10238                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10239                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10240             }
10241             
10242         } else if ( this.fieldLabel.length) {
10243             cfg.cn = [
10244
10245                {
10246                    tag: 'label',
10247                    //cls : 'input-group-addon',
10248                    html : this.fieldLabel
10249
10250                },
10251
10252                inputblock
10253
10254            ];
10255
10256         } else {
10257
10258             cfg.cn = [
10259
10260                 inputblock
10261
10262             ];
10263                 
10264         }
10265         
10266         if (this.disabled) {
10267             input.disabled=true;
10268         }
10269         
10270         return cfg;
10271         
10272     },
10273     /**
10274      * return the real textarea element.
10275      */
10276     inputEl: function ()
10277     {
10278         return this.el.select('textarea.form-control',true).first();
10279     },
10280     
10281     /**
10282      * Clear any invalid styles/messages for this field
10283      */
10284     clearInvalid : function()
10285     {
10286         
10287         if(!this.el || this.preventMark){ // not rendered
10288             return;
10289         }
10290         
10291         var label = this.el.select('label', true).first();
10292         var icon = this.el.select('i.fa-star', true).first();
10293         
10294         if(label && icon){
10295             icon.remove();
10296         }
10297         this.el.removeClass( this.validClass);
10298         this.inputEl().removeClass('is-invalid');
10299          
10300         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10301             
10302             var feedback = this.el.select('.form-control-feedback', true).first();
10303             
10304             if(feedback){
10305                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10306             }
10307             
10308         }
10309         
10310         this.fireEvent('valid', this);
10311     },
10312     
10313      /**
10314      * Mark this field as valid
10315      */
10316     markValid : function()
10317     {
10318         if(!this.el  || this.preventMark){ // not rendered
10319             return;
10320         }
10321         
10322         this.el.removeClass([this.invalidClass, this.validClass]);
10323         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10324         
10325         var feedback = this.el.select('.form-control-feedback', true).first();
10326             
10327         if(feedback){
10328             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10329         }
10330
10331         if(this.disabled || this.allowBlank){
10332             return;
10333         }
10334         
10335         var label = this.el.select('label', true).first();
10336         var icon = this.el.select('i.fa-star', true).first();
10337         
10338         if(label && icon){
10339             icon.remove();
10340         }
10341         if (Roo.bootstrap.version == 3) {
10342             this.el.addClass(this.validClass);
10343         } else {
10344             this.inputEl().addClass('is-valid');
10345         }
10346         
10347         
10348         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10349             
10350             var feedback = this.el.select('.form-control-feedback', true).first();
10351             
10352             if(feedback){
10353                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10354                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10355             }
10356             
10357         }
10358         
10359         this.fireEvent('valid', this);
10360     },
10361     
10362      /**
10363      * Mark this field as invalid
10364      * @param {String} msg The validation message
10365      */
10366     markInvalid : function(msg)
10367     {
10368         if(!this.el  || this.preventMark){ // not rendered
10369             return;
10370         }
10371         
10372         this.el.removeClass([this.invalidClass, this.validClass]);
10373         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10374         
10375         var feedback = this.el.select('.form-control-feedback', true).first();
10376             
10377         if(feedback){
10378             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10379         }
10380
10381         if(this.disabled || this.allowBlank){
10382             return;
10383         }
10384         
10385         var label = this.el.select('label', true).first();
10386         var icon = this.el.select('i.fa-star', true).first();
10387         
10388         if(!this.getValue().length && label && !icon){
10389             this.el.createChild({
10390                 tag : 'i',
10391                 cls : 'text-danger fa fa-lg fa-star',
10392                 tooltip : 'This field is required',
10393                 style : 'margin-right:5px;'
10394             }, label, true);
10395         }
10396         
10397         if (Roo.bootstrap.version == 3) {
10398             this.el.addClass(this.invalidClass);
10399         } else {
10400             this.inputEl().addClass('is-invalid');
10401         }
10402         
10403         // fixme ... this may be depricated need to test..
10404         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10405             
10406             var feedback = this.el.select('.form-control-feedback', true).first();
10407             
10408             if(feedback){
10409                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10410                 
10411                 if(this.getValue().length || this.forceFeedback){
10412                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10413                 }
10414                 
10415             }
10416             
10417         }
10418         
10419         this.fireEvent('invalid', this, msg);
10420     }
10421 });
10422
10423  
10424 /*
10425  * - LGPL
10426  *
10427  * trigger field - base class for combo..
10428  * 
10429  */
10430  
10431 /**
10432  * @class Roo.bootstrap.TriggerField
10433  * @extends Roo.bootstrap.Input
10434  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10435  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10436  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10437  * for which you can provide a custom implementation.  For example:
10438  * <pre><code>
10439 var trigger = new Roo.bootstrap.TriggerField();
10440 trigger.onTriggerClick = myTriggerFn;
10441 trigger.applyTo('my-field');
10442 </code></pre>
10443  *
10444  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10445  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10446  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10447  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10448  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10449
10450  * @constructor
10451  * Create a new TriggerField.
10452  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10453  * to the base TextField)
10454  */
10455 Roo.bootstrap.TriggerField = function(config){
10456     this.mimicing = false;
10457     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10458 };
10459
10460 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10461     /**
10462      * @cfg {String} triggerClass A CSS class to apply to the trigger
10463      */
10464      /**
10465      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10466      */
10467     hideTrigger:false,
10468
10469     /**
10470      * @cfg {Boolean} removable (true|false) special filter default false
10471      */
10472     removable : false,
10473     
10474     /** @cfg {Boolean} grow @hide */
10475     /** @cfg {Number} growMin @hide */
10476     /** @cfg {Number} growMax @hide */
10477
10478     /**
10479      * @hide 
10480      * @method
10481      */
10482     autoSize: Roo.emptyFn,
10483     // private
10484     monitorTab : true,
10485     // private
10486     deferHeight : true,
10487
10488     
10489     actionMode : 'wrap',
10490     
10491     caret : false,
10492     
10493     
10494     getAutoCreate : function(){
10495        
10496         var align = this.labelAlign || this.parentLabelAlign();
10497         
10498         var id = Roo.id();
10499         
10500         var cfg = {
10501             cls: 'form-group' //input-group
10502         };
10503         
10504         
10505         var input =  {
10506             tag: 'input',
10507             id : id,
10508             type : this.inputType,
10509             cls : 'form-control',
10510             autocomplete: 'new-password',
10511             placeholder : this.placeholder || '' 
10512             
10513         };
10514         if (this.name) {
10515             input.name = this.name;
10516         }
10517         if (this.size) {
10518             input.cls += ' input-' + this.size;
10519         }
10520         
10521         if (this.disabled) {
10522             input.disabled=true;
10523         }
10524         
10525         var inputblock = input;
10526         
10527         if(this.hasFeedback && !this.allowBlank){
10528             
10529             var feedback = {
10530                 tag: 'span',
10531                 cls: 'glyphicon form-control-feedback'
10532             };
10533             
10534             if(this.removable && !this.editable && !this.tickable){
10535                 inputblock = {
10536                     cls : 'has-feedback',
10537                     cn :  [
10538                         inputblock,
10539                         {
10540                             tag: 'button',
10541                             html : 'x',
10542                             cls : 'roo-combo-removable-btn close'
10543                         },
10544                         feedback
10545                     ] 
10546                 };
10547             } else {
10548                 inputblock = {
10549                     cls : 'has-feedback',
10550                     cn :  [
10551                         inputblock,
10552                         feedback
10553                     ] 
10554                 };
10555             }
10556
10557         } else {
10558             if(this.removable && !this.editable && !this.tickable){
10559                 inputblock = {
10560                     cls : 'roo-removable',
10561                     cn :  [
10562                         inputblock,
10563                         {
10564                             tag: 'button',
10565                             html : 'x',
10566                             cls : 'roo-combo-removable-btn close'
10567                         }
10568                     ] 
10569                 };
10570             }
10571         }
10572         
10573         if (this.before || this.after) {
10574             
10575             inputblock = {
10576                 cls : 'input-group',
10577                 cn :  [] 
10578             };
10579             if (this.before) {
10580                 inputblock.cn.push({
10581                     tag :'span',
10582                     cls : 'input-group-addon input-group-prepend input-group-text',
10583                     html : this.before
10584                 });
10585             }
10586             
10587             inputblock.cn.push(input);
10588             
10589             if(this.hasFeedback && !this.allowBlank){
10590                 inputblock.cls += ' has-feedback';
10591                 inputblock.cn.push(feedback);
10592             }
10593             
10594             if (this.after) {
10595                 inputblock.cn.push({
10596                     tag :'span',
10597                     cls : 'input-group-addon input-group-append input-group-text',
10598                     html : this.after
10599                 });
10600             }
10601             
10602         };
10603         
10604       
10605         
10606         var ibwrap = inputblock;
10607         
10608         if(this.multiple){
10609             ibwrap = {
10610                 tag: 'ul',
10611                 cls: 'roo-select2-choices',
10612                 cn:[
10613                     {
10614                         tag: 'li',
10615                         cls: 'roo-select2-search-field',
10616                         cn: [
10617
10618                             inputblock
10619                         ]
10620                     }
10621                 ]
10622             };
10623                 
10624         }
10625         
10626         var combobox = {
10627             cls: 'roo-select2-container input-group',
10628             cn: [
10629                  {
10630                     tag: 'input',
10631                     type : 'hidden',
10632                     cls: 'form-hidden-field'
10633                 },
10634                 ibwrap
10635             ]
10636         };
10637         
10638         if(!this.multiple && this.showToggleBtn){
10639             
10640             var caret = {
10641                         tag: 'span',
10642                         cls: 'caret'
10643              };
10644             if (this.caret != false) {
10645                 caret = {
10646                      tag: 'i',
10647                      cls: 'fa fa-' + this.caret
10648                 };
10649                 
10650             }
10651             
10652             combobox.cn.push({
10653                 tag :'span',
10654                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10655                 cn : [
10656                     caret,
10657                     {
10658                         tag: 'span',
10659                         cls: 'combobox-clear',
10660                         cn  : [
10661                             {
10662                                 tag : 'i',
10663                                 cls: 'icon-remove'
10664                             }
10665                         ]
10666                     }
10667                 ]
10668
10669             })
10670         }
10671         
10672         if(this.multiple){
10673             combobox.cls += ' roo-select2-container-multi';
10674         }
10675          var indicator = {
10676             tag : 'i',
10677             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10678             tooltip : 'This field is required'
10679         };
10680         if (Roo.bootstrap.version == 4) {
10681             indicator = {
10682                 tag : 'i',
10683                 style : 'display:none'
10684             };
10685         }
10686         
10687         
10688         if (align ==='left' && this.fieldLabel.length) {
10689             
10690             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10691
10692             cfg.cn = [
10693                 indicator,
10694                 {
10695                     tag: 'label',
10696                     'for' :  id,
10697                     cls : 'control-label',
10698                     html : this.fieldLabel
10699
10700                 },
10701                 {
10702                     cls : "", 
10703                     cn: [
10704                         combobox
10705                     ]
10706                 }
10707
10708             ];
10709             
10710             var labelCfg = cfg.cn[1];
10711             var contentCfg = cfg.cn[2];
10712             
10713             if(this.indicatorpos == 'right'){
10714                 cfg.cn = [
10715                     {
10716                         tag: 'label',
10717                         'for' :  id,
10718                         cls : 'control-label',
10719                         cn : [
10720                             {
10721                                 tag : 'span',
10722                                 html : this.fieldLabel
10723                             },
10724                             indicator
10725                         ]
10726                     },
10727                     {
10728                         cls : "", 
10729                         cn: [
10730                             combobox
10731                         ]
10732                     }
10733
10734                 ];
10735                 
10736                 labelCfg = cfg.cn[0];
10737                 contentCfg = cfg.cn[1];
10738             }
10739             
10740             if(this.labelWidth > 12){
10741                 labelCfg.style = "width: " + this.labelWidth + 'px';
10742             }
10743             
10744             if(this.labelWidth < 13 && this.labelmd == 0){
10745                 this.labelmd = this.labelWidth;
10746             }
10747             
10748             if(this.labellg > 0){
10749                 labelCfg.cls += ' col-lg-' + this.labellg;
10750                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10751             }
10752             
10753             if(this.labelmd > 0){
10754                 labelCfg.cls += ' col-md-' + this.labelmd;
10755                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10756             }
10757             
10758             if(this.labelsm > 0){
10759                 labelCfg.cls += ' col-sm-' + this.labelsm;
10760                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10761             }
10762             
10763             if(this.labelxs > 0){
10764                 labelCfg.cls += ' col-xs-' + this.labelxs;
10765                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10766             }
10767             
10768         } else if ( this.fieldLabel.length) {
10769 //                Roo.log(" label");
10770             cfg.cn = [
10771                 indicator,
10772                {
10773                    tag: 'label',
10774                    //cls : 'input-group-addon',
10775                    html : this.fieldLabel
10776
10777                },
10778
10779                combobox
10780
10781             ];
10782             
10783             if(this.indicatorpos == 'right'){
10784                 
10785                 cfg.cn = [
10786                     {
10787                        tag: 'label',
10788                        cn : [
10789                            {
10790                                tag : 'span',
10791                                html : this.fieldLabel
10792                            },
10793                            indicator
10794                        ]
10795
10796                     },
10797                     combobox
10798
10799                 ];
10800
10801             }
10802
10803         } else {
10804             
10805 //                Roo.log(" no label && no align");
10806                 cfg = combobox
10807                      
10808                 
10809         }
10810         
10811         var settings=this;
10812         ['xs','sm','md','lg'].map(function(size){
10813             if (settings[size]) {
10814                 cfg.cls += ' col-' + size + '-' + settings[size];
10815             }
10816         });
10817         
10818         return cfg;
10819         
10820     },
10821     
10822     
10823     
10824     // private
10825     onResize : function(w, h){
10826 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10827 //        if(typeof w == 'number'){
10828 //            var x = w - this.trigger.getWidth();
10829 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10830 //            this.trigger.setStyle('left', x+'px');
10831 //        }
10832     },
10833
10834     // private
10835     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10836
10837     // private
10838     getResizeEl : function(){
10839         return this.inputEl();
10840     },
10841
10842     // private
10843     getPositionEl : function(){
10844         return this.inputEl();
10845     },
10846
10847     // private
10848     alignErrorIcon : function(){
10849         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10850     },
10851
10852     // private
10853     initEvents : function(){
10854         
10855         this.createList();
10856         
10857         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10858         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10859         if(!this.multiple && this.showToggleBtn){
10860             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10861             if(this.hideTrigger){
10862                 this.trigger.setDisplayed(false);
10863             }
10864             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10865         }
10866         
10867         if(this.multiple){
10868             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10869         }
10870         
10871         if(this.removable && !this.editable && !this.tickable){
10872             var close = this.closeTriggerEl();
10873             
10874             if(close){
10875                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10876                 close.on('click', this.removeBtnClick, this, close);
10877             }
10878         }
10879         
10880         //this.trigger.addClassOnOver('x-form-trigger-over');
10881         //this.trigger.addClassOnClick('x-form-trigger-click');
10882         
10883         //if(!this.width){
10884         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10885         //}
10886     },
10887     
10888     closeTriggerEl : function()
10889     {
10890         var close = this.el.select('.roo-combo-removable-btn', true).first();
10891         return close ? close : false;
10892     },
10893     
10894     removeBtnClick : function(e, h, el)
10895     {
10896         e.preventDefault();
10897         
10898         if(this.fireEvent("remove", this) !== false){
10899             this.reset();
10900             this.fireEvent("afterremove", this)
10901         }
10902     },
10903     
10904     createList : function()
10905     {
10906         this.list = Roo.get(document.body).createChild({
10907             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10908             cls: 'typeahead typeahead-long dropdown-menu',
10909             style: 'display:none'
10910         });
10911         
10912         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10913         
10914     },
10915
10916     // private
10917     initTrigger : function(){
10918        
10919     },
10920
10921     // private
10922     onDestroy : function(){
10923         if(this.trigger){
10924             this.trigger.removeAllListeners();
10925           //  this.trigger.remove();
10926         }
10927         //if(this.wrap){
10928         //    this.wrap.remove();
10929         //}
10930         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10931     },
10932
10933     // private
10934     onFocus : function(){
10935         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10936         /*
10937         if(!this.mimicing){
10938             this.wrap.addClass('x-trigger-wrap-focus');
10939             this.mimicing = true;
10940             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10941             if(this.monitorTab){
10942                 this.el.on("keydown", this.checkTab, this);
10943             }
10944         }
10945         */
10946     },
10947
10948     // private
10949     checkTab : function(e){
10950         if(e.getKey() == e.TAB){
10951             this.triggerBlur();
10952         }
10953     },
10954
10955     // private
10956     onBlur : function(){
10957         // do nothing
10958     },
10959
10960     // private
10961     mimicBlur : function(e, t){
10962         /*
10963         if(!this.wrap.contains(t) && this.validateBlur()){
10964             this.triggerBlur();
10965         }
10966         */
10967     },
10968
10969     // private
10970     triggerBlur : function(){
10971         this.mimicing = false;
10972         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10973         if(this.monitorTab){
10974             this.el.un("keydown", this.checkTab, this);
10975         }
10976         //this.wrap.removeClass('x-trigger-wrap-focus');
10977         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10978     },
10979
10980     // private
10981     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10982     validateBlur : function(e, t){
10983         return true;
10984     },
10985
10986     // private
10987     onDisable : function(){
10988         this.inputEl().dom.disabled = true;
10989         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10990         //if(this.wrap){
10991         //    this.wrap.addClass('x-item-disabled');
10992         //}
10993     },
10994
10995     // private
10996     onEnable : function(){
10997         this.inputEl().dom.disabled = false;
10998         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10999         //if(this.wrap){
11000         //    this.el.removeClass('x-item-disabled');
11001         //}
11002     },
11003
11004     // private
11005     onShow : function(){
11006         var ae = this.getActionEl();
11007         
11008         if(ae){
11009             ae.dom.style.display = '';
11010             ae.dom.style.visibility = 'visible';
11011         }
11012     },
11013
11014     // private
11015     
11016     onHide : function(){
11017         var ae = this.getActionEl();
11018         ae.dom.style.display = 'none';
11019     },
11020
11021     /**
11022      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
11023      * by an implementing function.
11024      * @method
11025      * @param {EventObject} e
11026      */
11027     onTriggerClick : Roo.emptyFn
11028 });
11029  /*
11030  * Based on:
11031  * Ext JS Library 1.1.1
11032  * Copyright(c) 2006-2007, Ext JS, LLC.
11033  *
11034  * Originally Released Under LGPL - original licence link has changed is not relivant.
11035  *
11036  * Fork - LGPL
11037  * <script type="text/javascript">
11038  */
11039
11040
11041 /**
11042  * @class Roo.data.SortTypes
11043  * @singleton
11044  * Defines the default sorting (casting?) comparison functions used when sorting data.
11045  */
11046 Roo.data.SortTypes = {
11047     /**
11048      * Default sort that does nothing
11049      * @param {Mixed} s The value being converted
11050      * @return {Mixed} The comparison value
11051      */
11052     none : function(s){
11053         return s;
11054     },
11055     
11056     /**
11057      * The regular expression used to strip tags
11058      * @type {RegExp}
11059      * @property
11060      */
11061     stripTagsRE : /<\/?[^>]+>/gi,
11062     
11063     /**
11064      * Strips all HTML tags to sort on text only
11065      * @param {Mixed} s The value being converted
11066      * @return {String} The comparison value
11067      */
11068     asText : function(s){
11069         return String(s).replace(this.stripTagsRE, "");
11070     },
11071     
11072     /**
11073      * Strips all HTML tags to sort on text only - Case insensitive
11074      * @param {Mixed} s The value being converted
11075      * @return {String} The comparison value
11076      */
11077     asUCText : function(s){
11078         return String(s).toUpperCase().replace(this.stripTagsRE, "");
11079     },
11080     
11081     /**
11082      * Case insensitive string
11083      * @param {Mixed} s The value being converted
11084      * @return {String} The comparison value
11085      */
11086     asUCString : function(s) {
11087         return String(s).toUpperCase();
11088     },
11089     
11090     /**
11091      * Date sorting
11092      * @param {Mixed} s The value being converted
11093      * @return {Number} The comparison value
11094      */
11095     asDate : function(s) {
11096         if(!s){
11097             return 0;
11098         }
11099         if(s instanceof Date){
11100             return s.getTime();
11101         }
11102         return Date.parse(String(s));
11103     },
11104     
11105     /**
11106      * Float sorting
11107      * @param {Mixed} s The value being converted
11108      * @return {Float} The comparison value
11109      */
11110     asFloat : function(s) {
11111         var val = parseFloat(String(s).replace(/,/g, ""));
11112         if(isNaN(val)) {
11113             val = 0;
11114         }
11115         return val;
11116     },
11117     
11118     /**
11119      * Integer sorting
11120      * @param {Mixed} s The value being converted
11121      * @return {Number} The comparison value
11122      */
11123     asInt : function(s) {
11124         var val = parseInt(String(s).replace(/,/g, ""));
11125         if(isNaN(val)) {
11126             val = 0;
11127         }
11128         return val;
11129     }
11130 };/*
11131  * Based on:
11132  * Ext JS Library 1.1.1
11133  * Copyright(c) 2006-2007, Ext JS, LLC.
11134  *
11135  * Originally Released Under LGPL - original licence link has changed is not relivant.
11136  *
11137  * Fork - LGPL
11138  * <script type="text/javascript">
11139  */
11140
11141 /**
11142 * @class Roo.data.Record
11143  * Instances of this class encapsulate both record <em>definition</em> information, and record
11144  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
11145  * to access Records cached in an {@link Roo.data.Store} object.<br>
11146  * <p>
11147  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
11148  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
11149  * objects.<br>
11150  * <p>
11151  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
11152  * @constructor
11153  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
11154  * {@link #create}. The parameters are the same.
11155  * @param {Array} data An associative Array of data values keyed by the field name.
11156  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
11157  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
11158  * not specified an integer id is generated.
11159  */
11160 Roo.data.Record = function(data, id){
11161     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11162     this.data = data;
11163 };
11164
11165 /**
11166  * Generate a constructor for a specific record layout.
11167  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11168  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11169  * Each field definition object may contain the following properties: <ul>
11170  * <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,
11171  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11172  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11173  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11174  * is being used, then this is a string containing the javascript expression to reference the data relative to 
11175  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11176  * to the data item relative to the record element. If the mapping expression is the same as the field name,
11177  * this may be omitted.</p></li>
11178  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11179  * <ul><li>auto (Default, implies no conversion)</li>
11180  * <li>string</li>
11181  * <li>int</li>
11182  * <li>float</li>
11183  * <li>boolean</li>
11184  * <li>date</li></ul></p></li>
11185  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11186  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11187  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11188  * by the Reader into an object that will be stored in the Record. It is passed the
11189  * following parameters:<ul>
11190  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11191  * </ul></p></li>
11192  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11193  * </ul>
11194  * <br>usage:<br><pre><code>
11195 var TopicRecord = Roo.data.Record.create(
11196     {name: 'title', mapping: 'topic_title'},
11197     {name: 'author', mapping: 'username'},
11198     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11199     {name: 'lastPost', mapping: 'post_time', type: 'date'},
11200     {name: 'lastPoster', mapping: 'user2'},
11201     {name: 'excerpt', mapping: 'post_text'}
11202 );
11203
11204 var myNewRecord = new TopicRecord({
11205     title: 'Do my job please',
11206     author: 'noobie',
11207     totalPosts: 1,
11208     lastPost: new Date(),
11209     lastPoster: 'Animal',
11210     excerpt: 'No way dude!'
11211 });
11212 myStore.add(myNewRecord);
11213 </code></pre>
11214  * @method create
11215  * @static
11216  */
11217 Roo.data.Record.create = function(o){
11218     var f = function(){
11219         f.superclass.constructor.apply(this, arguments);
11220     };
11221     Roo.extend(f, Roo.data.Record);
11222     var p = f.prototype;
11223     p.fields = new Roo.util.MixedCollection(false, function(field){
11224         return field.name;
11225     });
11226     for(var i = 0, len = o.length; i < len; i++){
11227         p.fields.add(new Roo.data.Field(o[i]));
11228     }
11229     f.getField = function(name){
11230         return p.fields.get(name);  
11231     };
11232     return f;
11233 };
11234
11235 Roo.data.Record.AUTO_ID = 1000;
11236 Roo.data.Record.EDIT = 'edit';
11237 Roo.data.Record.REJECT = 'reject';
11238 Roo.data.Record.COMMIT = 'commit';
11239
11240 Roo.data.Record.prototype = {
11241     /**
11242      * Readonly flag - true if this record has been modified.
11243      * @type Boolean
11244      */
11245     dirty : false,
11246     editing : false,
11247     error: null,
11248     modified: null,
11249
11250     // private
11251     join : function(store){
11252         this.store = store;
11253     },
11254
11255     /**
11256      * Set the named field to the specified value.
11257      * @param {String} name The name of the field to set.
11258      * @param {Object} value The value to set the field to.
11259      */
11260     set : function(name, value){
11261         if(this.data[name] == value){
11262             return;
11263         }
11264         this.dirty = true;
11265         if(!this.modified){
11266             this.modified = {};
11267         }
11268         if(typeof this.modified[name] == 'undefined'){
11269             this.modified[name] = this.data[name];
11270         }
11271         this.data[name] = value;
11272         if(!this.editing && this.store){
11273             this.store.afterEdit(this);
11274         }       
11275     },
11276
11277     /**
11278      * Get the value of the named field.
11279      * @param {String} name The name of the field to get the value of.
11280      * @return {Object} The value of the field.
11281      */
11282     get : function(name){
11283         return this.data[name]; 
11284     },
11285
11286     // private
11287     beginEdit : function(){
11288         this.editing = true;
11289         this.modified = {}; 
11290     },
11291
11292     // private
11293     cancelEdit : function(){
11294         this.editing = false;
11295         delete this.modified;
11296     },
11297
11298     // private
11299     endEdit : function(){
11300         this.editing = false;
11301         if(this.dirty && this.store){
11302             this.store.afterEdit(this);
11303         }
11304     },
11305
11306     /**
11307      * Usually called by the {@link Roo.data.Store} which owns the Record.
11308      * Rejects all changes made to the Record since either creation, or the last commit operation.
11309      * Modified fields are reverted to their original values.
11310      * <p>
11311      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11312      * of reject operations.
11313      */
11314     reject : function(){
11315         var m = this.modified;
11316         for(var n in m){
11317             if(typeof m[n] != "function"){
11318                 this.data[n] = m[n];
11319             }
11320         }
11321         this.dirty = false;
11322         delete this.modified;
11323         this.editing = false;
11324         if(this.store){
11325             this.store.afterReject(this);
11326         }
11327     },
11328
11329     /**
11330      * Usually called by the {@link Roo.data.Store} which owns the Record.
11331      * Commits all changes made to the Record since either creation, or the last commit operation.
11332      * <p>
11333      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11334      * of commit operations.
11335      */
11336     commit : function(){
11337         this.dirty = false;
11338         delete this.modified;
11339         this.editing = false;
11340         if(this.store){
11341             this.store.afterCommit(this);
11342         }
11343     },
11344
11345     // private
11346     hasError : function(){
11347         return this.error != null;
11348     },
11349
11350     // private
11351     clearError : function(){
11352         this.error = null;
11353     },
11354
11355     /**
11356      * Creates a copy of this record.
11357      * @param {String} id (optional) A new record id if you don't want to use this record's id
11358      * @return {Record}
11359      */
11360     copy : function(newId) {
11361         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11362     }
11363 };/*
11364  * Based on:
11365  * Ext JS Library 1.1.1
11366  * Copyright(c) 2006-2007, Ext JS, LLC.
11367  *
11368  * Originally Released Under LGPL - original licence link has changed is not relivant.
11369  *
11370  * Fork - LGPL
11371  * <script type="text/javascript">
11372  */
11373
11374
11375
11376 /**
11377  * @class Roo.data.Store
11378  * @extends Roo.util.Observable
11379  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11380  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11381  * <p>
11382  * 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
11383  * has no knowledge of the format of the data returned by the Proxy.<br>
11384  * <p>
11385  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11386  * instances from the data object. These records are cached and made available through accessor functions.
11387  * @constructor
11388  * Creates a new Store.
11389  * @param {Object} config A config object containing the objects needed for the Store to access data,
11390  * and read the data into Records.
11391  */
11392 Roo.data.Store = function(config){
11393     this.data = new Roo.util.MixedCollection(false);
11394     this.data.getKey = function(o){
11395         return o.id;
11396     };
11397     this.baseParams = {};
11398     // private
11399     this.paramNames = {
11400         "start" : "start",
11401         "limit" : "limit",
11402         "sort" : "sort",
11403         "dir" : "dir",
11404         "multisort" : "_multisort"
11405     };
11406
11407     if(config && config.data){
11408         this.inlineData = config.data;
11409         delete config.data;
11410     }
11411
11412     Roo.apply(this, config);
11413     
11414     if(this.reader){ // reader passed
11415         this.reader = Roo.factory(this.reader, Roo.data);
11416         this.reader.xmodule = this.xmodule || false;
11417         if(!this.recordType){
11418             this.recordType = this.reader.recordType;
11419         }
11420         if(this.reader.onMetaChange){
11421             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11422         }
11423     }
11424
11425     if(this.recordType){
11426         this.fields = this.recordType.prototype.fields;
11427     }
11428     this.modified = [];
11429
11430     this.addEvents({
11431         /**
11432          * @event datachanged
11433          * Fires when the data cache has changed, and a widget which is using this Store
11434          * as a Record cache should refresh its view.
11435          * @param {Store} this
11436          */
11437         datachanged : true,
11438         /**
11439          * @event metachange
11440          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11441          * @param {Store} this
11442          * @param {Object} meta The JSON metadata
11443          */
11444         metachange : true,
11445         /**
11446          * @event add
11447          * Fires when Records have been added to the Store
11448          * @param {Store} this
11449          * @param {Roo.data.Record[]} records The array of Records added
11450          * @param {Number} index The index at which the record(s) were added
11451          */
11452         add : true,
11453         /**
11454          * @event remove
11455          * Fires when a Record has been removed from the Store
11456          * @param {Store} this
11457          * @param {Roo.data.Record} record The Record that was removed
11458          * @param {Number} index The index at which the record was removed
11459          */
11460         remove : true,
11461         /**
11462          * @event update
11463          * Fires when a Record has been updated
11464          * @param {Store} this
11465          * @param {Roo.data.Record} record The Record that was updated
11466          * @param {String} operation The update operation being performed.  Value may be one of:
11467          * <pre><code>
11468  Roo.data.Record.EDIT
11469  Roo.data.Record.REJECT
11470  Roo.data.Record.COMMIT
11471          * </code></pre>
11472          */
11473         update : true,
11474         /**
11475          * @event clear
11476          * Fires when the data cache has been cleared.
11477          * @param {Store} this
11478          */
11479         clear : true,
11480         /**
11481          * @event beforeload
11482          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11483          * the load action will be canceled.
11484          * @param {Store} this
11485          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11486          */
11487         beforeload : true,
11488         /**
11489          * @event beforeloadadd
11490          * Fires after a new set of Records has been loaded.
11491          * @param {Store} this
11492          * @param {Roo.data.Record[]} records The Records that were loaded
11493          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11494          */
11495         beforeloadadd : true,
11496         /**
11497          * @event load
11498          * Fires after a new set of Records has been loaded, before they are added to the store.
11499          * @param {Store} this
11500          * @param {Roo.data.Record[]} records The Records that were loaded
11501          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11502          * @params {Object} return from reader
11503          */
11504         load : true,
11505         /**
11506          * @event loadexception
11507          * Fires if an exception occurs in the Proxy during loading.
11508          * Called with the signature of the Proxy's "loadexception" event.
11509          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11510          * 
11511          * @param {Proxy} 
11512          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11513          * @param {Object} load options 
11514          * @param {Object} jsonData from your request (normally this contains the Exception)
11515          */
11516         loadexception : true
11517     });
11518     
11519     if(this.proxy){
11520         this.proxy = Roo.factory(this.proxy, Roo.data);
11521         this.proxy.xmodule = this.xmodule || false;
11522         this.relayEvents(this.proxy,  ["loadexception"]);
11523     }
11524     this.sortToggle = {};
11525     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11526
11527     Roo.data.Store.superclass.constructor.call(this);
11528
11529     if(this.inlineData){
11530         this.loadData(this.inlineData);
11531         delete this.inlineData;
11532     }
11533 };
11534
11535 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11536      /**
11537     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11538     * without a remote query - used by combo/forms at present.
11539     */
11540     
11541     /**
11542     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11543     */
11544     /**
11545     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11546     */
11547     /**
11548     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11549     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11550     */
11551     /**
11552     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11553     * on any HTTP request
11554     */
11555     /**
11556     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11557     */
11558     /**
11559     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11560     */
11561     multiSort: false,
11562     /**
11563     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11564     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11565     */
11566     remoteSort : false,
11567
11568     /**
11569     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11570      * loaded or when a record is removed. (defaults to false).
11571     */
11572     pruneModifiedRecords : false,
11573
11574     // private
11575     lastOptions : null,
11576
11577     /**
11578      * Add Records to the Store and fires the add event.
11579      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11580      */
11581     add : function(records){
11582         records = [].concat(records);
11583         for(var i = 0, len = records.length; i < len; i++){
11584             records[i].join(this);
11585         }
11586         var index = this.data.length;
11587         this.data.addAll(records);
11588         this.fireEvent("add", this, records, index);
11589     },
11590
11591     /**
11592      * Remove a Record from the Store and fires the remove event.
11593      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11594      */
11595     remove : function(record){
11596         var index = this.data.indexOf(record);
11597         this.data.removeAt(index);
11598  
11599         if(this.pruneModifiedRecords){
11600             this.modified.remove(record);
11601         }
11602         this.fireEvent("remove", this, record, index);
11603     },
11604
11605     /**
11606      * Remove all Records from the Store and fires the clear event.
11607      */
11608     removeAll : function(){
11609         this.data.clear();
11610         if(this.pruneModifiedRecords){
11611             this.modified = [];
11612         }
11613         this.fireEvent("clear", this);
11614     },
11615
11616     /**
11617      * Inserts Records to the Store at the given index and fires the add event.
11618      * @param {Number} index The start index at which to insert the passed Records.
11619      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11620      */
11621     insert : function(index, records){
11622         records = [].concat(records);
11623         for(var i = 0, len = records.length; i < len; i++){
11624             this.data.insert(index, records[i]);
11625             records[i].join(this);
11626         }
11627         this.fireEvent("add", this, records, index);
11628     },
11629
11630     /**
11631      * Get the index within the cache of the passed Record.
11632      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11633      * @return {Number} The index of the passed Record. Returns -1 if not found.
11634      */
11635     indexOf : function(record){
11636         return this.data.indexOf(record);
11637     },
11638
11639     /**
11640      * Get the index within the cache of the Record with the passed id.
11641      * @param {String} id The id of the Record to find.
11642      * @return {Number} The index of the Record. Returns -1 if not found.
11643      */
11644     indexOfId : function(id){
11645         return this.data.indexOfKey(id);
11646     },
11647
11648     /**
11649      * Get the Record with the specified id.
11650      * @param {String} id The id of the Record to find.
11651      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11652      */
11653     getById : function(id){
11654         return this.data.key(id);
11655     },
11656
11657     /**
11658      * Get the Record at the specified index.
11659      * @param {Number} index The index of the Record to find.
11660      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11661      */
11662     getAt : function(index){
11663         return this.data.itemAt(index);
11664     },
11665
11666     /**
11667      * Returns a range of Records between specified indices.
11668      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11669      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11670      * @return {Roo.data.Record[]} An array of Records
11671      */
11672     getRange : function(start, end){
11673         return this.data.getRange(start, end);
11674     },
11675
11676     // private
11677     storeOptions : function(o){
11678         o = Roo.apply({}, o);
11679         delete o.callback;
11680         delete o.scope;
11681         this.lastOptions = o;
11682     },
11683
11684     /**
11685      * Loads the Record cache from the configured Proxy using the configured Reader.
11686      * <p>
11687      * If using remote paging, then the first load call must specify the <em>start</em>
11688      * and <em>limit</em> properties in the options.params property to establish the initial
11689      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11690      * <p>
11691      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11692      * and this call will return before the new data has been loaded. Perform any post-processing
11693      * in a callback function, or in a "load" event handler.</strong>
11694      * <p>
11695      * @param {Object} options An object containing properties which control loading options:<ul>
11696      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11697      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11698      * passed the following arguments:<ul>
11699      * <li>r : Roo.data.Record[]</li>
11700      * <li>options: Options object from the load call</li>
11701      * <li>success: Boolean success indicator</li></ul></li>
11702      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11703      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11704      * </ul>
11705      */
11706     load : function(options){
11707         options = options || {};
11708         if(this.fireEvent("beforeload", this, options) !== false){
11709             this.storeOptions(options);
11710             var p = Roo.apply(options.params || {}, this.baseParams);
11711             // if meta was not loaded from remote source.. try requesting it.
11712             if (!this.reader.metaFromRemote) {
11713                 p._requestMeta = 1;
11714             }
11715             if(this.sortInfo && this.remoteSort){
11716                 var pn = this.paramNames;
11717                 p[pn["sort"]] = this.sortInfo.field;
11718                 p[pn["dir"]] = this.sortInfo.direction;
11719             }
11720             if (this.multiSort) {
11721                 var pn = this.paramNames;
11722                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11723             }
11724             
11725             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11726         }
11727     },
11728
11729     /**
11730      * Reloads the Record cache from the configured Proxy using the configured Reader and
11731      * the options from the last load operation performed.
11732      * @param {Object} options (optional) An object containing properties which may override the options
11733      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11734      * the most recently used options are reused).
11735      */
11736     reload : function(options){
11737         this.load(Roo.applyIf(options||{}, this.lastOptions));
11738     },
11739
11740     // private
11741     // Called as a callback by the Reader during a load operation.
11742     loadRecords : function(o, options, success){
11743         if(!o || success === false){
11744             if(success !== false){
11745                 this.fireEvent("load", this, [], options, o);
11746             }
11747             if(options.callback){
11748                 options.callback.call(options.scope || this, [], options, false);
11749             }
11750             return;
11751         }
11752         // if data returned failure - throw an exception.
11753         if (o.success === false) {
11754             // show a message if no listener is registered.
11755             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11756                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11757             }
11758             // loadmask wil be hooked into this..
11759             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11760             return;
11761         }
11762         var r = o.records, t = o.totalRecords || r.length;
11763         
11764         this.fireEvent("beforeloadadd", this, r, options, o);
11765         
11766         if(!options || options.add !== true){
11767             if(this.pruneModifiedRecords){
11768                 this.modified = [];
11769             }
11770             for(var i = 0, len = r.length; i < len; i++){
11771                 r[i].join(this);
11772             }
11773             if(this.snapshot){
11774                 this.data = this.snapshot;
11775                 delete this.snapshot;
11776             }
11777             this.data.clear();
11778             this.data.addAll(r);
11779             this.totalLength = t;
11780             this.applySort();
11781             this.fireEvent("datachanged", this);
11782         }else{
11783             this.totalLength = Math.max(t, this.data.length+r.length);
11784             this.add(r);
11785         }
11786         
11787         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11788                 
11789             var e = new Roo.data.Record({});
11790
11791             e.set(this.parent.displayField, this.parent.emptyTitle);
11792             e.set(this.parent.valueField, '');
11793
11794             this.insert(0, e);
11795         }
11796             
11797         this.fireEvent("load", this, r, options, o);
11798         if(options.callback){
11799             options.callback.call(options.scope || this, r, options, true);
11800         }
11801     },
11802
11803
11804     /**
11805      * Loads data from a passed data block. A Reader which understands the format of the data
11806      * must have been configured in the constructor.
11807      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11808      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11809      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11810      */
11811     loadData : function(o, append){
11812         var r = this.reader.readRecords(o);
11813         this.loadRecords(r, {add: append}, true);
11814     },
11815
11816     /**
11817      * Gets the number of cached records.
11818      * <p>
11819      * <em>If using paging, this may not be the total size of the dataset. If the data object
11820      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11821      * the data set size</em>
11822      */
11823     getCount : function(){
11824         return this.data.length || 0;
11825     },
11826
11827     /**
11828      * Gets the total number of records in the dataset as returned by the server.
11829      * <p>
11830      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11831      * the dataset size</em>
11832      */
11833     getTotalCount : function(){
11834         return this.totalLength || 0;
11835     },
11836
11837     /**
11838      * Returns the sort state of the Store as an object with two properties:
11839      * <pre><code>
11840  field {String} The name of the field by which the Records are sorted
11841  direction {String} The sort order, "ASC" or "DESC"
11842      * </code></pre>
11843      */
11844     getSortState : function(){
11845         return this.sortInfo;
11846     },
11847
11848     // private
11849     applySort : function(){
11850         if(this.sortInfo && !this.remoteSort){
11851             var s = this.sortInfo, f = s.field;
11852             var st = this.fields.get(f).sortType;
11853             var fn = function(r1, r2){
11854                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11855                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11856             };
11857             this.data.sort(s.direction, fn);
11858             if(this.snapshot && this.snapshot != this.data){
11859                 this.snapshot.sort(s.direction, fn);
11860             }
11861         }
11862     },
11863
11864     /**
11865      * Sets the default sort column and order to be used by the next load operation.
11866      * @param {String} fieldName The name of the field to sort by.
11867      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11868      */
11869     setDefaultSort : function(field, dir){
11870         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11871     },
11872
11873     /**
11874      * Sort the Records.
11875      * If remote sorting is used, the sort is performed on the server, and the cache is
11876      * reloaded. If local sorting is used, the cache is sorted internally.
11877      * @param {String} fieldName The name of the field to sort by.
11878      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11879      */
11880     sort : function(fieldName, dir){
11881         var f = this.fields.get(fieldName);
11882         if(!dir){
11883             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11884             
11885             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11886                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11887             }else{
11888                 dir = f.sortDir;
11889             }
11890         }
11891         this.sortToggle[f.name] = dir;
11892         this.sortInfo = {field: f.name, direction: dir};
11893         if(!this.remoteSort){
11894             this.applySort();
11895             this.fireEvent("datachanged", this);
11896         }else{
11897             this.load(this.lastOptions);
11898         }
11899     },
11900
11901     /**
11902      * Calls the specified function for each of the Records in the cache.
11903      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11904      * Returning <em>false</em> aborts and exits the iteration.
11905      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11906      */
11907     each : function(fn, scope){
11908         this.data.each(fn, scope);
11909     },
11910
11911     /**
11912      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11913      * (e.g., during paging).
11914      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11915      */
11916     getModifiedRecords : function(){
11917         return this.modified;
11918     },
11919
11920     // private
11921     createFilterFn : function(property, value, anyMatch){
11922         if(!value.exec){ // not a regex
11923             value = String(value);
11924             if(value.length == 0){
11925                 return false;
11926             }
11927             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11928         }
11929         return function(r){
11930             return value.test(r.data[property]);
11931         };
11932     },
11933
11934     /**
11935      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11936      * @param {String} property A field on your records
11937      * @param {Number} start The record index to start at (defaults to 0)
11938      * @param {Number} end The last record index to include (defaults to length - 1)
11939      * @return {Number} The sum
11940      */
11941     sum : function(property, start, end){
11942         var rs = this.data.items, v = 0;
11943         start = start || 0;
11944         end = (end || end === 0) ? end : rs.length-1;
11945
11946         for(var i = start; i <= end; i++){
11947             v += (rs[i].data[property] || 0);
11948         }
11949         return v;
11950     },
11951
11952     /**
11953      * Filter the records by a specified property.
11954      * @param {String} field A field on your records
11955      * @param {String/RegExp} value Either a string that the field
11956      * should start with or a RegExp to test against the field
11957      * @param {Boolean} anyMatch True to match any part not just the beginning
11958      */
11959     filter : function(property, value, anyMatch){
11960         var fn = this.createFilterFn(property, value, anyMatch);
11961         return fn ? this.filterBy(fn) : this.clearFilter();
11962     },
11963
11964     /**
11965      * Filter by a function. The specified function will be called with each
11966      * record in this data source. If the function returns true the record is included,
11967      * otherwise it is filtered.
11968      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11969      * @param {Object} scope (optional) The scope of the function (defaults to this)
11970      */
11971     filterBy : function(fn, scope){
11972         this.snapshot = this.snapshot || this.data;
11973         this.data = this.queryBy(fn, scope||this);
11974         this.fireEvent("datachanged", this);
11975     },
11976
11977     /**
11978      * Query the records by a specified property.
11979      * @param {String} field A field on your records
11980      * @param {String/RegExp} value Either a string that the field
11981      * should start with or a RegExp to test against the field
11982      * @param {Boolean} anyMatch True to match any part not just the beginning
11983      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11984      */
11985     query : function(property, value, anyMatch){
11986         var fn = this.createFilterFn(property, value, anyMatch);
11987         return fn ? this.queryBy(fn) : this.data.clone();
11988     },
11989
11990     /**
11991      * Query by a function. The specified function will be called with each
11992      * record in this data source. If the function returns true the record is included
11993      * in the results.
11994      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11995      * @param {Object} scope (optional) The scope of the function (defaults to this)
11996       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11997      **/
11998     queryBy : function(fn, scope){
11999         var data = this.snapshot || this.data;
12000         return data.filterBy(fn, scope||this);
12001     },
12002
12003     /**
12004      * Collects unique values for a particular dataIndex from this store.
12005      * @param {String} dataIndex The property to collect
12006      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
12007      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
12008      * @return {Array} An array of the unique values
12009      **/
12010     collect : function(dataIndex, allowNull, bypassFilter){
12011         var d = (bypassFilter === true && this.snapshot) ?
12012                 this.snapshot.items : this.data.items;
12013         var v, sv, r = [], l = {};
12014         for(var i = 0, len = d.length; i < len; i++){
12015             v = d[i].data[dataIndex];
12016             sv = String(v);
12017             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
12018                 l[sv] = true;
12019                 r[r.length] = v;
12020             }
12021         }
12022         return r;
12023     },
12024
12025     /**
12026      * Revert to a view of the Record cache with no filtering applied.
12027      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
12028      */
12029     clearFilter : function(suppressEvent){
12030         if(this.snapshot && this.snapshot != this.data){
12031             this.data = this.snapshot;
12032             delete this.snapshot;
12033             if(suppressEvent !== true){
12034                 this.fireEvent("datachanged", this);
12035             }
12036         }
12037     },
12038
12039     // private
12040     afterEdit : function(record){
12041         if(this.modified.indexOf(record) == -1){
12042             this.modified.push(record);
12043         }
12044         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
12045     },
12046     
12047     // private
12048     afterReject : function(record){
12049         this.modified.remove(record);
12050         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
12051     },
12052
12053     // private
12054     afterCommit : function(record){
12055         this.modified.remove(record);
12056         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
12057     },
12058
12059     /**
12060      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
12061      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
12062      */
12063     commitChanges : function(){
12064         var m = this.modified.slice(0);
12065         this.modified = [];
12066         for(var i = 0, len = m.length; i < len; i++){
12067             m[i].commit();
12068         }
12069     },
12070
12071     /**
12072      * Cancel outstanding changes on all changed records.
12073      */
12074     rejectChanges : function(){
12075         var m = this.modified.slice(0);
12076         this.modified = [];
12077         for(var i = 0, len = m.length; i < len; i++){
12078             m[i].reject();
12079         }
12080     },
12081
12082     onMetaChange : function(meta, rtype, o){
12083         this.recordType = rtype;
12084         this.fields = rtype.prototype.fields;
12085         delete this.snapshot;
12086         this.sortInfo = meta.sortInfo || this.sortInfo;
12087         this.modified = [];
12088         this.fireEvent('metachange', this, this.reader.meta);
12089     },
12090     
12091     moveIndex : function(data, type)
12092     {
12093         var index = this.indexOf(data);
12094         
12095         var newIndex = index + type;
12096         
12097         this.remove(data);
12098         
12099         this.insert(newIndex, data);
12100         
12101     }
12102 });/*
12103  * Based on:
12104  * Ext JS Library 1.1.1
12105  * Copyright(c) 2006-2007, Ext JS, LLC.
12106  *
12107  * Originally Released Under LGPL - original licence link has changed is not relivant.
12108  *
12109  * Fork - LGPL
12110  * <script type="text/javascript">
12111  */
12112
12113 /**
12114  * @class Roo.data.SimpleStore
12115  * @extends Roo.data.Store
12116  * Small helper class to make creating Stores from Array data easier.
12117  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
12118  * @cfg {Array} fields An array of field definition objects, or field name strings.
12119  * @cfg {Array} data The multi-dimensional array of data
12120  * @constructor
12121  * @param {Object} config
12122  */
12123 Roo.data.SimpleStore = function(config){
12124     Roo.data.SimpleStore.superclass.constructor.call(this, {
12125         isLocal : true,
12126         reader: new Roo.data.ArrayReader({
12127                 id: config.id
12128             },
12129             Roo.data.Record.create(config.fields)
12130         ),
12131         proxy : new Roo.data.MemoryProxy(config.data)
12132     });
12133     this.load();
12134 };
12135 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
12136  * Based on:
12137  * Ext JS Library 1.1.1
12138  * Copyright(c) 2006-2007, Ext JS, LLC.
12139  *
12140  * Originally Released Under LGPL - original licence link has changed is not relivant.
12141  *
12142  * Fork - LGPL
12143  * <script type="text/javascript">
12144  */
12145
12146 /**
12147 /**
12148  * @extends Roo.data.Store
12149  * @class Roo.data.JsonStore
12150  * Small helper class to make creating Stores for JSON data easier. <br/>
12151 <pre><code>
12152 var store = new Roo.data.JsonStore({
12153     url: 'get-images.php',
12154     root: 'images',
12155     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
12156 });
12157 </code></pre>
12158  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12159  * JsonReader and HttpProxy (unless inline data is provided).</b>
12160  * @cfg {Array} fields An array of field definition objects, or field name strings.
12161  * @constructor
12162  * @param {Object} config
12163  */
12164 Roo.data.JsonStore = function(c){
12165     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12166         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12167         reader: new Roo.data.JsonReader(c, c.fields)
12168     }));
12169 };
12170 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12171  * Based on:
12172  * Ext JS Library 1.1.1
12173  * Copyright(c) 2006-2007, Ext JS, LLC.
12174  *
12175  * Originally Released Under LGPL - original licence link has changed is not relivant.
12176  *
12177  * Fork - LGPL
12178  * <script type="text/javascript">
12179  */
12180
12181  
12182 Roo.data.Field = function(config){
12183     if(typeof config == "string"){
12184         config = {name: config};
12185     }
12186     Roo.apply(this, config);
12187     
12188     if(!this.type){
12189         this.type = "auto";
12190     }
12191     
12192     var st = Roo.data.SortTypes;
12193     // named sortTypes are supported, here we look them up
12194     if(typeof this.sortType == "string"){
12195         this.sortType = st[this.sortType];
12196     }
12197     
12198     // set default sortType for strings and dates
12199     if(!this.sortType){
12200         switch(this.type){
12201             case "string":
12202                 this.sortType = st.asUCString;
12203                 break;
12204             case "date":
12205                 this.sortType = st.asDate;
12206                 break;
12207             default:
12208                 this.sortType = st.none;
12209         }
12210     }
12211
12212     // define once
12213     var stripRe = /[\$,%]/g;
12214
12215     // prebuilt conversion function for this field, instead of
12216     // switching every time we're reading a value
12217     if(!this.convert){
12218         var cv, dateFormat = this.dateFormat;
12219         switch(this.type){
12220             case "":
12221             case "auto":
12222             case undefined:
12223                 cv = function(v){ return v; };
12224                 break;
12225             case "string":
12226                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12227                 break;
12228             case "int":
12229                 cv = function(v){
12230                     return v !== undefined && v !== null && v !== '' ?
12231                            parseInt(String(v).replace(stripRe, ""), 10) : '';
12232                     };
12233                 break;
12234             case "float":
12235                 cv = function(v){
12236                     return v !== undefined && v !== null && v !== '' ?
12237                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
12238                     };
12239                 break;
12240             case "bool":
12241             case "boolean":
12242                 cv = function(v){ return v === true || v === "true" || v == 1; };
12243                 break;
12244             case "date":
12245                 cv = function(v){
12246                     if(!v){
12247                         return '';
12248                     }
12249                     if(v instanceof Date){
12250                         return v;
12251                     }
12252                     if(dateFormat){
12253                         if(dateFormat == "timestamp"){
12254                             return new Date(v*1000);
12255                         }
12256                         return Date.parseDate(v, dateFormat);
12257                     }
12258                     var parsed = Date.parse(v);
12259                     return parsed ? new Date(parsed) : null;
12260                 };
12261              break;
12262             
12263         }
12264         this.convert = cv;
12265     }
12266 };
12267
12268 Roo.data.Field.prototype = {
12269     dateFormat: null,
12270     defaultValue: "",
12271     mapping: null,
12272     sortType : null,
12273     sortDir : "ASC"
12274 };/*
12275  * Based on:
12276  * Ext JS Library 1.1.1
12277  * Copyright(c) 2006-2007, Ext JS, LLC.
12278  *
12279  * Originally Released Under LGPL - original licence link has changed is not relivant.
12280  *
12281  * Fork - LGPL
12282  * <script type="text/javascript">
12283  */
12284  
12285 // Base class for reading structured data from a data source.  This class is intended to be
12286 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12287
12288 /**
12289  * @class Roo.data.DataReader
12290  * Base class for reading structured data from a data source.  This class is intended to be
12291  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12292  */
12293
12294 Roo.data.DataReader = function(meta, recordType){
12295     
12296     this.meta = meta;
12297     
12298     this.recordType = recordType instanceof Array ? 
12299         Roo.data.Record.create(recordType) : recordType;
12300 };
12301
12302 Roo.data.DataReader.prototype = {
12303      /**
12304      * Create an empty record
12305      * @param {Object} data (optional) - overlay some values
12306      * @return {Roo.data.Record} record created.
12307      */
12308     newRow :  function(d) {
12309         var da =  {};
12310         this.recordType.prototype.fields.each(function(c) {
12311             switch( c.type) {
12312                 case 'int' : da[c.name] = 0; break;
12313                 case 'date' : da[c.name] = new Date(); break;
12314                 case 'float' : da[c.name] = 0.0; break;
12315                 case 'boolean' : da[c.name] = false; break;
12316                 default : da[c.name] = ""; break;
12317             }
12318             
12319         });
12320         return new this.recordType(Roo.apply(da, d));
12321     }
12322     
12323 };/*
12324  * Based on:
12325  * Ext JS Library 1.1.1
12326  * Copyright(c) 2006-2007, Ext JS, LLC.
12327  *
12328  * Originally Released Under LGPL - original licence link has changed is not relivant.
12329  *
12330  * Fork - LGPL
12331  * <script type="text/javascript">
12332  */
12333
12334 /**
12335  * @class Roo.data.DataProxy
12336  * @extends Roo.data.Observable
12337  * This class is an abstract base class for implementations which provide retrieval of
12338  * unformatted data objects.<br>
12339  * <p>
12340  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12341  * (of the appropriate type which knows how to parse the data object) to provide a block of
12342  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12343  * <p>
12344  * Custom implementations must implement the load method as described in
12345  * {@link Roo.data.HttpProxy#load}.
12346  */
12347 Roo.data.DataProxy = function(){
12348     this.addEvents({
12349         /**
12350          * @event beforeload
12351          * Fires before a network request is made to retrieve a data object.
12352          * @param {Object} This DataProxy object.
12353          * @param {Object} params The params parameter to the load function.
12354          */
12355         beforeload : true,
12356         /**
12357          * @event load
12358          * Fires before the load method's callback is called.
12359          * @param {Object} This DataProxy object.
12360          * @param {Object} o The data object.
12361          * @param {Object} arg The callback argument object passed to the load function.
12362          */
12363         load : true,
12364         /**
12365          * @event loadexception
12366          * Fires if an Exception occurs during data retrieval.
12367          * @param {Object} This DataProxy object.
12368          * @param {Object} o The data object.
12369          * @param {Object} arg The callback argument object passed to the load function.
12370          * @param {Object} e The Exception.
12371          */
12372         loadexception : true
12373     });
12374     Roo.data.DataProxy.superclass.constructor.call(this);
12375 };
12376
12377 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12378
12379     /**
12380      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12381      */
12382 /*
12383  * Based on:
12384  * Ext JS Library 1.1.1
12385  * Copyright(c) 2006-2007, Ext JS, LLC.
12386  *
12387  * Originally Released Under LGPL - original licence link has changed is not relivant.
12388  *
12389  * Fork - LGPL
12390  * <script type="text/javascript">
12391  */
12392 /**
12393  * @class Roo.data.MemoryProxy
12394  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12395  * to the Reader when its load method is called.
12396  * @constructor
12397  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12398  */
12399 Roo.data.MemoryProxy = function(data){
12400     if (data.data) {
12401         data = data.data;
12402     }
12403     Roo.data.MemoryProxy.superclass.constructor.call(this);
12404     this.data = data;
12405 };
12406
12407 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12408     
12409     /**
12410      * Load data from the requested source (in this case an in-memory
12411      * data object passed to the constructor), read the data object into
12412      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12413      * process that block using the passed callback.
12414      * @param {Object} params This parameter is not used by the MemoryProxy class.
12415      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12416      * object into a block of Roo.data.Records.
12417      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12418      * The function must be passed <ul>
12419      * <li>The Record block object</li>
12420      * <li>The "arg" argument from the load function</li>
12421      * <li>A boolean success indicator</li>
12422      * </ul>
12423      * @param {Object} scope The scope in which to call the callback
12424      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12425      */
12426     load : function(params, reader, callback, scope, arg){
12427         params = params || {};
12428         var result;
12429         try {
12430             result = reader.readRecords(params.data ? params.data :this.data);
12431         }catch(e){
12432             this.fireEvent("loadexception", this, arg, null, e);
12433             callback.call(scope, null, arg, false);
12434             return;
12435         }
12436         callback.call(scope, result, arg, true);
12437     },
12438     
12439     // private
12440     update : function(params, records){
12441         
12442     }
12443 });/*
12444  * Based on:
12445  * Ext JS Library 1.1.1
12446  * Copyright(c) 2006-2007, Ext JS, LLC.
12447  *
12448  * Originally Released Under LGPL - original licence link has changed is not relivant.
12449  *
12450  * Fork - LGPL
12451  * <script type="text/javascript">
12452  */
12453 /**
12454  * @class Roo.data.HttpProxy
12455  * @extends Roo.data.DataProxy
12456  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12457  * configured to reference a certain URL.<br><br>
12458  * <p>
12459  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12460  * from which the running page was served.<br><br>
12461  * <p>
12462  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12463  * <p>
12464  * Be aware that to enable the browser to parse an XML document, the server must set
12465  * the Content-Type header in the HTTP response to "text/xml".
12466  * @constructor
12467  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12468  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12469  * will be used to make the request.
12470  */
12471 Roo.data.HttpProxy = function(conn){
12472     Roo.data.HttpProxy.superclass.constructor.call(this);
12473     // is conn a conn config or a real conn?
12474     this.conn = conn;
12475     this.useAjax = !conn || !conn.events;
12476   
12477 };
12478
12479 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12480     // thse are take from connection...
12481     
12482     /**
12483      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12484      */
12485     /**
12486      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12487      * extra parameters to each request made by this object. (defaults to undefined)
12488      */
12489     /**
12490      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12491      *  to each request made by this object. (defaults to undefined)
12492      */
12493     /**
12494      * @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)
12495      */
12496     /**
12497      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12498      */
12499      /**
12500      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12501      * @type Boolean
12502      */
12503   
12504
12505     /**
12506      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12507      * @type Boolean
12508      */
12509     /**
12510      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12511      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12512      * a finer-grained basis than the DataProxy events.
12513      */
12514     getConnection : function(){
12515         return this.useAjax ? Roo.Ajax : this.conn;
12516     },
12517
12518     /**
12519      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12520      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12521      * process that block using the passed callback.
12522      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12523      * for the request to the remote server.
12524      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12525      * object into a block of Roo.data.Records.
12526      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12527      * The function must be passed <ul>
12528      * <li>The Record block object</li>
12529      * <li>The "arg" argument from the load function</li>
12530      * <li>A boolean success indicator</li>
12531      * </ul>
12532      * @param {Object} scope The scope in which to call the callback
12533      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12534      */
12535     load : function(params, reader, callback, scope, arg){
12536         if(this.fireEvent("beforeload", this, params) !== false){
12537             var  o = {
12538                 params : params || {},
12539                 request: {
12540                     callback : callback,
12541                     scope : scope,
12542                     arg : arg
12543                 },
12544                 reader: reader,
12545                 callback : this.loadResponse,
12546                 scope: this
12547             };
12548             if(this.useAjax){
12549                 Roo.applyIf(o, this.conn);
12550                 if(this.activeRequest){
12551                     Roo.Ajax.abort(this.activeRequest);
12552                 }
12553                 this.activeRequest = Roo.Ajax.request(o);
12554             }else{
12555                 this.conn.request(o);
12556             }
12557         }else{
12558             callback.call(scope||this, null, arg, false);
12559         }
12560     },
12561
12562     // private
12563     loadResponse : function(o, success, response){
12564         delete this.activeRequest;
12565         if(!success){
12566             this.fireEvent("loadexception", this, o, response);
12567             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12568             return;
12569         }
12570         var result;
12571         try {
12572             result = o.reader.read(response);
12573         }catch(e){
12574             this.fireEvent("loadexception", this, o, response, e);
12575             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12576             return;
12577         }
12578         
12579         this.fireEvent("load", this, o, o.request.arg);
12580         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12581     },
12582
12583     // private
12584     update : function(dataSet){
12585
12586     },
12587
12588     // private
12589     updateResponse : function(dataSet){
12590
12591     }
12592 });/*
12593  * Based on:
12594  * Ext JS Library 1.1.1
12595  * Copyright(c) 2006-2007, Ext JS, LLC.
12596  *
12597  * Originally Released Under LGPL - original licence link has changed is not relivant.
12598  *
12599  * Fork - LGPL
12600  * <script type="text/javascript">
12601  */
12602
12603 /**
12604  * @class Roo.data.ScriptTagProxy
12605  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12606  * other than the originating domain of the running page.<br><br>
12607  * <p>
12608  * <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
12609  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12610  * <p>
12611  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12612  * source code that is used as the source inside a &lt;script> tag.<br><br>
12613  * <p>
12614  * In order for the browser to process the returned data, the server must wrap the data object
12615  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12616  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12617  * depending on whether the callback name was passed:
12618  * <p>
12619  * <pre><code>
12620 boolean scriptTag = false;
12621 String cb = request.getParameter("callback");
12622 if (cb != null) {
12623     scriptTag = true;
12624     response.setContentType("text/javascript");
12625 } else {
12626     response.setContentType("application/x-json");
12627 }
12628 Writer out = response.getWriter();
12629 if (scriptTag) {
12630     out.write(cb + "(");
12631 }
12632 out.print(dataBlock.toJsonString());
12633 if (scriptTag) {
12634     out.write(");");
12635 }
12636 </pre></code>
12637  *
12638  * @constructor
12639  * @param {Object} config A configuration object.
12640  */
12641 Roo.data.ScriptTagProxy = function(config){
12642     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12643     Roo.apply(this, config);
12644     this.head = document.getElementsByTagName("head")[0];
12645 };
12646
12647 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12648
12649 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12650     /**
12651      * @cfg {String} url The URL from which to request the data object.
12652      */
12653     /**
12654      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12655      */
12656     timeout : 30000,
12657     /**
12658      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12659      * the server the name of the callback function set up by the load call to process the returned data object.
12660      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12661      * javascript output which calls this named function passing the data object as its only parameter.
12662      */
12663     callbackParam : "callback",
12664     /**
12665      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12666      * name to the request.
12667      */
12668     nocache : true,
12669
12670     /**
12671      * Load data from the configured URL, read the data object into
12672      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12673      * process that block using the passed callback.
12674      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12675      * for the request to the remote server.
12676      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12677      * object into a block of Roo.data.Records.
12678      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12679      * The function must be passed <ul>
12680      * <li>The Record block object</li>
12681      * <li>The "arg" argument from the load function</li>
12682      * <li>A boolean success indicator</li>
12683      * </ul>
12684      * @param {Object} scope The scope in which to call the callback
12685      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12686      */
12687     load : function(params, reader, callback, scope, arg){
12688         if(this.fireEvent("beforeload", this, params) !== false){
12689
12690             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12691
12692             var url = this.url;
12693             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12694             if(this.nocache){
12695                 url += "&_dc=" + (new Date().getTime());
12696             }
12697             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12698             var trans = {
12699                 id : transId,
12700                 cb : "stcCallback"+transId,
12701                 scriptId : "stcScript"+transId,
12702                 params : params,
12703                 arg : arg,
12704                 url : url,
12705                 callback : callback,
12706                 scope : scope,
12707                 reader : reader
12708             };
12709             var conn = this;
12710
12711             window[trans.cb] = function(o){
12712                 conn.handleResponse(o, trans);
12713             };
12714
12715             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12716
12717             if(this.autoAbort !== false){
12718                 this.abort();
12719             }
12720
12721             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12722
12723             var script = document.createElement("script");
12724             script.setAttribute("src", url);
12725             script.setAttribute("type", "text/javascript");
12726             script.setAttribute("id", trans.scriptId);
12727             this.head.appendChild(script);
12728
12729             this.trans = trans;
12730         }else{
12731             callback.call(scope||this, null, arg, false);
12732         }
12733     },
12734
12735     // private
12736     isLoading : function(){
12737         return this.trans ? true : false;
12738     },
12739
12740     /**
12741      * Abort the current server request.
12742      */
12743     abort : function(){
12744         if(this.isLoading()){
12745             this.destroyTrans(this.trans);
12746         }
12747     },
12748
12749     // private
12750     destroyTrans : function(trans, isLoaded){
12751         this.head.removeChild(document.getElementById(trans.scriptId));
12752         clearTimeout(trans.timeoutId);
12753         if(isLoaded){
12754             window[trans.cb] = undefined;
12755             try{
12756                 delete window[trans.cb];
12757             }catch(e){}
12758         }else{
12759             // if hasn't been loaded, wait for load to remove it to prevent script error
12760             window[trans.cb] = function(){
12761                 window[trans.cb] = undefined;
12762                 try{
12763                     delete window[trans.cb];
12764                 }catch(e){}
12765             };
12766         }
12767     },
12768
12769     // private
12770     handleResponse : function(o, trans){
12771         this.trans = false;
12772         this.destroyTrans(trans, true);
12773         var result;
12774         try {
12775             result = trans.reader.readRecords(o);
12776         }catch(e){
12777             this.fireEvent("loadexception", this, o, trans.arg, e);
12778             trans.callback.call(trans.scope||window, null, trans.arg, false);
12779             return;
12780         }
12781         this.fireEvent("load", this, o, trans.arg);
12782         trans.callback.call(trans.scope||window, result, trans.arg, true);
12783     },
12784
12785     // private
12786     handleFailure : function(trans){
12787         this.trans = false;
12788         this.destroyTrans(trans, false);
12789         this.fireEvent("loadexception", this, null, trans.arg);
12790         trans.callback.call(trans.scope||window, null, trans.arg, false);
12791     }
12792 });/*
12793  * Based on:
12794  * Ext JS Library 1.1.1
12795  * Copyright(c) 2006-2007, Ext JS, LLC.
12796  *
12797  * Originally Released Under LGPL - original licence link has changed is not relivant.
12798  *
12799  * Fork - LGPL
12800  * <script type="text/javascript">
12801  */
12802
12803 /**
12804  * @class Roo.data.JsonReader
12805  * @extends Roo.data.DataReader
12806  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12807  * based on mappings in a provided Roo.data.Record constructor.
12808  * 
12809  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12810  * in the reply previously. 
12811  * 
12812  * <p>
12813  * Example code:
12814  * <pre><code>
12815 var RecordDef = Roo.data.Record.create([
12816     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12817     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12818 ]);
12819 var myReader = new Roo.data.JsonReader({
12820     totalProperty: "results",    // The property which contains the total dataset size (optional)
12821     root: "rows",                // The property which contains an Array of row objects
12822     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12823 }, RecordDef);
12824 </code></pre>
12825  * <p>
12826  * This would consume a JSON file like this:
12827  * <pre><code>
12828 { 'results': 2, 'rows': [
12829     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12830     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12831 }
12832 </code></pre>
12833  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12834  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12835  * paged from the remote server.
12836  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12837  * @cfg {String} root name of the property which contains the Array of row objects.
12838  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12839  * @cfg {Array} fields Array of field definition objects
12840  * @constructor
12841  * Create a new JsonReader
12842  * @param {Object} meta Metadata configuration options
12843  * @param {Object} recordType Either an Array of field definition objects,
12844  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12845  */
12846 Roo.data.JsonReader = function(meta, recordType){
12847     
12848     meta = meta || {};
12849     // set some defaults:
12850     Roo.applyIf(meta, {
12851         totalProperty: 'total',
12852         successProperty : 'success',
12853         root : 'data',
12854         id : 'id'
12855     });
12856     
12857     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12858 };
12859 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12860     
12861     /**
12862      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12863      * Used by Store query builder to append _requestMeta to params.
12864      * 
12865      */
12866     metaFromRemote : false,
12867     /**
12868      * This method is only used by a DataProxy which has retrieved data from a remote server.
12869      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12870      * @return {Object} data A data block which is used by an Roo.data.Store object as
12871      * a cache of Roo.data.Records.
12872      */
12873     read : function(response){
12874         var json = response.responseText;
12875        
12876         var o = /* eval:var:o */ eval("("+json+")");
12877         if(!o) {
12878             throw {message: "JsonReader.read: Json object not found"};
12879         }
12880         
12881         if(o.metaData){
12882             
12883             delete this.ef;
12884             this.metaFromRemote = true;
12885             this.meta = o.metaData;
12886             this.recordType = Roo.data.Record.create(o.metaData.fields);
12887             this.onMetaChange(this.meta, this.recordType, o);
12888         }
12889         return this.readRecords(o);
12890     },
12891
12892     // private function a store will implement
12893     onMetaChange : function(meta, recordType, o){
12894
12895     },
12896
12897     /**
12898          * @ignore
12899          */
12900     simpleAccess: function(obj, subsc) {
12901         return obj[subsc];
12902     },
12903
12904         /**
12905          * @ignore
12906          */
12907     getJsonAccessor: function(){
12908         var re = /[\[\.]/;
12909         return function(expr) {
12910             try {
12911                 return(re.test(expr))
12912                     ? new Function("obj", "return obj." + expr)
12913                     : function(obj){
12914                         return obj[expr];
12915                     };
12916             } catch(e){}
12917             return Roo.emptyFn;
12918         };
12919     }(),
12920
12921     /**
12922      * Create a data block containing Roo.data.Records from an XML document.
12923      * @param {Object} o An object which contains an Array of row objects in the property specified
12924      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12925      * which contains the total size of the dataset.
12926      * @return {Object} data A data block which is used by an Roo.data.Store object as
12927      * a cache of Roo.data.Records.
12928      */
12929     readRecords : function(o){
12930         /**
12931          * After any data loads, the raw JSON data is available for further custom processing.
12932          * @type Object
12933          */
12934         this.o = o;
12935         var s = this.meta, Record = this.recordType,
12936             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12937
12938 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12939         if (!this.ef) {
12940             if(s.totalProperty) {
12941                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12942                 }
12943                 if(s.successProperty) {
12944                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12945                 }
12946                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12947                 if (s.id) {
12948                         var g = this.getJsonAccessor(s.id);
12949                         this.getId = function(rec) {
12950                                 var r = g(rec);  
12951                                 return (r === undefined || r === "") ? null : r;
12952                         };
12953                 } else {
12954                         this.getId = function(){return null;};
12955                 }
12956             this.ef = [];
12957             for(var jj = 0; jj < fl; jj++){
12958                 f = fi[jj];
12959                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12960                 this.ef[jj] = this.getJsonAccessor(map);
12961             }
12962         }
12963
12964         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12965         if(s.totalProperty){
12966             var vt = parseInt(this.getTotal(o), 10);
12967             if(!isNaN(vt)){
12968                 totalRecords = vt;
12969             }
12970         }
12971         if(s.successProperty){
12972             var vs = this.getSuccess(o);
12973             if(vs === false || vs === 'false'){
12974                 success = false;
12975             }
12976         }
12977         var records = [];
12978         for(var i = 0; i < c; i++){
12979                 var n = root[i];
12980             var values = {};
12981             var id = this.getId(n);
12982             for(var j = 0; j < fl; j++){
12983                 f = fi[j];
12984             var v = this.ef[j](n);
12985             if (!f.convert) {
12986                 Roo.log('missing convert for ' + f.name);
12987                 Roo.log(f);
12988                 continue;
12989             }
12990             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12991             }
12992             var record = new Record(values, id);
12993             record.json = n;
12994             records[i] = record;
12995         }
12996         return {
12997             raw : o,
12998             success : success,
12999             records : records,
13000             totalRecords : totalRecords
13001         };
13002     }
13003 });/*
13004  * Based on:
13005  * Ext JS Library 1.1.1
13006  * Copyright(c) 2006-2007, Ext JS, LLC.
13007  *
13008  * Originally Released Under LGPL - original licence link has changed is not relivant.
13009  *
13010  * Fork - LGPL
13011  * <script type="text/javascript">
13012  */
13013
13014 /**
13015  * @class Roo.data.ArrayReader
13016  * @extends Roo.data.DataReader
13017  * Data reader class to create an Array of Roo.data.Record objects from an Array.
13018  * Each element of that Array represents a row of data fields. The
13019  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
13020  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
13021  * <p>
13022  * Example code:.
13023  * <pre><code>
13024 var RecordDef = Roo.data.Record.create([
13025     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
13026     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
13027 ]);
13028 var myReader = new Roo.data.ArrayReader({
13029     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
13030 }, RecordDef);
13031 </code></pre>
13032  * <p>
13033  * This would consume an Array like this:
13034  * <pre><code>
13035 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
13036   </code></pre>
13037  
13038  * @constructor
13039  * Create a new JsonReader
13040  * @param {Object} meta Metadata configuration options.
13041  * @param {Object|Array} recordType Either an Array of field definition objects
13042  * 
13043  * @cfg {Array} fields Array of field definition objects
13044  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
13045  * as specified to {@link Roo.data.Record#create},
13046  * or an {@link Roo.data.Record} object
13047  *
13048  * 
13049  * created using {@link Roo.data.Record#create}.
13050  */
13051 Roo.data.ArrayReader = function(meta, recordType){
13052     
13053      
13054     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
13055 };
13056
13057 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
13058     /**
13059      * Create a data block containing Roo.data.Records from an XML document.
13060      * @param {Object} o An Array of row objects which represents the dataset.
13061      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
13062      * a cache of Roo.data.Records.
13063      */
13064     readRecords : function(o){
13065         var sid = this.meta ? this.meta.id : null;
13066         var recordType = this.recordType, fields = recordType.prototype.fields;
13067         var records = [];
13068         var root = o;
13069             for(var i = 0; i < root.length; i++){
13070                     var n = root[i];
13071                 var values = {};
13072                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
13073                 for(var j = 0, jlen = fields.length; j < jlen; j++){
13074                 var f = fields.items[j];
13075                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
13076                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
13077                 v = f.convert(v);
13078                 values[f.name] = v;
13079             }
13080                 var record = new recordType(values, id);
13081                 record.json = n;
13082                 records[records.length] = record;
13083             }
13084             return {
13085                 records : records,
13086                 totalRecords : records.length
13087             };
13088     }
13089 });/*
13090  * - LGPL
13091  * * 
13092  */
13093
13094 /**
13095  * @class Roo.bootstrap.ComboBox
13096  * @extends Roo.bootstrap.TriggerField
13097  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
13098  * @cfg {Boolean} append (true|false) default false
13099  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
13100  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
13101  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
13102  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
13103  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
13104  * @cfg {Boolean} animate default true
13105  * @cfg {Boolean} emptyResultText only for touch device
13106  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
13107  * @cfg {String} emptyTitle default ''
13108  * @constructor
13109  * Create a new ComboBox.
13110  * @param {Object} config Configuration options
13111  */
13112 Roo.bootstrap.ComboBox = function(config){
13113     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
13114     this.addEvents({
13115         /**
13116          * @event expand
13117          * Fires when the dropdown list is expanded
13118         * @param {Roo.bootstrap.ComboBox} combo This combo box
13119         */
13120         'expand' : true,
13121         /**
13122          * @event collapse
13123          * Fires when the dropdown list is collapsed
13124         * @param {Roo.bootstrap.ComboBox} combo This combo box
13125         */
13126         'collapse' : true,
13127         /**
13128          * @event beforeselect
13129          * Fires before a list item is selected. Return false to cancel the selection.
13130         * @param {Roo.bootstrap.ComboBox} combo This combo box
13131         * @param {Roo.data.Record} record The data record returned from the underlying store
13132         * @param {Number} index The index of the selected item in the dropdown list
13133         */
13134         'beforeselect' : true,
13135         /**
13136          * @event select
13137          * Fires when a list item is selected
13138         * @param {Roo.bootstrap.ComboBox} combo This combo box
13139         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
13140         * @param {Number} index The index of the selected item in the dropdown list
13141         */
13142         'select' : true,
13143         /**
13144          * @event beforequery
13145          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
13146          * The event object passed has these properties:
13147         * @param {Roo.bootstrap.ComboBox} combo This combo box
13148         * @param {String} query The query
13149         * @param {Boolean} forceAll true to force "all" query
13150         * @param {Boolean} cancel true to cancel the query
13151         * @param {Object} e The query event object
13152         */
13153         'beforequery': true,
13154          /**
13155          * @event add
13156          * Fires when the 'add' icon is pressed (add a listener to enable add button)
13157         * @param {Roo.bootstrap.ComboBox} combo This combo box
13158         */
13159         'add' : true,
13160         /**
13161          * @event edit
13162          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
13163         * @param {Roo.bootstrap.ComboBox} combo This combo box
13164         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13165         */
13166         'edit' : true,
13167         /**
13168          * @event remove
13169          * Fires when the remove value from the combobox array
13170         * @param {Roo.bootstrap.ComboBox} combo This combo box
13171         */
13172         'remove' : true,
13173         /**
13174          * @event afterremove
13175          * Fires when the remove value from the combobox array
13176         * @param {Roo.bootstrap.ComboBox} combo This combo box
13177         */
13178         'afterremove' : true,
13179         /**
13180          * @event specialfilter
13181          * Fires when specialfilter
13182             * @param {Roo.bootstrap.ComboBox} combo This combo box
13183             */
13184         'specialfilter' : true,
13185         /**
13186          * @event tick
13187          * Fires when tick the element
13188             * @param {Roo.bootstrap.ComboBox} combo This combo box
13189             */
13190         'tick' : true,
13191         /**
13192          * @event touchviewdisplay
13193          * Fires when touch view require special display (default is using displayField)
13194             * @param {Roo.bootstrap.ComboBox} combo This combo box
13195             * @param {Object} cfg set html .
13196             */
13197         'touchviewdisplay' : true
13198         
13199     });
13200     
13201     this.item = [];
13202     this.tickItems = [];
13203     
13204     this.selectedIndex = -1;
13205     if(this.mode == 'local'){
13206         if(config.queryDelay === undefined){
13207             this.queryDelay = 10;
13208         }
13209         if(config.minChars === undefined){
13210             this.minChars = 0;
13211         }
13212     }
13213 };
13214
13215 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13216      
13217     /**
13218      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13219      * rendering into an Roo.Editor, defaults to false)
13220      */
13221     /**
13222      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13223      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13224      */
13225     /**
13226      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13227      */
13228     /**
13229      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13230      * the dropdown list (defaults to undefined, with no header element)
13231      */
13232
13233      /**
13234      * @cfg {String/Roo.Template} tpl The template to use to render the output
13235      */
13236      
13237      /**
13238      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13239      */
13240     listWidth: undefined,
13241     /**
13242      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13243      * mode = 'remote' or 'text' if mode = 'local')
13244      */
13245     displayField: undefined,
13246     
13247     /**
13248      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13249      * mode = 'remote' or 'value' if mode = 'local'). 
13250      * Note: use of a valueField requires the user make a selection
13251      * in order for a value to be mapped.
13252      */
13253     valueField: undefined,
13254     /**
13255      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13256      */
13257     modalTitle : '',
13258     
13259     /**
13260      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13261      * field's data value (defaults to the underlying DOM element's name)
13262      */
13263     hiddenName: undefined,
13264     /**
13265      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13266      */
13267     listClass: '',
13268     /**
13269      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13270      */
13271     selectedClass: 'active',
13272     
13273     /**
13274      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13275      */
13276     shadow:'sides',
13277     /**
13278      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13279      * anchor positions (defaults to 'tl-bl')
13280      */
13281     listAlign: 'tl-bl?',
13282     /**
13283      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13284      */
13285     maxHeight: 300,
13286     /**
13287      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13288      * query specified by the allQuery config option (defaults to 'query')
13289      */
13290     triggerAction: 'query',
13291     /**
13292      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13293      * (defaults to 4, does not apply if editable = false)
13294      */
13295     minChars : 4,
13296     /**
13297      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13298      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13299      */
13300     typeAhead: false,
13301     /**
13302      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13303      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13304      */
13305     queryDelay: 500,
13306     /**
13307      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13308      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13309      */
13310     pageSize: 0,
13311     /**
13312      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13313      * when editable = true (defaults to false)
13314      */
13315     selectOnFocus:false,
13316     /**
13317      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13318      */
13319     queryParam: 'query',
13320     /**
13321      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13322      * when mode = 'remote' (defaults to 'Loading...')
13323      */
13324     loadingText: 'Loading...',
13325     /**
13326      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13327      */
13328     resizable: false,
13329     /**
13330      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13331      */
13332     handleHeight : 8,
13333     /**
13334      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13335      * traditional select (defaults to true)
13336      */
13337     editable: true,
13338     /**
13339      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13340      */
13341     allQuery: '',
13342     /**
13343      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13344      */
13345     mode: 'remote',
13346     /**
13347      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13348      * listWidth has a higher value)
13349      */
13350     minListWidth : 70,
13351     /**
13352      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13353      * allow the user to set arbitrary text into the field (defaults to false)
13354      */
13355     forceSelection:false,
13356     /**
13357      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13358      * if typeAhead = true (defaults to 250)
13359      */
13360     typeAheadDelay : 250,
13361     /**
13362      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13363      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13364      */
13365     valueNotFoundText : undefined,
13366     /**
13367      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13368      */
13369     blockFocus : false,
13370     
13371     /**
13372      * @cfg {Boolean} disableClear Disable showing of clear button.
13373      */
13374     disableClear : false,
13375     /**
13376      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13377      */
13378     alwaysQuery : false,
13379     
13380     /**
13381      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13382      */
13383     multiple : false,
13384     
13385     /**
13386      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
13387      */
13388     invalidClass : "has-warning",
13389     
13390     /**
13391      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
13392      */
13393     validClass : "has-success",
13394     
13395     /**
13396      * @cfg {Boolean} specialFilter (true|false) special filter default false
13397      */
13398     specialFilter : false,
13399     
13400     /**
13401      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13402      */
13403     mobileTouchView : true,
13404     
13405     /**
13406      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13407      */
13408     useNativeIOS : false,
13409     
13410     /**
13411      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13412      */
13413     mobile_restrict_height : false,
13414     
13415     ios_options : false,
13416     
13417     //private
13418     addicon : false,
13419     editicon: false,
13420     
13421     page: 0,
13422     hasQuery: false,
13423     append: false,
13424     loadNext: false,
13425     autoFocus : true,
13426     tickable : false,
13427     btnPosition : 'right',
13428     triggerList : true,
13429     showToggleBtn : true,
13430     animate : true,
13431     emptyResultText: 'Empty',
13432     triggerText : 'Select',
13433     emptyTitle : '',
13434     
13435     // element that contains real text value.. (when hidden is used..)
13436     
13437     getAutoCreate : function()
13438     {   
13439         var cfg = false;
13440         //render
13441         /*
13442          * Render classic select for iso
13443          */
13444         
13445         if(Roo.isIOS && this.useNativeIOS){
13446             cfg = this.getAutoCreateNativeIOS();
13447             return cfg;
13448         }
13449         
13450         /*
13451          * Touch Devices
13452          */
13453         
13454         if(Roo.isTouch && this.mobileTouchView){
13455             cfg = this.getAutoCreateTouchView();
13456             return cfg;;
13457         }
13458         
13459         /*
13460          *  Normal ComboBox
13461          */
13462         if(!this.tickable){
13463             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13464             return cfg;
13465         }
13466         
13467         /*
13468          *  ComboBox with tickable selections
13469          */
13470              
13471         var align = this.labelAlign || this.parentLabelAlign();
13472         
13473         cfg = {
13474             cls : 'form-group roo-combobox-tickable' //input-group
13475         };
13476         
13477         var btn_text_select = '';
13478         var btn_text_done = '';
13479         var btn_text_cancel = '';
13480         
13481         if (this.btn_text_show) {
13482             btn_text_select = 'Select';
13483             btn_text_done = 'Done';
13484             btn_text_cancel = 'Cancel'; 
13485         }
13486         
13487         var buttons = {
13488             tag : 'div',
13489             cls : 'tickable-buttons',
13490             cn : [
13491                 {
13492                     tag : 'button',
13493                     type : 'button',
13494                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13495                     //html : this.triggerText
13496                     html: btn_text_select
13497                 },
13498                 {
13499                     tag : 'button',
13500                     type : 'button',
13501                     name : 'ok',
13502                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13503                     //html : 'Done'
13504                     html: btn_text_done
13505                 },
13506                 {
13507                     tag : 'button',
13508                     type : 'button',
13509                     name : 'cancel',
13510                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13511                     //html : 'Cancel'
13512                     html: btn_text_cancel
13513                 }
13514             ]
13515         };
13516         
13517         if(this.editable){
13518             buttons.cn.unshift({
13519                 tag: 'input',
13520                 cls: 'roo-select2-search-field-input'
13521             });
13522         }
13523         
13524         var _this = this;
13525         
13526         Roo.each(buttons.cn, function(c){
13527             if (_this.size) {
13528                 c.cls += ' btn-' + _this.size;
13529             }
13530
13531             if (_this.disabled) {
13532                 c.disabled = true;
13533             }
13534         });
13535         
13536         var box = {
13537             tag: 'div',
13538             style : 'display: contents',
13539             cn: [
13540                 {
13541                     tag: 'input',
13542                     type : 'hidden',
13543                     cls: 'form-hidden-field'
13544                 },
13545                 {
13546                     tag: 'ul',
13547                     cls: 'roo-select2-choices',
13548                     cn:[
13549                         {
13550                             tag: 'li',
13551                             cls: 'roo-select2-search-field',
13552                             cn: [
13553                                 buttons
13554                             ]
13555                         }
13556                     ]
13557                 }
13558             ]
13559         };
13560         
13561         var combobox = {
13562             cls: 'roo-select2-container input-group roo-select2-container-multi',
13563             cn: [
13564                 
13565                 box
13566 //                {
13567 //                    tag: 'ul',
13568 //                    cls: 'typeahead typeahead-long dropdown-menu',
13569 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13570 //                }
13571             ]
13572         };
13573         
13574         if(this.hasFeedback && !this.allowBlank){
13575             
13576             var feedback = {
13577                 tag: 'span',
13578                 cls: 'glyphicon form-control-feedback'
13579             };
13580
13581             combobox.cn.push(feedback);
13582         }
13583         
13584         var indicator = {
13585             tag : 'i',
13586             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13587             tooltip : 'This field is required'
13588         };
13589         if (Roo.bootstrap.version == 4) {
13590             indicator = {
13591                 tag : 'i',
13592                 style : 'display:none'
13593             };
13594         }
13595         if (align ==='left' && this.fieldLabel.length) {
13596             
13597             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13598             
13599             cfg.cn = [
13600                 indicator,
13601                 {
13602                     tag: 'label',
13603                     'for' :  id,
13604                     cls : 'control-label col-form-label',
13605                     html : this.fieldLabel
13606
13607                 },
13608                 {
13609                     cls : "", 
13610                     cn: [
13611                         combobox
13612                     ]
13613                 }
13614
13615             ];
13616             
13617             var labelCfg = cfg.cn[1];
13618             var contentCfg = cfg.cn[2];
13619             
13620
13621             if(this.indicatorpos == 'right'){
13622                 
13623                 cfg.cn = [
13624                     {
13625                         tag: 'label',
13626                         'for' :  id,
13627                         cls : 'control-label col-form-label',
13628                         cn : [
13629                             {
13630                                 tag : 'span',
13631                                 html : this.fieldLabel
13632                             },
13633                             indicator
13634                         ]
13635                     },
13636                     {
13637                         cls : "",
13638                         cn: [
13639                             combobox
13640                         ]
13641                     }
13642
13643                 ];
13644                 
13645                 
13646                 
13647                 labelCfg = cfg.cn[0];
13648                 contentCfg = cfg.cn[1];
13649             
13650             }
13651             
13652             if(this.labelWidth > 12){
13653                 labelCfg.style = "width: " + this.labelWidth + 'px';
13654             }
13655             
13656             if(this.labelWidth < 13 && this.labelmd == 0){
13657                 this.labelmd = this.labelWidth;
13658             }
13659             
13660             if(this.labellg > 0){
13661                 labelCfg.cls += ' col-lg-' + this.labellg;
13662                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13663             }
13664             
13665             if(this.labelmd > 0){
13666                 labelCfg.cls += ' col-md-' + this.labelmd;
13667                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13668             }
13669             
13670             if(this.labelsm > 0){
13671                 labelCfg.cls += ' col-sm-' + this.labelsm;
13672                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13673             }
13674             
13675             if(this.labelxs > 0){
13676                 labelCfg.cls += ' col-xs-' + this.labelxs;
13677                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13678             }
13679                 
13680                 
13681         } else if ( this.fieldLabel.length) {
13682 //                Roo.log(" label");
13683                  cfg.cn = [
13684                    indicator,
13685                     {
13686                         tag: 'label',
13687                         //cls : 'input-group-addon',
13688                         html : this.fieldLabel
13689                     },
13690                     combobox
13691                 ];
13692                 
13693                 if(this.indicatorpos == 'right'){
13694                     cfg.cn = [
13695                         {
13696                             tag: 'label',
13697                             //cls : 'input-group-addon',
13698                             html : this.fieldLabel
13699                         },
13700                         indicator,
13701                         combobox
13702                     ];
13703                     
13704                 }
13705
13706         } else {
13707             
13708 //                Roo.log(" no label && no align");
13709                 cfg = combobox
13710                      
13711                 
13712         }
13713          
13714         var settings=this;
13715         ['xs','sm','md','lg'].map(function(size){
13716             if (settings[size]) {
13717                 cfg.cls += ' col-' + size + '-' + settings[size];
13718             }
13719         });
13720         
13721         return cfg;
13722         
13723     },
13724     
13725     _initEventsCalled : false,
13726     
13727     // private
13728     initEvents: function()
13729     {   
13730         if (this._initEventsCalled) { // as we call render... prevent looping...
13731             return;
13732         }
13733         this._initEventsCalled = true;
13734         
13735         if (!this.store) {
13736             throw "can not find store for combo";
13737         }
13738         
13739         this.indicator = this.indicatorEl();
13740         
13741         this.store = Roo.factory(this.store, Roo.data);
13742         this.store.parent = this;
13743         
13744         // if we are building from html. then this element is so complex, that we can not really
13745         // use the rendered HTML.
13746         // so we have to trash and replace the previous code.
13747         if (Roo.XComponent.build_from_html) {
13748             // remove this element....
13749             var e = this.el.dom, k=0;
13750             while (e ) { e = e.previousSibling;  ++k;}
13751
13752             this.el.remove();
13753             
13754             this.el=false;
13755             this.rendered = false;
13756             
13757             this.render(this.parent().getChildContainer(true), k);
13758         }
13759         
13760         if(Roo.isIOS && this.useNativeIOS){
13761             this.initIOSView();
13762             return;
13763         }
13764         
13765         /*
13766          * Touch Devices
13767          */
13768         
13769         if(Roo.isTouch && this.mobileTouchView){
13770             this.initTouchView();
13771             return;
13772         }
13773         
13774         if(this.tickable){
13775             this.initTickableEvents();
13776             return;
13777         }
13778         
13779         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13780         
13781         if(this.hiddenName){
13782             
13783             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13784             
13785             this.hiddenField.dom.value =
13786                 this.hiddenValue !== undefined ? this.hiddenValue :
13787                 this.value !== undefined ? this.value : '';
13788
13789             // prevent input submission
13790             this.el.dom.removeAttribute('name');
13791             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13792              
13793              
13794         }
13795         //if(Roo.isGecko){
13796         //    this.el.dom.setAttribute('autocomplete', 'off');
13797         //}
13798         
13799         var cls = 'x-combo-list';
13800         
13801         //this.list = new Roo.Layer({
13802         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13803         //});
13804         
13805         var _this = this;
13806         
13807         (function(){
13808             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13809             _this.list.setWidth(lw);
13810         }).defer(100);
13811         
13812         this.list.on('mouseover', this.onViewOver, this);
13813         this.list.on('mousemove', this.onViewMove, this);
13814         this.list.on('scroll', this.onViewScroll, this);
13815         
13816         /*
13817         this.list.swallowEvent('mousewheel');
13818         this.assetHeight = 0;
13819
13820         if(this.title){
13821             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13822             this.assetHeight += this.header.getHeight();
13823         }
13824
13825         this.innerList = this.list.createChild({cls:cls+'-inner'});
13826         this.innerList.on('mouseover', this.onViewOver, this);
13827         this.innerList.on('mousemove', this.onViewMove, this);
13828         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13829         
13830         if(this.allowBlank && !this.pageSize && !this.disableClear){
13831             this.footer = this.list.createChild({cls:cls+'-ft'});
13832             this.pageTb = new Roo.Toolbar(this.footer);
13833            
13834         }
13835         if(this.pageSize){
13836             this.footer = this.list.createChild({cls:cls+'-ft'});
13837             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13838                     {pageSize: this.pageSize});
13839             
13840         }
13841         
13842         if (this.pageTb && this.allowBlank && !this.disableClear) {
13843             var _this = this;
13844             this.pageTb.add(new Roo.Toolbar.Fill(), {
13845                 cls: 'x-btn-icon x-btn-clear',
13846                 text: '&#160;',
13847                 handler: function()
13848                 {
13849                     _this.collapse();
13850                     _this.clearValue();
13851                     _this.onSelect(false, -1);
13852                 }
13853             });
13854         }
13855         if (this.footer) {
13856             this.assetHeight += this.footer.getHeight();
13857         }
13858         */
13859             
13860         if(!this.tpl){
13861             this.tpl = Roo.bootstrap.version == 4 ?
13862                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
13863                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13864         }
13865
13866         this.view = new Roo.View(this.list, this.tpl, {
13867             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13868         });
13869         //this.view.wrapEl.setDisplayed(false);
13870         this.view.on('click', this.onViewClick, this);
13871         
13872         
13873         this.store.on('beforeload', this.onBeforeLoad, this);
13874         this.store.on('load', this.onLoad, this);
13875         this.store.on('loadexception', this.onLoadException, this);
13876         /*
13877         if(this.resizable){
13878             this.resizer = new Roo.Resizable(this.list,  {
13879                pinned:true, handles:'se'
13880             });
13881             this.resizer.on('resize', function(r, w, h){
13882                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13883                 this.listWidth = w;
13884                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13885                 this.restrictHeight();
13886             }, this);
13887             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13888         }
13889         */
13890         if(!this.editable){
13891             this.editable = true;
13892             this.setEditable(false);
13893         }
13894         
13895         /*
13896         
13897         if (typeof(this.events.add.listeners) != 'undefined') {
13898             
13899             this.addicon = this.wrap.createChild(
13900                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13901        
13902             this.addicon.on('click', function(e) {
13903                 this.fireEvent('add', this);
13904             }, this);
13905         }
13906         if (typeof(this.events.edit.listeners) != 'undefined') {
13907             
13908             this.editicon = this.wrap.createChild(
13909                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13910             if (this.addicon) {
13911                 this.editicon.setStyle('margin-left', '40px');
13912             }
13913             this.editicon.on('click', function(e) {
13914                 
13915                 // we fire even  if inothing is selected..
13916                 this.fireEvent('edit', this, this.lastData );
13917                 
13918             }, this);
13919         }
13920         */
13921         
13922         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13923             "up" : function(e){
13924                 this.inKeyMode = true;
13925                 this.selectPrev();
13926             },
13927
13928             "down" : function(e){
13929                 if(!this.isExpanded()){
13930                     this.onTriggerClick();
13931                 }else{
13932                     this.inKeyMode = true;
13933                     this.selectNext();
13934                 }
13935             },
13936
13937             "enter" : function(e){
13938 //                this.onViewClick();
13939                 //return true;
13940                 this.collapse();
13941                 
13942                 if(this.fireEvent("specialkey", this, e)){
13943                     this.onViewClick(false);
13944                 }
13945                 
13946                 return true;
13947             },
13948
13949             "esc" : function(e){
13950                 this.collapse();
13951             },
13952
13953             "tab" : function(e){
13954                 this.collapse();
13955                 
13956                 if(this.fireEvent("specialkey", this, e)){
13957                     this.onViewClick(false);
13958                 }
13959                 
13960                 return true;
13961             },
13962
13963             scope : this,
13964
13965             doRelay : function(foo, bar, hname){
13966                 if(hname == 'down' || this.scope.isExpanded()){
13967                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13968                 }
13969                 return true;
13970             },
13971
13972             forceKeyDown: true
13973         });
13974         
13975         
13976         this.queryDelay = Math.max(this.queryDelay || 10,
13977                 this.mode == 'local' ? 10 : 250);
13978         
13979         
13980         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13981         
13982         if(this.typeAhead){
13983             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13984         }
13985         if(this.editable !== false){
13986             this.inputEl().on("keyup", this.onKeyUp, this);
13987         }
13988         if(this.forceSelection){
13989             this.inputEl().on('blur', this.doForce, this);
13990         }
13991         
13992         if(this.multiple){
13993             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13994             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13995         }
13996     },
13997     
13998     initTickableEvents: function()
13999     {   
14000         this.createList();
14001         
14002         if(this.hiddenName){
14003             
14004             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14005             
14006             this.hiddenField.dom.value =
14007                 this.hiddenValue !== undefined ? this.hiddenValue :
14008                 this.value !== undefined ? this.value : '';
14009
14010             // prevent input submission
14011             this.el.dom.removeAttribute('name');
14012             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14013              
14014              
14015         }
14016         
14017 //        this.list = this.el.select('ul.dropdown-menu',true).first();
14018         
14019         this.choices = this.el.select('ul.roo-select2-choices', true).first();
14020         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14021         if(this.triggerList){
14022             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
14023         }
14024          
14025         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
14026         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
14027         
14028         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
14029         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
14030         
14031         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
14032         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
14033         
14034         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
14035         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
14036         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
14037         
14038         this.okBtn.hide();
14039         this.cancelBtn.hide();
14040         
14041         var _this = this;
14042         
14043         (function(){
14044             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
14045             _this.list.setWidth(lw);
14046         }).defer(100);
14047         
14048         this.list.on('mouseover', this.onViewOver, this);
14049         this.list.on('mousemove', this.onViewMove, this);
14050         
14051         this.list.on('scroll', this.onViewScroll, this);
14052         
14053         if(!this.tpl){
14054             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
14055                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
14056         }
14057
14058         this.view = new Roo.View(this.list, this.tpl, {
14059             singleSelect:true,
14060             tickable:true,
14061             parent:this,
14062             store: this.store,
14063             selectedClass: this.selectedClass
14064         });
14065         
14066         //this.view.wrapEl.setDisplayed(false);
14067         this.view.on('click', this.onViewClick, this);
14068         
14069         
14070         
14071         this.store.on('beforeload', this.onBeforeLoad, this);
14072         this.store.on('load', this.onLoad, this);
14073         this.store.on('loadexception', this.onLoadException, this);
14074         
14075         if(this.editable){
14076             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
14077                 "up" : function(e){
14078                     this.inKeyMode = true;
14079                     this.selectPrev();
14080                 },
14081
14082                 "down" : function(e){
14083                     this.inKeyMode = true;
14084                     this.selectNext();
14085                 },
14086
14087                 "enter" : function(e){
14088                     if(this.fireEvent("specialkey", this, e)){
14089                         this.onViewClick(false);
14090                     }
14091                     
14092                     return true;
14093                 },
14094
14095                 "esc" : function(e){
14096                     this.onTickableFooterButtonClick(e, false, false);
14097                 },
14098
14099                 "tab" : function(e){
14100                     this.fireEvent("specialkey", this, e);
14101                     
14102                     this.onTickableFooterButtonClick(e, false, false);
14103                     
14104                     return true;
14105                 },
14106
14107                 scope : this,
14108
14109                 doRelay : function(e, fn, key){
14110                     if(this.scope.isExpanded()){
14111                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14112                     }
14113                     return true;
14114                 },
14115
14116                 forceKeyDown: true
14117             });
14118         }
14119         
14120         this.queryDelay = Math.max(this.queryDelay || 10,
14121                 this.mode == 'local' ? 10 : 250);
14122         
14123         
14124         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14125         
14126         if(this.typeAhead){
14127             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14128         }
14129         
14130         if(this.editable !== false){
14131             this.tickableInputEl().on("keyup", this.onKeyUp, this);
14132         }
14133         
14134         this.indicator = this.indicatorEl();
14135         
14136         if(this.indicator){
14137             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
14138             this.indicator.hide();
14139         }
14140         
14141     },
14142
14143     onDestroy : function(){
14144         if(this.view){
14145             this.view.setStore(null);
14146             this.view.el.removeAllListeners();
14147             this.view.el.remove();
14148             this.view.purgeListeners();
14149         }
14150         if(this.list){
14151             this.list.dom.innerHTML  = '';
14152         }
14153         
14154         if(this.store){
14155             this.store.un('beforeload', this.onBeforeLoad, this);
14156             this.store.un('load', this.onLoad, this);
14157             this.store.un('loadexception', this.onLoadException, this);
14158         }
14159         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
14160     },
14161
14162     // private
14163     fireKey : function(e){
14164         if(e.isNavKeyPress() && !this.list.isVisible()){
14165             this.fireEvent("specialkey", this, e);
14166         }
14167     },
14168
14169     // private
14170     onResize: function(w, h){
14171 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14172 //        
14173 //        if(typeof w != 'number'){
14174 //            // we do not handle it!?!?
14175 //            return;
14176 //        }
14177 //        var tw = this.trigger.getWidth();
14178 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
14179 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
14180 //        var x = w - tw;
14181 //        this.inputEl().setWidth( this.adjustWidth('input', x));
14182 //            
14183 //        //this.trigger.setStyle('left', x+'px');
14184 //        
14185 //        if(this.list && this.listWidth === undefined){
14186 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14187 //            this.list.setWidth(lw);
14188 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14189 //        }
14190         
14191     
14192         
14193     },
14194
14195     /**
14196      * Allow or prevent the user from directly editing the field text.  If false is passed,
14197      * the user will only be able to select from the items defined in the dropdown list.  This method
14198      * is the runtime equivalent of setting the 'editable' config option at config time.
14199      * @param {Boolean} value True to allow the user to directly edit the field text
14200      */
14201     setEditable : function(value){
14202         if(value == this.editable){
14203             return;
14204         }
14205         this.editable = value;
14206         if(!value){
14207             this.inputEl().dom.setAttribute('readOnly', true);
14208             this.inputEl().on('mousedown', this.onTriggerClick,  this);
14209             this.inputEl().addClass('x-combo-noedit');
14210         }else{
14211             this.inputEl().dom.setAttribute('readOnly', false);
14212             this.inputEl().un('mousedown', this.onTriggerClick,  this);
14213             this.inputEl().removeClass('x-combo-noedit');
14214         }
14215     },
14216
14217     // private
14218     
14219     onBeforeLoad : function(combo,opts){
14220         if(!this.hasFocus){
14221             return;
14222         }
14223          if (!opts.add) {
14224             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14225          }
14226         this.restrictHeight();
14227         this.selectedIndex = -1;
14228     },
14229
14230     // private
14231     onLoad : function(){
14232         
14233         this.hasQuery = false;
14234         
14235         if(!this.hasFocus){
14236             return;
14237         }
14238         
14239         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14240             this.loading.hide();
14241         }
14242         
14243         if(this.store.getCount() > 0){
14244             
14245             this.expand();
14246             this.restrictHeight();
14247             if(this.lastQuery == this.allQuery){
14248                 if(this.editable && !this.tickable){
14249                     this.inputEl().dom.select();
14250                 }
14251                 
14252                 if(
14253                     !this.selectByValue(this.value, true) &&
14254                     this.autoFocus && 
14255                     (
14256                         !this.store.lastOptions ||
14257                         typeof(this.store.lastOptions.add) == 'undefined' || 
14258                         this.store.lastOptions.add != true
14259                     )
14260                 ){
14261                     this.select(0, true);
14262                 }
14263             }else{
14264                 if(this.autoFocus){
14265                     this.selectNext();
14266                 }
14267                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14268                     this.taTask.delay(this.typeAheadDelay);
14269                 }
14270             }
14271         }else{
14272             this.onEmptyResults();
14273         }
14274         
14275         //this.el.focus();
14276     },
14277     // private
14278     onLoadException : function()
14279     {
14280         this.hasQuery = false;
14281         
14282         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14283             this.loading.hide();
14284         }
14285         
14286         if(this.tickable && this.editable){
14287             return;
14288         }
14289         
14290         this.collapse();
14291         // only causes errors at present
14292         //Roo.log(this.store.reader.jsonData);
14293         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14294             // fixme
14295             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14296         //}
14297         
14298         
14299     },
14300     // private
14301     onTypeAhead : function(){
14302         if(this.store.getCount() > 0){
14303             var r = this.store.getAt(0);
14304             var newValue = r.data[this.displayField];
14305             var len = newValue.length;
14306             var selStart = this.getRawValue().length;
14307             
14308             if(selStart != len){
14309                 this.setRawValue(newValue);
14310                 this.selectText(selStart, newValue.length);
14311             }
14312         }
14313     },
14314
14315     // private
14316     onSelect : function(record, index){
14317         
14318         if(this.fireEvent('beforeselect', this, record, index) !== false){
14319         
14320             this.setFromData(index > -1 ? record.data : false);
14321             
14322             this.collapse();
14323             this.fireEvent('select', this, record, index);
14324         }
14325     },
14326
14327     /**
14328      * Returns the currently selected field value or empty string if no value is set.
14329      * @return {String} value The selected value
14330      */
14331     getValue : function()
14332     {
14333         if(Roo.isIOS && this.useNativeIOS){
14334             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14335         }
14336         
14337         if(this.multiple){
14338             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14339         }
14340         
14341         if(this.valueField){
14342             return typeof this.value != 'undefined' ? this.value : '';
14343         }else{
14344             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14345         }
14346     },
14347     
14348     getRawValue : function()
14349     {
14350         if(Roo.isIOS && this.useNativeIOS){
14351             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14352         }
14353         
14354         var v = this.inputEl().getValue();
14355         
14356         return v;
14357     },
14358
14359     /**
14360      * Clears any text/value currently set in the field
14361      */
14362     clearValue : function(){
14363         
14364         if(this.hiddenField){
14365             this.hiddenField.dom.value = '';
14366         }
14367         this.value = '';
14368         this.setRawValue('');
14369         this.lastSelectionText = '';
14370         this.lastData = false;
14371         
14372         var close = this.closeTriggerEl();
14373         
14374         if(close){
14375             close.hide();
14376         }
14377         
14378         this.validate();
14379         
14380     },
14381
14382     /**
14383      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14384      * will be displayed in the field.  If the value does not match the data value of an existing item,
14385      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14386      * Otherwise the field will be blank (although the value will still be set).
14387      * @param {String} value The value to match
14388      */
14389     setValue : function(v)
14390     {
14391         if(Roo.isIOS && this.useNativeIOS){
14392             this.setIOSValue(v);
14393             return;
14394         }
14395         
14396         if(this.multiple){
14397             this.syncValue();
14398             return;
14399         }
14400         
14401         var text = v;
14402         if(this.valueField){
14403             var r = this.findRecord(this.valueField, v);
14404             if(r){
14405                 text = r.data[this.displayField];
14406             }else if(this.valueNotFoundText !== undefined){
14407                 text = this.valueNotFoundText;
14408             }
14409         }
14410         this.lastSelectionText = text;
14411         if(this.hiddenField){
14412             this.hiddenField.dom.value = v;
14413         }
14414         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14415         this.value = v;
14416         
14417         var close = this.closeTriggerEl();
14418         
14419         if(close){
14420             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14421         }
14422         
14423         this.validate();
14424     },
14425     /**
14426      * @property {Object} the last set data for the element
14427      */
14428     
14429     lastData : false,
14430     /**
14431      * Sets the value of the field based on a object which is related to the record format for the store.
14432      * @param {Object} value the value to set as. or false on reset?
14433      */
14434     setFromData : function(o){
14435         
14436         if(this.multiple){
14437             this.addItem(o);
14438             return;
14439         }
14440             
14441         var dv = ''; // display value
14442         var vv = ''; // value value..
14443         this.lastData = o;
14444         if (this.displayField) {
14445             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14446         } else {
14447             // this is an error condition!!!
14448             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14449         }
14450         
14451         if(this.valueField){
14452             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14453         }
14454         
14455         var close = this.closeTriggerEl();
14456         
14457         if(close){
14458             if(dv.length || vv * 1 > 0){
14459                 close.show() ;
14460                 this.blockFocus=true;
14461             } else {
14462                 close.hide();
14463             }             
14464         }
14465         
14466         if(this.hiddenField){
14467             this.hiddenField.dom.value = vv;
14468             
14469             this.lastSelectionText = dv;
14470             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14471             this.value = vv;
14472             return;
14473         }
14474         // no hidden field.. - we store the value in 'value', but still display
14475         // display field!!!!
14476         this.lastSelectionText = dv;
14477         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14478         this.value = vv;
14479         
14480         
14481         
14482     },
14483     // private
14484     reset : function(){
14485         // overridden so that last data is reset..
14486         
14487         if(this.multiple){
14488             this.clearItem();
14489             return;
14490         }
14491         
14492         this.setValue(this.originalValue);
14493         //this.clearInvalid();
14494         this.lastData = false;
14495         if (this.view) {
14496             this.view.clearSelections();
14497         }
14498         
14499         this.validate();
14500     },
14501     // private
14502     findRecord : function(prop, value){
14503         var record;
14504         if(this.store.getCount() > 0){
14505             this.store.each(function(r){
14506                 if(r.data[prop] == value){
14507                     record = r;
14508                     return false;
14509                 }
14510                 return true;
14511             });
14512         }
14513         return record;
14514     },
14515     
14516     getName: function()
14517     {
14518         // returns hidden if it's set..
14519         if (!this.rendered) {return ''};
14520         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14521         
14522     },
14523     // private
14524     onViewMove : function(e, t){
14525         this.inKeyMode = false;
14526     },
14527
14528     // private
14529     onViewOver : function(e, t){
14530         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14531             return;
14532         }
14533         var item = this.view.findItemFromChild(t);
14534         
14535         if(item){
14536             var index = this.view.indexOf(item);
14537             this.select(index, false);
14538         }
14539     },
14540
14541     // private
14542     onViewClick : function(view, doFocus, el, e)
14543     {
14544         var index = this.view.getSelectedIndexes()[0];
14545         
14546         var r = this.store.getAt(index);
14547         
14548         if(this.tickable){
14549             
14550             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14551                 return;
14552             }
14553             
14554             var rm = false;
14555             var _this = this;
14556             
14557             Roo.each(this.tickItems, function(v,k){
14558                 
14559                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14560                     Roo.log(v);
14561                     _this.tickItems.splice(k, 1);
14562                     
14563                     if(typeof(e) == 'undefined' && view == false){
14564                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14565                     }
14566                     
14567                     rm = true;
14568                     return;
14569                 }
14570             });
14571             
14572             if(rm){
14573                 return;
14574             }
14575             
14576             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14577                 this.tickItems.push(r.data);
14578             }
14579             
14580             if(typeof(e) == 'undefined' && view == false){
14581                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14582             }
14583                     
14584             return;
14585         }
14586         
14587         if(r){
14588             this.onSelect(r, index);
14589         }
14590         if(doFocus !== false && !this.blockFocus){
14591             this.inputEl().focus();
14592         }
14593     },
14594
14595     // private
14596     restrictHeight : function(){
14597         //this.innerList.dom.style.height = '';
14598         //var inner = this.innerList.dom;
14599         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14600         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14601         //this.list.beginUpdate();
14602         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14603         this.list.alignTo(this.inputEl(), this.listAlign);
14604         this.list.alignTo(this.inputEl(), this.listAlign);
14605         //this.list.endUpdate();
14606     },
14607
14608     // private
14609     onEmptyResults : function(){
14610         
14611         if(this.tickable && this.editable){
14612             this.hasFocus = false;
14613             this.restrictHeight();
14614             return;
14615         }
14616         
14617         this.collapse();
14618     },
14619
14620     /**
14621      * Returns true if the dropdown list is expanded, else false.
14622      */
14623     isExpanded : function(){
14624         return this.list.isVisible();
14625     },
14626
14627     /**
14628      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14629      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14630      * @param {String} value The data value of the item to select
14631      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14632      * selected item if it is not currently in view (defaults to true)
14633      * @return {Boolean} True if the value matched an item in the list, else false
14634      */
14635     selectByValue : function(v, scrollIntoView){
14636         if(v !== undefined && v !== null){
14637             var r = this.findRecord(this.valueField || this.displayField, v);
14638             if(r){
14639                 this.select(this.store.indexOf(r), scrollIntoView);
14640                 return true;
14641             }
14642         }
14643         return false;
14644     },
14645
14646     /**
14647      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14648      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14649      * @param {Number} index The zero-based index of the list item to select
14650      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14651      * selected item if it is not currently in view (defaults to true)
14652      */
14653     select : function(index, scrollIntoView){
14654         this.selectedIndex = index;
14655         this.view.select(index);
14656         if(scrollIntoView !== false){
14657             var el = this.view.getNode(index);
14658             /*
14659              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14660              */
14661             if(el){
14662                 this.list.scrollChildIntoView(el, false);
14663             }
14664         }
14665     },
14666
14667     // private
14668     selectNext : function(){
14669         var ct = this.store.getCount();
14670         if(ct > 0){
14671             if(this.selectedIndex == -1){
14672                 this.select(0);
14673             }else if(this.selectedIndex < ct-1){
14674                 this.select(this.selectedIndex+1);
14675             }
14676         }
14677     },
14678
14679     // private
14680     selectPrev : function(){
14681         var ct = this.store.getCount();
14682         if(ct > 0){
14683             if(this.selectedIndex == -1){
14684                 this.select(0);
14685             }else if(this.selectedIndex != 0){
14686                 this.select(this.selectedIndex-1);
14687             }
14688         }
14689     },
14690
14691     // private
14692     onKeyUp : function(e){
14693         if(this.editable !== false && !e.isSpecialKey()){
14694             this.lastKey = e.getKey();
14695             this.dqTask.delay(this.queryDelay);
14696         }
14697     },
14698
14699     // private
14700     validateBlur : function(){
14701         return !this.list || !this.list.isVisible();   
14702     },
14703
14704     // private
14705     initQuery : function(){
14706         
14707         var v = this.getRawValue();
14708         
14709         if(this.tickable && this.editable){
14710             v = this.tickableInputEl().getValue();
14711         }
14712         
14713         this.doQuery(v);
14714     },
14715
14716     // private
14717     doForce : function(){
14718         if(this.inputEl().dom.value.length > 0){
14719             this.inputEl().dom.value =
14720                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14721              
14722         }
14723     },
14724
14725     /**
14726      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14727      * query allowing the query action to be canceled if needed.
14728      * @param {String} query The SQL query to execute
14729      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14730      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14731      * saved in the current store (defaults to false)
14732      */
14733     doQuery : function(q, forceAll){
14734         
14735         if(q === undefined || q === null){
14736             q = '';
14737         }
14738         var qe = {
14739             query: q,
14740             forceAll: forceAll,
14741             combo: this,
14742             cancel:false
14743         };
14744         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14745             return false;
14746         }
14747         q = qe.query;
14748         
14749         forceAll = qe.forceAll;
14750         if(forceAll === true || (q.length >= this.minChars)){
14751             
14752             this.hasQuery = true;
14753             
14754             if(this.lastQuery != q || this.alwaysQuery){
14755                 this.lastQuery = q;
14756                 if(this.mode == 'local'){
14757                     this.selectedIndex = -1;
14758                     if(forceAll){
14759                         this.store.clearFilter();
14760                     }else{
14761                         
14762                         if(this.specialFilter){
14763                             this.fireEvent('specialfilter', this);
14764                             this.onLoad();
14765                             return;
14766                         }
14767                         
14768                         this.store.filter(this.displayField, q);
14769                     }
14770                     
14771                     this.store.fireEvent("datachanged", this.store);
14772                     
14773                     this.onLoad();
14774                     
14775                     
14776                 }else{
14777                     
14778                     this.store.baseParams[this.queryParam] = q;
14779                     
14780                     var options = {params : this.getParams(q)};
14781                     
14782                     if(this.loadNext){
14783                         options.add = true;
14784                         options.params.start = this.page * this.pageSize;
14785                     }
14786                     
14787                     this.store.load(options);
14788                     
14789                     /*
14790                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14791                      *  we should expand the list on onLoad
14792                      *  so command out it
14793                      */
14794 //                    this.expand();
14795                 }
14796             }else{
14797                 this.selectedIndex = -1;
14798                 this.onLoad();   
14799             }
14800         }
14801         
14802         this.loadNext = false;
14803     },
14804     
14805     // private
14806     getParams : function(q){
14807         var p = {};
14808         //p[this.queryParam] = q;
14809         
14810         if(this.pageSize){
14811             p.start = 0;
14812             p.limit = this.pageSize;
14813         }
14814         return p;
14815     },
14816
14817     /**
14818      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14819      */
14820     collapse : function(){
14821         if(!this.isExpanded()){
14822             return;
14823         }
14824         
14825         this.list.hide();
14826         
14827         this.hasFocus = false;
14828         
14829         if(this.tickable){
14830             this.okBtn.hide();
14831             this.cancelBtn.hide();
14832             this.trigger.show();
14833             
14834             if(this.editable){
14835                 this.tickableInputEl().dom.value = '';
14836                 this.tickableInputEl().blur();
14837             }
14838             
14839         }
14840         
14841         Roo.get(document).un('mousedown', this.collapseIf, this);
14842         Roo.get(document).un('mousewheel', this.collapseIf, this);
14843         if (!this.editable) {
14844             Roo.get(document).un('keydown', this.listKeyPress, this);
14845         }
14846         this.fireEvent('collapse', this);
14847         
14848         this.validate();
14849     },
14850
14851     // private
14852     collapseIf : function(e){
14853         var in_combo  = e.within(this.el);
14854         var in_list =  e.within(this.list);
14855         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14856         
14857         if (in_combo || in_list || is_list) {
14858             //e.stopPropagation();
14859             return;
14860         }
14861         
14862         if(this.tickable){
14863             this.onTickableFooterButtonClick(e, false, false);
14864         }
14865
14866         this.collapse();
14867         
14868     },
14869
14870     /**
14871      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14872      */
14873     expand : function(){
14874        
14875         if(this.isExpanded() || !this.hasFocus){
14876             return;
14877         }
14878         
14879         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14880         this.list.setWidth(lw);
14881         
14882         Roo.log('expand');
14883         
14884         this.list.show();
14885         
14886         this.restrictHeight();
14887         
14888         if(this.tickable){
14889             
14890             this.tickItems = Roo.apply([], this.item);
14891             
14892             this.okBtn.show();
14893             this.cancelBtn.show();
14894             this.trigger.hide();
14895             
14896             if(this.editable){
14897                 this.tickableInputEl().focus();
14898             }
14899             
14900         }
14901         
14902         Roo.get(document).on('mousedown', this.collapseIf, this);
14903         Roo.get(document).on('mousewheel', this.collapseIf, this);
14904         if (!this.editable) {
14905             Roo.get(document).on('keydown', this.listKeyPress, this);
14906         }
14907         
14908         this.fireEvent('expand', this);
14909     },
14910
14911     // private
14912     // Implements the default empty TriggerField.onTriggerClick function
14913     onTriggerClick : function(e)
14914     {
14915         Roo.log('trigger click');
14916         
14917         if(this.disabled || !this.triggerList){
14918             return;
14919         }
14920         
14921         this.page = 0;
14922         this.loadNext = false;
14923         
14924         if(this.isExpanded()){
14925             this.collapse();
14926             if (!this.blockFocus) {
14927                 this.inputEl().focus();
14928             }
14929             
14930         }else {
14931             this.hasFocus = true;
14932             if(this.triggerAction == 'all') {
14933                 this.doQuery(this.allQuery, true);
14934             } else {
14935                 this.doQuery(this.getRawValue());
14936             }
14937             if (!this.blockFocus) {
14938                 this.inputEl().focus();
14939             }
14940         }
14941     },
14942     
14943     onTickableTriggerClick : function(e)
14944     {
14945         if(this.disabled){
14946             return;
14947         }
14948         
14949         this.page = 0;
14950         this.loadNext = false;
14951         this.hasFocus = true;
14952         
14953         if(this.triggerAction == 'all') {
14954             this.doQuery(this.allQuery, true);
14955         } else {
14956             this.doQuery(this.getRawValue());
14957         }
14958     },
14959     
14960     onSearchFieldClick : function(e)
14961     {
14962         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14963             this.onTickableFooterButtonClick(e, false, false);
14964             return;
14965         }
14966         
14967         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14968             return;
14969         }
14970         
14971         this.page = 0;
14972         this.loadNext = false;
14973         this.hasFocus = true;
14974         
14975         if(this.triggerAction == 'all') {
14976             this.doQuery(this.allQuery, true);
14977         } else {
14978             this.doQuery(this.getRawValue());
14979         }
14980     },
14981     
14982     listKeyPress : function(e)
14983     {
14984         //Roo.log('listkeypress');
14985         // scroll to first matching element based on key pres..
14986         if (e.isSpecialKey()) {
14987             return false;
14988         }
14989         var k = String.fromCharCode(e.getKey()).toUpperCase();
14990         //Roo.log(k);
14991         var match  = false;
14992         var csel = this.view.getSelectedNodes();
14993         var cselitem = false;
14994         if (csel.length) {
14995             var ix = this.view.indexOf(csel[0]);
14996             cselitem  = this.store.getAt(ix);
14997             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14998                 cselitem = false;
14999             }
15000             
15001         }
15002         
15003         this.store.each(function(v) { 
15004             if (cselitem) {
15005                 // start at existing selection.
15006                 if (cselitem.id == v.id) {
15007                     cselitem = false;
15008                 }
15009                 return true;
15010             }
15011                 
15012             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
15013                 match = this.store.indexOf(v);
15014                 return false;
15015             }
15016             return true;
15017         }, this);
15018         
15019         if (match === false) {
15020             return true; // no more action?
15021         }
15022         // scroll to?
15023         this.view.select(match);
15024         var sn = Roo.get(this.view.getSelectedNodes()[0]);
15025         sn.scrollIntoView(sn.dom.parentNode, false);
15026     },
15027     
15028     onViewScroll : function(e, t){
15029         
15030         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){
15031             return;
15032         }
15033         
15034         this.hasQuery = true;
15035         
15036         this.loading = this.list.select('.loading', true).first();
15037         
15038         if(this.loading === null){
15039             this.list.createChild({
15040                 tag: 'div',
15041                 cls: 'loading roo-select2-more-results roo-select2-active',
15042                 html: 'Loading more results...'
15043             });
15044             
15045             this.loading = this.list.select('.loading', true).first();
15046             
15047             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
15048             
15049             this.loading.hide();
15050         }
15051         
15052         this.loading.show();
15053         
15054         var _combo = this;
15055         
15056         this.page++;
15057         this.loadNext = true;
15058         
15059         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
15060         
15061         return;
15062     },
15063     
15064     addItem : function(o)
15065     {   
15066         var dv = ''; // display value
15067         
15068         if (this.displayField) {
15069             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
15070         } else {
15071             // this is an error condition!!!
15072             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
15073         }
15074         
15075         if(!dv.length){
15076             return;
15077         }
15078         
15079         var choice = this.choices.createChild({
15080             tag: 'li',
15081             cls: 'roo-select2-search-choice',
15082             cn: [
15083                 {
15084                     tag: 'div',
15085                     html: dv
15086                 },
15087                 {
15088                     tag: 'a',
15089                     href: '#',
15090                     cls: 'roo-select2-search-choice-close fa fa-times',
15091                     tabindex: '-1'
15092                 }
15093             ]
15094             
15095         }, this.searchField);
15096         
15097         var close = choice.select('a.roo-select2-search-choice-close', true).first();
15098         
15099         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
15100         
15101         this.item.push(o);
15102         
15103         this.lastData = o;
15104         
15105         this.syncValue();
15106         
15107         this.inputEl().dom.value = '';
15108         
15109         this.validate();
15110     },
15111     
15112     onRemoveItem : function(e, _self, o)
15113     {
15114         e.preventDefault();
15115         
15116         this.lastItem = Roo.apply([], this.item);
15117         
15118         var index = this.item.indexOf(o.data) * 1;
15119         
15120         if( index < 0){
15121             Roo.log('not this item?!');
15122             return;
15123         }
15124         
15125         this.item.splice(index, 1);
15126         o.item.remove();
15127         
15128         this.syncValue();
15129         
15130         this.fireEvent('remove', this, e);
15131         
15132         this.validate();
15133         
15134     },
15135     
15136     syncValue : function()
15137     {
15138         if(!this.item.length){
15139             this.clearValue();
15140             return;
15141         }
15142             
15143         var value = [];
15144         var _this = this;
15145         Roo.each(this.item, function(i){
15146             if(_this.valueField){
15147                 value.push(i[_this.valueField]);
15148                 return;
15149             }
15150
15151             value.push(i);
15152         });
15153
15154         this.value = value.join(',');
15155
15156         if(this.hiddenField){
15157             this.hiddenField.dom.value = this.value;
15158         }
15159         
15160         this.store.fireEvent("datachanged", this.store);
15161         
15162         this.validate();
15163     },
15164     
15165     clearItem : function()
15166     {
15167         if(!this.multiple){
15168             return;
15169         }
15170         
15171         this.item = [];
15172         
15173         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15174            c.remove();
15175         });
15176         
15177         this.syncValue();
15178         
15179         this.validate();
15180         
15181         if(this.tickable && !Roo.isTouch){
15182             this.view.refresh();
15183         }
15184     },
15185     
15186     inputEl: function ()
15187     {
15188         if(Roo.isIOS && this.useNativeIOS){
15189             return this.el.select('select.roo-ios-select', true).first();
15190         }
15191         
15192         if(Roo.isTouch && this.mobileTouchView){
15193             return this.el.select('input.form-control',true).first();
15194         }
15195         
15196         if(this.tickable){
15197             return this.searchField;
15198         }
15199         
15200         return this.el.select('input.form-control',true).first();
15201     },
15202     
15203     onTickableFooterButtonClick : function(e, btn, el)
15204     {
15205         e.preventDefault();
15206         
15207         this.lastItem = Roo.apply([], this.item);
15208         
15209         if(btn && btn.name == 'cancel'){
15210             this.tickItems = Roo.apply([], this.item);
15211             this.collapse();
15212             return;
15213         }
15214         
15215         this.clearItem();
15216         
15217         var _this = this;
15218         
15219         Roo.each(this.tickItems, function(o){
15220             _this.addItem(o);
15221         });
15222         
15223         this.collapse();
15224         
15225     },
15226     
15227     validate : function()
15228     {
15229         if(this.getVisibilityEl().hasClass('hidden')){
15230             return true;
15231         }
15232         
15233         var v = this.getRawValue();
15234         
15235         if(this.multiple){
15236             v = this.getValue();
15237         }
15238         
15239         if(this.disabled || this.allowBlank || v.length){
15240             this.markValid();
15241             return true;
15242         }
15243         
15244         this.markInvalid();
15245         return false;
15246     },
15247     
15248     tickableInputEl : function()
15249     {
15250         if(!this.tickable || !this.editable){
15251             return this.inputEl();
15252         }
15253         
15254         return this.inputEl().select('.roo-select2-search-field-input', true).first();
15255     },
15256     
15257     
15258     getAutoCreateTouchView : function()
15259     {
15260         var id = Roo.id();
15261         
15262         var cfg = {
15263             cls: 'form-group' //input-group
15264         };
15265         
15266         var input =  {
15267             tag: 'input',
15268             id : id,
15269             type : this.inputType,
15270             cls : 'form-control x-combo-noedit',
15271             autocomplete: 'new-password',
15272             placeholder : this.placeholder || '',
15273             readonly : true
15274         };
15275         
15276         if (this.name) {
15277             input.name = this.name;
15278         }
15279         
15280         if (this.size) {
15281             input.cls += ' input-' + this.size;
15282         }
15283         
15284         if (this.disabled) {
15285             input.disabled = true;
15286         }
15287         
15288         var inputblock = {
15289             cls : '',
15290             cn : [
15291                 input
15292             ]
15293         };
15294         
15295         if(this.before){
15296             inputblock.cls += ' input-group';
15297             
15298             inputblock.cn.unshift({
15299                 tag :'span',
15300                 cls : 'input-group-addon input-group-prepend input-group-text',
15301                 html : this.before
15302             });
15303         }
15304         
15305         if(this.removable && !this.multiple){
15306             inputblock.cls += ' roo-removable';
15307             
15308             inputblock.cn.push({
15309                 tag: 'button',
15310                 html : 'x',
15311                 cls : 'roo-combo-removable-btn close'
15312             });
15313         }
15314
15315         if(this.hasFeedback && !this.allowBlank){
15316             
15317             inputblock.cls += ' has-feedback';
15318             
15319             inputblock.cn.push({
15320                 tag: 'span',
15321                 cls: 'glyphicon form-control-feedback'
15322             });
15323             
15324         }
15325         
15326         if (this.after) {
15327             
15328             inputblock.cls += (this.before) ? '' : ' input-group';
15329             
15330             inputblock.cn.push({
15331                 tag :'span',
15332                 cls : 'input-group-addon input-group-append input-group-text',
15333                 html : this.after
15334             });
15335         }
15336
15337         
15338         var ibwrap = inputblock;
15339         
15340         if(this.multiple){
15341             ibwrap = {
15342                 tag: 'ul',
15343                 cls: 'roo-select2-choices',
15344                 cn:[
15345                     {
15346                         tag: 'li',
15347                         cls: 'roo-select2-search-field',
15348                         cn: [
15349
15350                             inputblock
15351                         ]
15352                     }
15353                 ]
15354             };
15355         
15356             
15357         }
15358         
15359         var combobox = {
15360             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15361             cn: [
15362                 {
15363                     tag: 'input',
15364                     type : 'hidden',
15365                     cls: 'form-hidden-field'
15366                 },
15367                 ibwrap
15368             ]
15369         };
15370         
15371         if(!this.multiple && this.showToggleBtn){
15372             
15373             var caret = {
15374                         tag: 'span',
15375                         cls: 'caret'
15376             };
15377             
15378             if (this.caret != false) {
15379                 caret = {
15380                      tag: 'i',
15381                      cls: 'fa fa-' + this.caret
15382                 };
15383                 
15384             }
15385             
15386             combobox.cn.push({
15387                 tag :'span',
15388                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15389                 cn : [
15390                     caret,
15391                     {
15392                         tag: 'span',
15393                         cls: 'combobox-clear',
15394                         cn  : [
15395                             {
15396                                 tag : 'i',
15397                                 cls: 'icon-remove'
15398                             }
15399                         ]
15400                     }
15401                 ]
15402
15403             })
15404         }
15405         
15406         if(this.multiple){
15407             combobox.cls += ' roo-select2-container-multi';
15408         }
15409         
15410         var align = this.labelAlign || this.parentLabelAlign();
15411         
15412         if (align ==='left' && this.fieldLabel.length) {
15413
15414             cfg.cn = [
15415                 {
15416                    tag : 'i',
15417                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15418                    tooltip : 'This field is required'
15419                 },
15420                 {
15421                     tag: 'label',
15422                     cls : 'control-label col-form-label',
15423                     html : this.fieldLabel
15424
15425                 },
15426                 {
15427                     cls : '', 
15428                     cn: [
15429                         combobox
15430                     ]
15431                 }
15432             ];
15433             
15434             var labelCfg = cfg.cn[1];
15435             var contentCfg = cfg.cn[2];
15436             
15437
15438             if(this.indicatorpos == 'right'){
15439                 cfg.cn = [
15440                     {
15441                         tag: 'label',
15442                         'for' :  id,
15443                         cls : 'control-label col-form-label',
15444                         cn : [
15445                             {
15446                                 tag : 'span',
15447                                 html : this.fieldLabel
15448                             },
15449                             {
15450                                 tag : 'i',
15451                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15452                                 tooltip : 'This field is required'
15453                             }
15454                         ]
15455                     },
15456                     {
15457                         cls : "",
15458                         cn: [
15459                             combobox
15460                         ]
15461                     }
15462
15463                 ];
15464                 
15465                 labelCfg = cfg.cn[0];
15466                 contentCfg = cfg.cn[1];
15467             }
15468             
15469            
15470             
15471             if(this.labelWidth > 12){
15472                 labelCfg.style = "width: " + this.labelWidth + 'px';
15473             }
15474             
15475             if(this.labelWidth < 13 && this.labelmd == 0){
15476                 this.labelmd = this.labelWidth;
15477             }
15478             
15479             if(this.labellg > 0){
15480                 labelCfg.cls += ' col-lg-' + this.labellg;
15481                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15482             }
15483             
15484             if(this.labelmd > 0){
15485                 labelCfg.cls += ' col-md-' + this.labelmd;
15486                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15487             }
15488             
15489             if(this.labelsm > 0){
15490                 labelCfg.cls += ' col-sm-' + this.labelsm;
15491                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15492             }
15493             
15494             if(this.labelxs > 0){
15495                 labelCfg.cls += ' col-xs-' + this.labelxs;
15496                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15497             }
15498                 
15499                 
15500         } else if ( this.fieldLabel.length) {
15501             cfg.cn = [
15502                 {
15503                    tag : 'i',
15504                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15505                    tooltip : 'This field is required'
15506                 },
15507                 {
15508                     tag: 'label',
15509                     cls : 'control-label',
15510                     html : this.fieldLabel
15511
15512                 },
15513                 {
15514                     cls : '', 
15515                     cn: [
15516                         combobox
15517                     ]
15518                 }
15519             ];
15520             
15521             if(this.indicatorpos == 'right'){
15522                 cfg.cn = [
15523                     {
15524                         tag: 'label',
15525                         cls : 'control-label',
15526                         html : this.fieldLabel,
15527                         cn : [
15528                             {
15529                                tag : 'i',
15530                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15531                                tooltip : 'This field is required'
15532                             }
15533                         ]
15534                     },
15535                     {
15536                         cls : '', 
15537                         cn: [
15538                             combobox
15539                         ]
15540                     }
15541                 ];
15542             }
15543         } else {
15544             cfg.cn = combobox;    
15545         }
15546         
15547         
15548         var settings = this;
15549         
15550         ['xs','sm','md','lg'].map(function(size){
15551             if (settings[size]) {
15552                 cfg.cls += ' col-' + size + '-' + settings[size];
15553             }
15554         });
15555         
15556         return cfg;
15557     },
15558     
15559     initTouchView : function()
15560     {
15561         this.renderTouchView();
15562         
15563         this.touchViewEl.on('scroll', function(){
15564             this.el.dom.scrollTop = 0;
15565         }, this);
15566         
15567         this.originalValue = this.getValue();
15568         
15569         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15570         
15571         this.inputEl().on("click", this.showTouchView, this);
15572         if (this.triggerEl) {
15573             this.triggerEl.on("click", this.showTouchView, this);
15574         }
15575         
15576         
15577         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15578         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15579         
15580         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15581         
15582         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15583         this.store.on('load', this.onTouchViewLoad, this);
15584         this.store.on('loadexception', this.onTouchViewLoadException, this);
15585         
15586         if(this.hiddenName){
15587             
15588             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15589             
15590             this.hiddenField.dom.value =
15591                 this.hiddenValue !== undefined ? this.hiddenValue :
15592                 this.value !== undefined ? this.value : '';
15593         
15594             this.el.dom.removeAttribute('name');
15595             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15596         }
15597         
15598         if(this.multiple){
15599             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15600             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15601         }
15602         
15603         if(this.removable && !this.multiple){
15604             var close = this.closeTriggerEl();
15605             if(close){
15606                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15607                 close.on('click', this.removeBtnClick, this, close);
15608             }
15609         }
15610         /*
15611          * fix the bug in Safari iOS8
15612          */
15613         this.inputEl().on("focus", function(e){
15614             document.activeElement.blur();
15615         }, this);
15616         
15617         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15618         
15619         return;
15620         
15621         
15622     },
15623     
15624     renderTouchView : function()
15625     {
15626         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15627         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15628         
15629         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15630         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15631         
15632         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15633         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15634         this.touchViewBodyEl.setStyle('overflow', 'auto');
15635         
15636         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15637         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15638         
15639         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15640         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15641         
15642     },
15643     
15644     showTouchView : function()
15645     {
15646         if(this.disabled){
15647             return;
15648         }
15649         
15650         this.touchViewHeaderEl.hide();
15651
15652         if(this.modalTitle.length){
15653             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15654             this.touchViewHeaderEl.show();
15655         }
15656
15657         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15658         this.touchViewEl.show();
15659
15660         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15661         
15662         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15663         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15664
15665         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15666
15667         if(this.modalTitle.length){
15668             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15669         }
15670         
15671         this.touchViewBodyEl.setHeight(bodyHeight);
15672
15673         if(this.animate){
15674             var _this = this;
15675             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15676         }else{
15677             this.touchViewEl.addClass('in');
15678         }
15679         
15680         if(this._touchViewMask){
15681             Roo.get(document.body).addClass("x-body-masked");
15682             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15683             this._touchViewMask.setStyle('z-index', 10000);
15684             this._touchViewMask.addClass('show');
15685         }
15686         
15687         this.doTouchViewQuery();
15688         
15689     },
15690     
15691     hideTouchView : function()
15692     {
15693         this.touchViewEl.removeClass('in');
15694
15695         if(this.animate){
15696             var _this = this;
15697             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15698         }else{
15699             this.touchViewEl.setStyle('display', 'none');
15700         }
15701         
15702         if(this._touchViewMask){
15703             this._touchViewMask.removeClass('show');
15704             Roo.get(document.body).removeClass("x-body-masked");
15705         }
15706     },
15707     
15708     setTouchViewValue : function()
15709     {
15710         if(this.multiple){
15711             this.clearItem();
15712         
15713             var _this = this;
15714
15715             Roo.each(this.tickItems, function(o){
15716                 this.addItem(o);
15717             }, this);
15718         }
15719         
15720         this.hideTouchView();
15721     },
15722     
15723     doTouchViewQuery : function()
15724     {
15725         var qe = {
15726             query: '',
15727             forceAll: true,
15728             combo: this,
15729             cancel:false
15730         };
15731         
15732         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15733             return false;
15734         }
15735         
15736         if(!this.alwaysQuery || this.mode == 'local'){
15737             this.onTouchViewLoad();
15738             return;
15739         }
15740         
15741         this.store.load();
15742     },
15743     
15744     onTouchViewBeforeLoad : function(combo,opts)
15745     {
15746         return;
15747     },
15748
15749     // private
15750     onTouchViewLoad : function()
15751     {
15752         if(this.store.getCount() < 1){
15753             this.onTouchViewEmptyResults();
15754             return;
15755         }
15756         
15757         this.clearTouchView();
15758         
15759         var rawValue = this.getRawValue();
15760         
15761         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15762         
15763         this.tickItems = [];
15764         
15765         this.store.data.each(function(d, rowIndex){
15766             var row = this.touchViewListGroup.createChild(template);
15767             
15768             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15769                 row.addClass(d.data.cls);
15770             }
15771             
15772             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15773                 var cfg = {
15774                     data : d.data,
15775                     html : d.data[this.displayField]
15776                 };
15777                 
15778                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15779                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15780                 }
15781             }
15782             row.removeClass('selected');
15783             if(!this.multiple && this.valueField &&
15784                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15785             {
15786                 // radio buttons..
15787                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15788                 row.addClass('selected');
15789             }
15790             
15791             if(this.multiple && this.valueField &&
15792                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15793             {
15794                 
15795                 // checkboxes...
15796                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15797                 this.tickItems.push(d.data);
15798             }
15799             
15800             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15801             
15802         }, this);
15803         
15804         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15805         
15806         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15807
15808         if(this.modalTitle.length){
15809             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15810         }
15811
15812         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15813         
15814         if(this.mobile_restrict_height && listHeight < bodyHeight){
15815             this.touchViewBodyEl.setHeight(listHeight);
15816         }
15817         
15818         var _this = this;
15819         
15820         if(firstChecked && listHeight > bodyHeight){
15821             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15822         }
15823         
15824     },
15825     
15826     onTouchViewLoadException : function()
15827     {
15828         this.hideTouchView();
15829     },
15830     
15831     onTouchViewEmptyResults : function()
15832     {
15833         this.clearTouchView();
15834         
15835         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15836         
15837         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15838         
15839     },
15840     
15841     clearTouchView : function()
15842     {
15843         this.touchViewListGroup.dom.innerHTML = '';
15844     },
15845     
15846     onTouchViewClick : function(e, el, o)
15847     {
15848         e.preventDefault();
15849         
15850         var row = o.row;
15851         var rowIndex = o.rowIndex;
15852         
15853         var r = this.store.getAt(rowIndex);
15854         
15855         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15856             
15857             if(!this.multiple){
15858                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15859                     c.dom.removeAttribute('checked');
15860                 }, this);
15861
15862                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15863
15864                 this.setFromData(r.data);
15865
15866                 var close = this.closeTriggerEl();
15867
15868                 if(close){
15869                     close.show();
15870                 }
15871
15872                 this.hideTouchView();
15873
15874                 this.fireEvent('select', this, r, rowIndex);
15875
15876                 return;
15877             }
15878
15879             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15880                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15881                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15882                 return;
15883             }
15884
15885             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15886             this.addItem(r.data);
15887             this.tickItems.push(r.data);
15888         }
15889     },
15890     
15891     getAutoCreateNativeIOS : function()
15892     {
15893         var cfg = {
15894             cls: 'form-group' //input-group,
15895         };
15896         
15897         var combobox =  {
15898             tag: 'select',
15899             cls : 'roo-ios-select'
15900         };
15901         
15902         if (this.name) {
15903             combobox.name = this.name;
15904         }
15905         
15906         if (this.disabled) {
15907             combobox.disabled = true;
15908         }
15909         
15910         var settings = this;
15911         
15912         ['xs','sm','md','lg'].map(function(size){
15913             if (settings[size]) {
15914                 cfg.cls += ' col-' + size + '-' + settings[size];
15915             }
15916         });
15917         
15918         cfg.cn = combobox;
15919         
15920         return cfg;
15921         
15922     },
15923     
15924     initIOSView : function()
15925     {
15926         this.store.on('load', this.onIOSViewLoad, this);
15927         
15928         return;
15929     },
15930     
15931     onIOSViewLoad : function()
15932     {
15933         if(this.store.getCount() < 1){
15934             return;
15935         }
15936         
15937         this.clearIOSView();
15938         
15939         if(this.allowBlank) {
15940             
15941             var default_text = '-- SELECT --';
15942             
15943             if(this.placeholder.length){
15944                 default_text = this.placeholder;
15945             }
15946             
15947             if(this.emptyTitle.length){
15948                 default_text += ' - ' + this.emptyTitle + ' -';
15949             }
15950             
15951             var opt = this.inputEl().createChild({
15952                 tag: 'option',
15953                 value : 0,
15954                 html : default_text
15955             });
15956             
15957             var o = {};
15958             o[this.valueField] = 0;
15959             o[this.displayField] = default_text;
15960             
15961             this.ios_options.push({
15962                 data : o,
15963                 el : opt
15964             });
15965             
15966         }
15967         
15968         this.store.data.each(function(d, rowIndex){
15969             
15970             var html = '';
15971             
15972             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15973                 html = d.data[this.displayField];
15974             }
15975             
15976             var value = '';
15977             
15978             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15979                 value = d.data[this.valueField];
15980             }
15981             
15982             var option = {
15983                 tag: 'option',
15984                 value : value,
15985                 html : html
15986             };
15987             
15988             if(this.value == d.data[this.valueField]){
15989                 option['selected'] = true;
15990             }
15991             
15992             var opt = this.inputEl().createChild(option);
15993             
15994             this.ios_options.push({
15995                 data : d.data,
15996                 el : opt
15997             });
15998             
15999         }, this);
16000         
16001         this.inputEl().on('change', function(){
16002            this.fireEvent('select', this);
16003         }, this);
16004         
16005     },
16006     
16007     clearIOSView: function()
16008     {
16009         this.inputEl().dom.innerHTML = '';
16010         
16011         this.ios_options = [];
16012     },
16013     
16014     setIOSValue: function(v)
16015     {
16016         this.value = v;
16017         
16018         if(!this.ios_options){
16019             return;
16020         }
16021         
16022         Roo.each(this.ios_options, function(opts){
16023            
16024            opts.el.dom.removeAttribute('selected');
16025            
16026            if(opts.data[this.valueField] != v){
16027                return;
16028            }
16029            
16030            opts.el.dom.setAttribute('selected', true);
16031            
16032         }, this);
16033     }
16034
16035     /** 
16036     * @cfg {Boolean} grow 
16037     * @hide 
16038     */
16039     /** 
16040     * @cfg {Number} growMin 
16041     * @hide 
16042     */
16043     /** 
16044     * @cfg {Number} growMax 
16045     * @hide 
16046     */
16047     /**
16048      * @hide
16049      * @method autoSize
16050      */
16051 });
16052
16053 Roo.apply(Roo.bootstrap.ComboBox,  {
16054     
16055     header : {
16056         tag: 'div',
16057         cls: 'modal-header',
16058         cn: [
16059             {
16060                 tag: 'h4',
16061                 cls: 'modal-title'
16062             }
16063         ]
16064     },
16065     
16066     body : {
16067         tag: 'div',
16068         cls: 'modal-body',
16069         cn: [
16070             {
16071                 tag: 'ul',
16072                 cls: 'list-group'
16073             }
16074         ]
16075     },
16076     
16077     listItemRadio : {
16078         tag: 'li',
16079         cls: 'list-group-item',
16080         cn: [
16081             {
16082                 tag: 'span',
16083                 cls: 'roo-combobox-list-group-item-value'
16084             },
16085             {
16086                 tag: 'div',
16087                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
16088                 cn: [
16089                     {
16090                         tag: 'input',
16091                         type: 'radio'
16092                     },
16093                     {
16094                         tag: 'label'
16095                     }
16096                 ]
16097             }
16098         ]
16099     },
16100     
16101     listItemCheckbox : {
16102         tag: 'li',
16103         cls: 'list-group-item',
16104         cn: [
16105             {
16106                 tag: 'span',
16107                 cls: 'roo-combobox-list-group-item-value'
16108             },
16109             {
16110                 tag: 'div',
16111                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
16112                 cn: [
16113                     {
16114                         tag: 'input',
16115                         type: 'checkbox'
16116                     },
16117                     {
16118                         tag: 'label'
16119                     }
16120                 ]
16121             }
16122         ]
16123     },
16124     
16125     emptyResult : {
16126         tag: 'div',
16127         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
16128     },
16129     
16130     footer : {
16131         tag: 'div',
16132         cls: 'modal-footer',
16133         cn: [
16134             {
16135                 tag: 'div',
16136                 cls: 'row',
16137                 cn: [
16138                     {
16139                         tag: 'div',
16140                         cls: 'col-xs-6 text-left',
16141                         cn: {
16142                             tag: 'button',
16143                             cls: 'btn btn-danger roo-touch-view-cancel',
16144                             html: 'Cancel'
16145                         }
16146                     },
16147                     {
16148                         tag: 'div',
16149                         cls: 'col-xs-6 text-right',
16150                         cn: {
16151                             tag: 'button',
16152                             cls: 'btn btn-success roo-touch-view-ok',
16153                             html: 'OK'
16154                         }
16155                     }
16156                 ]
16157             }
16158         ]
16159         
16160     }
16161 });
16162
16163 Roo.apply(Roo.bootstrap.ComboBox,  {
16164     
16165     touchViewTemplate : {
16166         tag: 'div',
16167         cls: 'modal fade roo-combobox-touch-view',
16168         cn: [
16169             {
16170                 tag: 'div',
16171                 cls: 'modal-dialog',
16172                 style : 'position:fixed', // we have to fix position....
16173                 cn: [
16174                     {
16175                         tag: 'div',
16176                         cls: 'modal-content',
16177                         cn: [
16178                             Roo.bootstrap.ComboBox.header,
16179                             Roo.bootstrap.ComboBox.body,
16180                             Roo.bootstrap.ComboBox.footer
16181                         ]
16182                     }
16183                 ]
16184             }
16185         ]
16186     }
16187 });/*
16188  * Based on:
16189  * Ext JS Library 1.1.1
16190  * Copyright(c) 2006-2007, Ext JS, LLC.
16191  *
16192  * Originally Released Under LGPL - original licence link has changed is not relivant.
16193  *
16194  * Fork - LGPL
16195  * <script type="text/javascript">
16196  */
16197
16198 /**
16199  * @class Roo.View
16200  * @extends Roo.util.Observable
16201  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
16202  * This class also supports single and multi selection modes. <br>
16203  * Create a data model bound view:
16204  <pre><code>
16205  var store = new Roo.data.Store(...);
16206
16207  var view = new Roo.View({
16208     el : "my-element",
16209     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
16210  
16211     singleSelect: true,
16212     selectedClass: "ydataview-selected",
16213     store: store
16214  });
16215
16216  // listen for node click?
16217  view.on("click", function(vw, index, node, e){
16218  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16219  });
16220
16221  // load XML data
16222  dataModel.load("foobar.xml");
16223  </code></pre>
16224  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16225  * <br><br>
16226  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16227  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16228  * 
16229  * Note: old style constructor is still suported (container, template, config)
16230  * 
16231  * @constructor
16232  * Create a new View
16233  * @param {Object} config The config object
16234  * 
16235  */
16236 Roo.View = function(config, depreciated_tpl, depreciated_config){
16237     
16238     this.parent = false;
16239     
16240     if (typeof(depreciated_tpl) == 'undefined') {
16241         // new way.. - universal constructor.
16242         Roo.apply(this, config);
16243         this.el  = Roo.get(this.el);
16244     } else {
16245         // old format..
16246         this.el  = Roo.get(config);
16247         this.tpl = depreciated_tpl;
16248         Roo.apply(this, depreciated_config);
16249     }
16250     this.wrapEl  = this.el.wrap().wrap();
16251     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16252     
16253     
16254     if(typeof(this.tpl) == "string"){
16255         this.tpl = new Roo.Template(this.tpl);
16256     } else {
16257         // support xtype ctors..
16258         this.tpl = new Roo.factory(this.tpl, Roo);
16259     }
16260     
16261     
16262     this.tpl.compile();
16263     
16264     /** @private */
16265     this.addEvents({
16266         /**
16267          * @event beforeclick
16268          * Fires before a click is processed. Returns false to cancel the default action.
16269          * @param {Roo.View} this
16270          * @param {Number} index The index of the target node
16271          * @param {HTMLElement} node The target node
16272          * @param {Roo.EventObject} e The raw event object
16273          */
16274             "beforeclick" : true,
16275         /**
16276          * @event click
16277          * Fires when a template node is clicked.
16278          * @param {Roo.View} this
16279          * @param {Number} index The index of the target node
16280          * @param {HTMLElement} node The target node
16281          * @param {Roo.EventObject} e The raw event object
16282          */
16283             "click" : true,
16284         /**
16285          * @event dblclick
16286          * Fires when a template node is double clicked.
16287          * @param {Roo.View} this
16288          * @param {Number} index The index of the target node
16289          * @param {HTMLElement} node The target node
16290          * @param {Roo.EventObject} e The raw event object
16291          */
16292             "dblclick" : true,
16293         /**
16294          * @event contextmenu
16295          * Fires when a template node is right clicked.
16296          * @param {Roo.View} this
16297          * @param {Number} index The index of the target node
16298          * @param {HTMLElement} node The target node
16299          * @param {Roo.EventObject} e The raw event object
16300          */
16301             "contextmenu" : true,
16302         /**
16303          * @event selectionchange
16304          * Fires when the selected nodes change.
16305          * @param {Roo.View} this
16306          * @param {Array} selections Array of the selected nodes
16307          */
16308             "selectionchange" : true,
16309     
16310         /**
16311          * @event beforeselect
16312          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16313          * @param {Roo.View} this
16314          * @param {HTMLElement} node The node to be selected
16315          * @param {Array} selections Array of currently selected nodes
16316          */
16317             "beforeselect" : true,
16318         /**
16319          * @event preparedata
16320          * Fires on every row to render, to allow you to change the data.
16321          * @param {Roo.View} this
16322          * @param {Object} data to be rendered (change this)
16323          */
16324           "preparedata" : true
16325           
16326           
16327         });
16328
16329
16330
16331     this.el.on({
16332         "click": this.onClick,
16333         "dblclick": this.onDblClick,
16334         "contextmenu": this.onContextMenu,
16335         scope:this
16336     });
16337
16338     this.selections = [];
16339     this.nodes = [];
16340     this.cmp = new Roo.CompositeElementLite([]);
16341     if(this.store){
16342         this.store = Roo.factory(this.store, Roo.data);
16343         this.setStore(this.store, true);
16344     }
16345     
16346     if ( this.footer && this.footer.xtype) {
16347            
16348          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16349         
16350         this.footer.dataSource = this.store;
16351         this.footer.container = fctr;
16352         this.footer = Roo.factory(this.footer, Roo);
16353         fctr.insertFirst(this.el);
16354         
16355         // this is a bit insane - as the paging toolbar seems to detach the el..
16356 //        dom.parentNode.parentNode.parentNode
16357          // they get detached?
16358     }
16359     
16360     
16361     Roo.View.superclass.constructor.call(this);
16362     
16363     
16364 };
16365
16366 Roo.extend(Roo.View, Roo.util.Observable, {
16367     
16368      /**
16369      * @cfg {Roo.data.Store} store Data store to load data from.
16370      */
16371     store : false,
16372     
16373     /**
16374      * @cfg {String|Roo.Element} el The container element.
16375      */
16376     el : '',
16377     
16378     /**
16379      * @cfg {String|Roo.Template} tpl The template used by this View 
16380      */
16381     tpl : false,
16382     /**
16383      * @cfg {String} dataName the named area of the template to use as the data area
16384      *                          Works with domtemplates roo-name="name"
16385      */
16386     dataName: false,
16387     /**
16388      * @cfg {String} selectedClass The css class to add to selected nodes
16389      */
16390     selectedClass : "x-view-selected",
16391      /**
16392      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16393      */
16394     emptyText : "",
16395     
16396     /**
16397      * @cfg {String} text to display on mask (default Loading)
16398      */
16399     mask : false,
16400     /**
16401      * @cfg {Boolean} multiSelect Allow multiple selection
16402      */
16403     multiSelect : false,
16404     /**
16405      * @cfg {Boolean} singleSelect Allow single selection
16406      */
16407     singleSelect:  false,
16408     
16409     /**
16410      * @cfg {Boolean} toggleSelect - selecting 
16411      */
16412     toggleSelect : false,
16413     
16414     /**
16415      * @cfg {Boolean} tickable - selecting 
16416      */
16417     tickable : false,
16418     
16419     /**
16420      * Returns the element this view is bound to.
16421      * @return {Roo.Element}
16422      */
16423     getEl : function(){
16424         return this.wrapEl;
16425     },
16426     
16427     
16428
16429     /**
16430      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16431      */
16432     refresh : function(){
16433         //Roo.log('refresh');
16434         var t = this.tpl;
16435         
16436         // if we are using something like 'domtemplate', then
16437         // the what gets used is:
16438         // t.applySubtemplate(NAME, data, wrapping data..)
16439         // the outer template then get' applied with
16440         //     the store 'extra data'
16441         // and the body get's added to the
16442         //      roo-name="data" node?
16443         //      <span class='roo-tpl-{name}'></span> ?????
16444         
16445         
16446         
16447         this.clearSelections();
16448         this.el.update("");
16449         var html = [];
16450         var records = this.store.getRange();
16451         if(records.length < 1) {
16452             
16453             // is this valid??  = should it render a template??
16454             
16455             this.el.update(this.emptyText);
16456             return;
16457         }
16458         var el = this.el;
16459         if (this.dataName) {
16460             this.el.update(t.apply(this.store.meta)); //????
16461             el = this.el.child('.roo-tpl-' + this.dataName);
16462         }
16463         
16464         for(var i = 0, len = records.length; i < len; i++){
16465             var data = this.prepareData(records[i].data, i, records[i]);
16466             this.fireEvent("preparedata", this, data, i, records[i]);
16467             
16468             var d = Roo.apply({}, data);
16469             
16470             if(this.tickable){
16471                 Roo.apply(d, {'roo-id' : Roo.id()});
16472                 
16473                 var _this = this;
16474             
16475                 Roo.each(this.parent.item, function(item){
16476                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16477                         return;
16478                     }
16479                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16480                 });
16481             }
16482             
16483             html[html.length] = Roo.util.Format.trim(
16484                 this.dataName ?
16485                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16486                     t.apply(d)
16487             );
16488         }
16489         
16490         
16491         
16492         el.update(html.join(""));
16493         this.nodes = el.dom.childNodes;
16494         this.updateIndexes(0);
16495     },
16496     
16497
16498     /**
16499      * Function to override to reformat the data that is sent to
16500      * the template for each node.
16501      * DEPRICATED - use the preparedata event handler.
16502      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16503      * a JSON object for an UpdateManager bound view).
16504      */
16505     prepareData : function(data, index, record)
16506     {
16507         this.fireEvent("preparedata", this, data, index, record);
16508         return data;
16509     },
16510
16511     onUpdate : function(ds, record){
16512         // Roo.log('on update');   
16513         this.clearSelections();
16514         var index = this.store.indexOf(record);
16515         var n = this.nodes[index];
16516         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16517         n.parentNode.removeChild(n);
16518         this.updateIndexes(index, index);
16519     },
16520
16521     
16522     
16523 // --------- FIXME     
16524     onAdd : function(ds, records, index)
16525     {
16526         //Roo.log(['on Add', ds, records, index] );        
16527         this.clearSelections();
16528         if(this.nodes.length == 0){
16529             this.refresh();
16530             return;
16531         }
16532         var n = this.nodes[index];
16533         for(var i = 0, len = records.length; i < len; i++){
16534             var d = this.prepareData(records[i].data, i, records[i]);
16535             if(n){
16536                 this.tpl.insertBefore(n, d);
16537             }else{
16538                 
16539                 this.tpl.append(this.el, d);
16540             }
16541         }
16542         this.updateIndexes(index);
16543     },
16544
16545     onRemove : function(ds, record, index){
16546        // Roo.log('onRemove');
16547         this.clearSelections();
16548         var el = this.dataName  ?
16549             this.el.child('.roo-tpl-' + this.dataName) :
16550             this.el; 
16551         
16552         el.dom.removeChild(this.nodes[index]);
16553         this.updateIndexes(index);
16554     },
16555
16556     /**
16557      * Refresh an individual node.
16558      * @param {Number} index
16559      */
16560     refreshNode : function(index){
16561         this.onUpdate(this.store, this.store.getAt(index));
16562     },
16563
16564     updateIndexes : function(startIndex, endIndex){
16565         var ns = this.nodes;
16566         startIndex = startIndex || 0;
16567         endIndex = endIndex || ns.length - 1;
16568         for(var i = startIndex; i <= endIndex; i++){
16569             ns[i].nodeIndex = i;
16570         }
16571     },
16572
16573     /**
16574      * Changes the data store this view uses and refresh the view.
16575      * @param {Store} store
16576      */
16577     setStore : function(store, initial){
16578         if(!initial && this.store){
16579             this.store.un("datachanged", this.refresh);
16580             this.store.un("add", this.onAdd);
16581             this.store.un("remove", this.onRemove);
16582             this.store.un("update", this.onUpdate);
16583             this.store.un("clear", this.refresh);
16584             this.store.un("beforeload", this.onBeforeLoad);
16585             this.store.un("load", this.onLoad);
16586             this.store.un("loadexception", this.onLoad);
16587         }
16588         if(store){
16589           
16590             store.on("datachanged", this.refresh, this);
16591             store.on("add", this.onAdd, this);
16592             store.on("remove", this.onRemove, this);
16593             store.on("update", this.onUpdate, this);
16594             store.on("clear", this.refresh, this);
16595             store.on("beforeload", this.onBeforeLoad, this);
16596             store.on("load", this.onLoad, this);
16597             store.on("loadexception", this.onLoad, this);
16598         }
16599         
16600         if(store){
16601             this.refresh();
16602         }
16603     },
16604     /**
16605      * onbeforeLoad - masks the loading area.
16606      *
16607      */
16608     onBeforeLoad : function(store,opts)
16609     {
16610          //Roo.log('onBeforeLoad');   
16611         if (!opts.add) {
16612             this.el.update("");
16613         }
16614         this.el.mask(this.mask ? this.mask : "Loading" ); 
16615     },
16616     onLoad : function ()
16617     {
16618         this.el.unmask();
16619     },
16620     
16621
16622     /**
16623      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16624      * @param {HTMLElement} node
16625      * @return {HTMLElement} The template node
16626      */
16627     findItemFromChild : function(node){
16628         var el = this.dataName  ?
16629             this.el.child('.roo-tpl-' + this.dataName,true) :
16630             this.el.dom; 
16631         
16632         if(!node || node.parentNode == el){
16633                     return node;
16634             }
16635             var p = node.parentNode;
16636             while(p && p != el){
16637             if(p.parentNode == el){
16638                 return p;
16639             }
16640             p = p.parentNode;
16641         }
16642             return null;
16643     },
16644
16645     /** @ignore */
16646     onClick : function(e){
16647         var item = this.findItemFromChild(e.getTarget());
16648         if(item){
16649             var index = this.indexOf(item);
16650             if(this.onItemClick(item, index, e) !== false){
16651                 this.fireEvent("click", this, index, item, e);
16652             }
16653         }else{
16654             this.clearSelections();
16655         }
16656     },
16657
16658     /** @ignore */
16659     onContextMenu : function(e){
16660         var item = this.findItemFromChild(e.getTarget());
16661         if(item){
16662             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16663         }
16664     },
16665
16666     /** @ignore */
16667     onDblClick : function(e){
16668         var item = this.findItemFromChild(e.getTarget());
16669         if(item){
16670             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16671         }
16672     },
16673
16674     onItemClick : function(item, index, e)
16675     {
16676         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16677             return false;
16678         }
16679         if (this.toggleSelect) {
16680             var m = this.isSelected(item) ? 'unselect' : 'select';
16681             //Roo.log(m);
16682             var _t = this;
16683             _t[m](item, true, false);
16684             return true;
16685         }
16686         if(this.multiSelect || this.singleSelect){
16687             if(this.multiSelect && e.shiftKey && this.lastSelection){
16688                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16689             }else{
16690                 this.select(item, this.multiSelect && e.ctrlKey);
16691                 this.lastSelection = item;
16692             }
16693             
16694             if(!this.tickable){
16695                 e.preventDefault();
16696             }
16697             
16698         }
16699         return true;
16700     },
16701
16702     /**
16703      * Get the number of selected nodes.
16704      * @return {Number}
16705      */
16706     getSelectionCount : function(){
16707         return this.selections.length;
16708     },
16709
16710     /**
16711      * Get the currently selected nodes.
16712      * @return {Array} An array of HTMLElements
16713      */
16714     getSelectedNodes : function(){
16715         return this.selections;
16716     },
16717
16718     /**
16719      * Get the indexes of the selected nodes.
16720      * @return {Array}
16721      */
16722     getSelectedIndexes : function(){
16723         var indexes = [], s = this.selections;
16724         for(var i = 0, len = s.length; i < len; i++){
16725             indexes.push(s[i].nodeIndex);
16726         }
16727         return indexes;
16728     },
16729
16730     /**
16731      * Clear all selections
16732      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16733      */
16734     clearSelections : function(suppressEvent){
16735         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16736             this.cmp.elements = this.selections;
16737             this.cmp.removeClass(this.selectedClass);
16738             this.selections = [];
16739             if(!suppressEvent){
16740                 this.fireEvent("selectionchange", this, this.selections);
16741             }
16742         }
16743     },
16744
16745     /**
16746      * Returns true if the passed node is selected
16747      * @param {HTMLElement/Number} node The node or node index
16748      * @return {Boolean}
16749      */
16750     isSelected : function(node){
16751         var s = this.selections;
16752         if(s.length < 1){
16753             return false;
16754         }
16755         node = this.getNode(node);
16756         return s.indexOf(node) !== -1;
16757     },
16758
16759     /**
16760      * Selects nodes.
16761      * @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
16762      * @param {Boolean} keepExisting (optional) true to keep existing selections
16763      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16764      */
16765     select : function(nodeInfo, keepExisting, suppressEvent){
16766         if(nodeInfo instanceof Array){
16767             if(!keepExisting){
16768                 this.clearSelections(true);
16769             }
16770             for(var i = 0, len = nodeInfo.length; i < len; i++){
16771                 this.select(nodeInfo[i], true, true);
16772             }
16773             return;
16774         } 
16775         var node = this.getNode(nodeInfo);
16776         if(!node || this.isSelected(node)){
16777             return; // already selected.
16778         }
16779         if(!keepExisting){
16780             this.clearSelections(true);
16781         }
16782         
16783         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16784             Roo.fly(node).addClass(this.selectedClass);
16785             this.selections.push(node);
16786             if(!suppressEvent){
16787                 this.fireEvent("selectionchange", this, this.selections);
16788             }
16789         }
16790         
16791         
16792     },
16793       /**
16794      * Unselects nodes.
16795      * @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
16796      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16797      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16798      */
16799     unselect : function(nodeInfo, keepExisting, suppressEvent)
16800     {
16801         if(nodeInfo instanceof Array){
16802             Roo.each(this.selections, function(s) {
16803                 this.unselect(s, nodeInfo);
16804             }, this);
16805             return;
16806         }
16807         var node = this.getNode(nodeInfo);
16808         if(!node || !this.isSelected(node)){
16809             //Roo.log("not selected");
16810             return; // not selected.
16811         }
16812         // fireevent???
16813         var ns = [];
16814         Roo.each(this.selections, function(s) {
16815             if (s == node ) {
16816                 Roo.fly(node).removeClass(this.selectedClass);
16817
16818                 return;
16819             }
16820             ns.push(s);
16821         },this);
16822         
16823         this.selections= ns;
16824         this.fireEvent("selectionchange", this, this.selections);
16825     },
16826
16827     /**
16828      * Gets a template node.
16829      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16830      * @return {HTMLElement} The node or null if it wasn't found
16831      */
16832     getNode : function(nodeInfo){
16833         if(typeof nodeInfo == "string"){
16834             return document.getElementById(nodeInfo);
16835         }else if(typeof nodeInfo == "number"){
16836             return this.nodes[nodeInfo];
16837         }
16838         return nodeInfo;
16839     },
16840
16841     /**
16842      * Gets a range template nodes.
16843      * @param {Number} startIndex
16844      * @param {Number} endIndex
16845      * @return {Array} An array of nodes
16846      */
16847     getNodes : function(start, end){
16848         var ns = this.nodes;
16849         start = start || 0;
16850         end = typeof end == "undefined" ? ns.length - 1 : end;
16851         var nodes = [];
16852         if(start <= end){
16853             for(var i = start; i <= end; i++){
16854                 nodes.push(ns[i]);
16855             }
16856         } else{
16857             for(var i = start; i >= end; i--){
16858                 nodes.push(ns[i]);
16859             }
16860         }
16861         return nodes;
16862     },
16863
16864     /**
16865      * Finds the index of the passed node
16866      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16867      * @return {Number} The index of the node or -1
16868      */
16869     indexOf : function(node){
16870         node = this.getNode(node);
16871         if(typeof node.nodeIndex == "number"){
16872             return node.nodeIndex;
16873         }
16874         var ns = this.nodes;
16875         for(var i = 0, len = ns.length; i < len; i++){
16876             if(ns[i] == node){
16877                 return i;
16878             }
16879         }
16880         return -1;
16881     }
16882 });
16883 /*
16884  * - LGPL
16885  *
16886  * based on jquery fullcalendar
16887  * 
16888  */
16889
16890 Roo.bootstrap = Roo.bootstrap || {};
16891 /**
16892  * @class Roo.bootstrap.Calendar
16893  * @extends Roo.bootstrap.Component
16894  * Bootstrap Calendar class
16895  * @cfg {Boolean} loadMask (true|false) default false
16896  * @cfg {Object} header generate the user specific header of the calendar, default false
16897
16898  * @constructor
16899  * Create a new Container
16900  * @param {Object} config The config object
16901  */
16902
16903
16904
16905 Roo.bootstrap.Calendar = function(config){
16906     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16907      this.addEvents({
16908         /**
16909              * @event select
16910              * Fires when a date is selected
16911              * @param {DatePicker} this
16912              * @param {Date} date The selected date
16913              */
16914         'select': true,
16915         /**
16916              * @event monthchange
16917              * Fires when the displayed month changes 
16918              * @param {DatePicker} this
16919              * @param {Date} date The selected month
16920              */
16921         'monthchange': true,
16922         /**
16923              * @event evententer
16924              * Fires when mouse over an event
16925              * @param {Calendar} this
16926              * @param {event} Event
16927              */
16928         'evententer': true,
16929         /**
16930              * @event eventleave
16931              * Fires when the mouse leaves an
16932              * @param {Calendar} this
16933              * @param {event}
16934              */
16935         'eventleave': true,
16936         /**
16937              * @event eventclick
16938              * Fires when the mouse click an
16939              * @param {Calendar} this
16940              * @param {event}
16941              */
16942         'eventclick': true
16943         
16944     });
16945
16946 };
16947
16948 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16949     
16950      /**
16951      * @cfg {Number} startDay
16952      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16953      */
16954     startDay : 0,
16955     
16956     loadMask : false,
16957     
16958     header : false,
16959       
16960     getAutoCreate : function(){
16961         
16962         
16963         var fc_button = function(name, corner, style, content ) {
16964             return Roo.apply({},{
16965                 tag : 'span',
16966                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16967                          (corner.length ?
16968                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16969                             ''
16970                         ),
16971                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16972                 unselectable: 'on'
16973             });
16974         };
16975         
16976         var header = {};
16977         
16978         if(!this.header){
16979             header = {
16980                 tag : 'table',
16981                 cls : 'fc-header',
16982                 style : 'width:100%',
16983                 cn : [
16984                     {
16985                         tag: 'tr',
16986                         cn : [
16987                             {
16988                                 tag : 'td',
16989                                 cls : 'fc-header-left',
16990                                 cn : [
16991                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16992                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16993                                     { tag: 'span', cls: 'fc-header-space' },
16994                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16995
16996
16997                                 ]
16998                             },
16999
17000                             {
17001                                 tag : 'td',
17002                                 cls : 'fc-header-center',
17003                                 cn : [
17004                                     {
17005                                         tag: 'span',
17006                                         cls: 'fc-header-title',
17007                                         cn : {
17008                                             tag: 'H2',
17009                                             html : 'month / year'
17010                                         }
17011                                     }
17012
17013                                 ]
17014                             },
17015                             {
17016                                 tag : 'td',
17017                                 cls : 'fc-header-right',
17018                                 cn : [
17019                               /*      fc_button('month', 'left', '', 'month' ),
17020                                     fc_button('week', '', '', 'week' ),
17021                                     fc_button('day', 'right', '', 'day' )
17022                                 */    
17023
17024                                 ]
17025                             }
17026
17027                         ]
17028                     }
17029                 ]
17030             };
17031         }
17032         
17033         header = this.header;
17034         
17035        
17036         var cal_heads = function() {
17037             var ret = [];
17038             // fixme - handle this.
17039             
17040             for (var i =0; i < Date.dayNames.length; i++) {
17041                 var d = Date.dayNames[i];
17042                 ret.push({
17043                     tag: 'th',
17044                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
17045                     html : d.substring(0,3)
17046                 });
17047                 
17048             }
17049             ret[0].cls += ' fc-first';
17050             ret[6].cls += ' fc-last';
17051             return ret;
17052         };
17053         var cal_cell = function(n) {
17054             return  {
17055                 tag: 'td',
17056                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
17057                 cn : [
17058                     {
17059                         cn : [
17060                             {
17061                                 cls: 'fc-day-number',
17062                                 html: 'D'
17063                             },
17064                             {
17065                                 cls: 'fc-day-content',
17066                              
17067                                 cn : [
17068                                      {
17069                                         style: 'position: relative;' // height: 17px;
17070                                     }
17071                                 ]
17072                             }
17073                             
17074                             
17075                         ]
17076                     }
17077                 ]
17078                 
17079             }
17080         };
17081         var cal_rows = function() {
17082             
17083             var ret = [];
17084             for (var r = 0; r < 6; r++) {
17085                 var row= {
17086                     tag : 'tr',
17087                     cls : 'fc-week',
17088                     cn : []
17089                 };
17090                 
17091                 for (var i =0; i < Date.dayNames.length; i++) {
17092                     var d = Date.dayNames[i];
17093                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
17094
17095                 }
17096                 row.cn[0].cls+=' fc-first';
17097                 row.cn[0].cn[0].style = 'min-height:90px';
17098                 row.cn[6].cls+=' fc-last';
17099                 ret.push(row);
17100                 
17101             }
17102             ret[0].cls += ' fc-first';
17103             ret[4].cls += ' fc-prev-last';
17104             ret[5].cls += ' fc-last';
17105             return ret;
17106             
17107         };
17108         
17109         var cal_table = {
17110             tag: 'table',
17111             cls: 'fc-border-separate',
17112             style : 'width:100%',
17113             cellspacing  : 0,
17114             cn : [
17115                 { 
17116                     tag: 'thead',
17117                     cn : [
17118                         { 
17119                             tag: 'tr',
17120                             cls : 'fc-first fc-last',
17121                             cn : cal_heads()
17122                         }
17123                     ]
17124                 },
17125                 { 
17126                     tag: 'tbody',
17127                     cn : cal_rows()
17128                 }
17129                   
17130             ]
17131         };
17132          
17133          var cfg = {
17134             cls : 'fc fc-ltr',
17135             cn : [
17136                 header,
17137                 {
17138                     cls : 'fc-content',
17139                     style : "position: relative;",
17140                     cn : [
17141                         {
17142                             cls : 'fc-view fc-view-month fc-grid',
17143                             style : 'position: relative',
17144                             unselectable : 'on',
17145                             cn : [
17146                                 {
17147                                     cls : 'fc-event-container',
17148                                     style : 'position:absolute;z-index:8;top:0;left:0;'
17149                                 },
17150                                 cal_table
17151                             ]
17152                         }
17153                     ]
17154     
17155                 }
17156            ] 
17157             
17158         };
17159         
17160          
17161         
17162         return cfg;
17163     },
17164     
17165     
17166     initEvents : function()
17167     {
17168         if(!this.store){
17169             throw "can not find store for calendar";
17170         }
17171         
17172         var mark = {
17173             tag: "div",
17174             cls:"x-dlg-mask",
17175             style: "text-align:center",
17176             cn: [
17177                 {
17178                     tag: "div",
17179                     style: "background-color:white;width:50%;margin:250 auto",
17180                     cn: [
17181                         {
17182                             tag: "img",
17183                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
17184                         },
17185                         {
17186                             tag: "span",
17187                             html: "Loading"
17188                         }
17189                         
17190                     ]
17191                 }
17192             ]
17193         };
17194         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17195         
17196         var size = this.el.select('.fc-content', true).first().getSize();
17197         this.maskEl.setSize(size.width, size.height);
17198         this.maskEl.enableDisplayMode("block");
17199         if(!this.loadMask){
17200             this.maskEl.hide();
17201         }
17202         
17203         this.store = Roo.factory(this.store, Roo.data);
17204         this.store.on('load', this.onLoad, this);
17205         this.store.on('beforeload', this.onBeforeLoad, this);
17206         
17207         this.resize();
17208         
17209         this.cells = this.el.select('.fc-day',true);
17210         //Roo.log(this.cells);
17211         this.textNodes = this.el.query('.fc-day-number');
17212         this.cells.addClassOnOver('fc-state-hover');
17213         
17214         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17215         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17216         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17217         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17218         
17219         this.on('monthchange', this.onMonthChange, this);
17220         
17221         this.update(new Date().clearTime());
17222     },
17223     
17224     resize : function() {
17225         var sz  = this.el.getSize();
17226         
17227         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17228         this.el.select('.fc-day-content div',true).setHeight(34);
17229     },
17230     
17231     
17232     // private
17233     showPrevMonth : function(e){
17234         this.update(this.activeDate.add("mo", -1));
17235     },
17236     showToday : function(e){
17237         this.update(new Date().clearTime());
17238     },
17239     // private
17240     showNextMonth : function(e){
17241         this.update(this.activeDate.add("mo", 1));
17242     },
17243
17244     // private
17245     showPrevYear : function(){
17246         this.update(this.activeDate.add("y", -1));
17247     },
17248
17249     // private
17250     showNextYear : function(){
17251         this.update(this.activeDate.add("y", 1));
17252     },
17253
17254     
17255    // private
17256     update : function(date)
17257     {
17258         var vd = this.activeDate;
17259         this.activeDate = date;
17260 //        if(vd && this.el){
17261 //            var t = date.getTime();
17262 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17263 //                Roo.log('using add remove');
17264 //                
17265 //                this.fireEvent('monthchange', this, date);
17266 //                
17267 //                this.cells.removeClass("fc-state-highlight");
17268 //                this.cells.each(function(c){
17269 //                   if(c.dateValue == t){
17270 //                       c.addClass("fc-state-highlight");
17271 //                       setTimeout(function(){
17272 //                            try{c.dom.firstChild.focus();}catch(e){}
17273 //                       }, 50);
17274 //                       return false;
17275 //                   }
17276 //                   return true;
17277 //                });
17278 //                return;
17279 //            }
17280 //        }
17281         
17282         var days = date.getDaysInMonth();
17283         
17284         var firstOfMonth = date.getFirstDateOfMonth();
17285         var startingPos = firstOfMonth.getDay()-this.startDay;
17286         
17287         if(startingPos < this.startDay){
17288             startingPos += 7;
17289         }
17290         
17291         var pm = date.add(Date.MONTH, -1);
17292         var prevStart = pm.getDaysInMonth()-startingPos;
17293 //        
17294         this.cells = this.el.select('.fc-day',true);
17295         this.textNodes = this.el.query('.fc-day-number');
17296         this.cells.addClassOnOver('fc-state-hover');
17297         
17298         var cells = this.cells.elements;
17299         var textEls = this.textNodes;
17300         
17301         Roo.each(cells, function(cell){
17302             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17303         });
17304         
17305         days += startingPos;
17306
17307         // convert everything to numbers so it's fast
17308         var day = 86400000;
17309         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17310         //Roo.log(d);
17311         //Roo.log(pm);
17312         //Roo.log(prevStart);
17313         
17314         var today = new Date().clearTime().getTime();
17315         var sel = date.clearTime().getTime();
17316         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17317         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17318         var ddMatch = this.disabledDatesRE;
17319         var ddText = this.disabledDatesText;
17320         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17321         var ddaysText = this.disabledDaysText;
17322         var format = this.format;
17323         
17324         var setCellClass = function(cal, cell){
17325             cell.row = 0;
17326             cell.events = [];
17327             cell.more = [];
17328             //Roo.log('set Cell Class');
17329             cell.title = "";
17330             var t = d.getTime();
17331             
17332             //Roo.log(d);
17333             
17334             cell.dateValue = t;
17335             if(t == today){
17336                 cell.className += " fc-today";
17337                 cell.className += " fc-state-highlight";
17338                 cell.title = cal.todayText;
17339             }
17340             if(t == sel){
17341                 // disable highlight in other month..
17342                 //cell.className += " fc-state-highlight";
17343                 
17344             }
17345             // disabling
17346             if(t < min) {
17347                 cell.className = " fc-state-disabled";
17348                 cell.title = cal.minText;
17349                 return;
17350             }
17351             if(t > max) {
17352                 cell.className = " fc-state-disabled";
17353                 cell.title = cal.maxText;
17354                 return;
17355             }
17356             if(ddays){
17357                 if(ddays.indexOf(d.getDay()) != -1){
17358                     cell.title = ddaysText;
17359                     cell.className = " fc-state-disabled";
17360                 }
17361             }
17362             if(ddMatch && format){
17363                 var fvalue = d.dateFormat(format);
17364                 if(ddMatch.test(fvalue)){
17365                     cell.title = ddText.replace("%0", fvalue);
17366                     cell.className = " fc-state-disabled";
17367                 }
17368             }
17369             
17370             if (!cell.initialClassName) {
17371                 cell.initialClassName = cell.dom.className;
17372             }
17373             
17374             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17375         };
17376
17377         var i = 0;
17378         
17379         for(; i < startingPos; i++) {
17380             textEls[i].innerHTML = (++prevStart);
17381             d.setDate(d.getDate()+1);
17382             
17383             cells[i].className = "fc-past fc-other-month";
17384             setCellClass(this, cells[i]);
17385         }
17386         
17387         var intDay = 0;
17388         
17389         for(; i < days; i++){
17390             intDay = i - startingPos + 1;
17391             textEls[i].innerHTML = (intDay);
17392             d.setDate(d.getDate()+1);
17393             
17394             cells[i].className = ''; // "x-date-active";
17395             setCellClass(this, cells[i]);
17396         }
17397         var extraDays = 0;
17398         
17399         for(; i < 42; i++) {
17400             textEls[i].innerHTML = (++extraDays);
17401             d.setDate(d.getDate()+1);
17402             
17403             cells[i].className = "fc-future fc-other-month";
17404             setCellClass(this, cells[i]);
17405         }
17406         
17407         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17408         
17409         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17410         
17411         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17412         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17413         
17414         if(totalRows != 6){
17415             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17416             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17417         }
17418         
17419         this.fireEvent('monthchange', this, date);
17420         
17421         
17422         /*
17423         if(!this.internalRender){
17424             var main = this.el.dom.firstChild;
17425             var w = main.offsetWidth;
17426             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17427             Roo.fly(main).setWidth(w);
17428             this.internalRender = true;
17429             // opera does not respect the auto grow header center column
17430             // then, after it gets a width opera refuses to recalculate
17431             // without a second pass
17432             if(Roo.isOpera && !this.secondPass){
17433                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17434                 this.secondPass = true;
17435                 this.update.defer(10, this, [date]);
17436             }
17437         }
17438         */
17439         
17440     },
17441     
17442     findCell : function(dt) {
17443         dt = dt.clearTime().getTime();
17444         var ret = false;
17445         this.cells.each(function(c){
17446             //Roo.log("check " +c.dateValue + '?=' + dt);
17447             if(c.dateValue == dt){
17448                 ret = c;
17449                 return false;
17450             }
17451             return true;
17452         });
17453         
17454         return ret;
17455     },
17456     
17457     findCells : function(ev) {
17458         var s = ev.start.clone().clearTime().getTime();
17459        // Roo.log(s);
17460         var e= ev.end.clone().clearTime().getTime();
17461        // Roo.log(e);
17462         var ret = [];
17463         this.cells.each(function(c){
17464              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17465             
17466             if(c.dateValue > e){
17467                 return ;
17468             }
17469             if(c.dateValue < s){
17470                 return ;
17471             }
17472             ret.push(c);
17473         });
17474         
17475         return ret;    
17476     },
17477     
17478 //    findBestRow: function(cells)
17479 //    {
17480 //        var ret = 0;
17481 //        
17482 //        for (var i =0 ; i < cells.length;i++) {
17483 //            ret  = Math.max(cells[i].rows || 0,ret);
17484 //        }
17485 //        return ret;
17486 //        
17487 //    },
17488     
17489     
17490     addItem : function(ev)
17491     {
17492         // look for vertical location slot in
17493         var cells = this.findCells(ev);
17494         
17495 //        ev.row = this.findBestRow(cells);
17496         
17497         // work out the location.
17498         
17499         var crow = false;
17500         var rows = [];
17501         for(var i =0; i < cells.length; i++) {
17502             
17503             cells[i].row = cells[0].row;
17504             
17505             if(i == 0){
17506                 cells[i].row = cells[i].row + 1;
17507             }
17508             
17509             if (!crow) {
17510                 crow = {
17511                     start : cells[i],
17512                     end :  cells[i]
17513                 };
17514                 continue;
17515             }
17516             if (crow.start.getY() == cells[i].getY()) {
17517                 // on same row.
17518                 crow.end = cells[i];
17519                 continue;
17520             }
17521             // different row.
17522             rows.push(crow);
17523             crow = {
17524                 start: cells[i],
17525                 end : cells[i]
17526             };
17527             
17528         }
17529         
17530         rows.push(crow);
17531         ev.els = [];
17532         ev.rows = rows;
17533         ev.cells = cells;
17534         
17535         cells[0].events.push(ev);
17536         
17537         this.calevents.push(ev);
17538     },
17539     
17540     clearEvents: function() {
17541         
17542         if(!this.calevents){
17543             return;
17544         }
17545         
17546         Roo.each(this.cells.elements, function(c){
17547             c.row = 0;
17548             c.events = [];
17549             c.more = [];
17550         });
17551         
17552         Roo.each(this.calevents, function(e) {
17553             Roo.each(e.els, function(el) {
17554                 el.un('mouseenter' ,this.onEventEnter, this);
17555                 el.un('mouseleave' ,this.onEventLeave, this);
17556                 el.remove();
17557             },this);
17558         },this);
17559         
17560         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17561             e.remove();
17562         });
17563         
17564     },
17565     
17566     renderEvents: function()
17567     {   
17568         var _this = this;
17569         
17570         this.cells.each(function(c) {
17571             
17572             if(c.row < 5){
17573                 return;
17574             }
17575             
17576             var ev = c.events;
17577             
17578             var r = 4;
17579             if(c.row != c.events.length){
17580                 r = 4 - (4 - (c.row - c.events.length));
17581             }
17582             
17583             c.events = ev.slice(0, r);
17584             c.more = ev.slice(r);
17585             
17586             if(c.more.length && c.more.length == 1){
17587                 c.events.push(c.more.pop());
17588             }
17589             
17590             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17591             
17592         });
17593             
17594         this.cells.each(function(c) {
17595             
17596             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17597             
17598             
17599             for (var e = 0; e < c.events.length; e++){
17600                 var ev = c.events[e];
17601                 var rows = ev.rows;
17602                 
17603                 for(var i = 0; i < rows.length; i++) {
17604                 
17605                     // how many rows should it span..
17606
17607                     var  cfg = {
17608                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17609                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17610
17611                         unselectable : "on",
17612                         cn : [
17613                             {
17614                                 cls: 'fc-event-inner',
17615                                 cn : [
17616     //                                {
17617     //                                  tag:'span',
17618     //                                  cls: 'fc-event-time',
17619     //                                  html : cells.length > 1 ? '' : ev.time
17620     //                                },
17621                                     {
17622                                       tag:'span',
17623                                       cls: 'fc-event-title',
17624                                       html : String.format('{0}', ev.title)
17625                                     }
17626
17627
17628                                 ]
17629                             },
17630                             {
17631                                 cls: 'ui-resizable-handle ui-resizable-e',
17632                                 html : '&nbsp;&nbsp;&nbsp'
17633                             }
17634
17635                         ]
17636                     };
17637
17638                     if (i == 0) {
17639                         cfg.cls += ' fc-event-start';
17640                     }
17641                     if ((i+1) == rows.length) {
17642                         cfg.cls += ' fc-event-end';
17643                     }
17644
17645                     var ctr = _this.el.select('.fc-event-container',true).first();
17646                     var cg = ctr.createChild(cfg);
17647
17648                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17649                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17650
17651                     var r = (c.more.length) ? 1 : 0;
17652                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17653                     cg.setWidth(ebox.right - sbox.x -2);
17654
17655                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17656                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17657                     cg.on('click', _this.onEventClick, _this, ev);
17658
17659                     ev.els.push(cg);
17660                     
17661                 }
17662                 
17663             }
17664             
17665             
17666             if(c.more.length){
17667                 var  cfg = {
17668                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17669                     style : 'position: absolute',
17670                     unselectable : "on",
17671                     cn : [
17672                         {
17673                             cls: 'fc-event-inner',
17674                             cn : [
17675                                 {
17676                                   tag:'span',
17677                                   cls: 'fc-event-title',
17678                                   html : 'More'
17679                                 }
17680
17681
17682                             ]
17683                         },
17684                         {
17685                             cls: 'ui-resizable-handle ui-resizable-e',
17686                             html : '&nbsp;&nbsp;&nbsp'
17687                         }
17688
17689                     ]
17690                 };
17691
17692                 var ctr = _this.el.select('.fc-event-container',true).first();
17693                 var cg = ctr.createChild(cfg);
17694
17695                 var sbox = c.select('.fc-day-content',true).first().getBox();
17696                 var ebox = c.select('.fc-day-content',true).first().getBox();
17697                 //Roo.log(cg);
17698                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17699                 cg.setWidth(ebox.right - sbox.x -2);
17700
17701                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17702                 
17703             }
17704             
17705         });
17706         
17707         
17708         
17709     },
17710     
17711     onEventEnter: function (e, el,event,d) {
17712         this.fireEvent('evententer', this, el, event);
17713     },
17714     
17715     onEventLeave: function (e, el,event,d) {
17716         this.fireEvent('eventleave', this, el, event);
17717     },
17718     
17719     onEventClick: function (e, el,event,d) {
17720         this.fireEvent('eventclick', this, el, event);
17721     },
17722     
17723     onMonthChange: function () {
17724         this.store.load();
17725     },
17726     
17727     onMoreEventClick: function(e, el, more)
17728     {
17729         var _this = this;
17730         
17731         this.calpopover.placement = 'right';
17732         this.calpopover.setTitle('More');
17733         
17734         this.calpopover.setContent('');
17735         
17736         var ctr = this.calpopover.el.select('.popover-content', true).first();
17737         
17738         Roo.each(more, function(m){
17739             var cfg = {
17740                 cls : 'fc-event-hori fc-event-draggable',
17741                 html : m.title
17742             };
17743             var cg = ctr.createChild(cfg);
17744             
17745             cg.on('click', _this.onEventClick, _this, m);
17746         });
17747         
17748         this.calpopover.show(el);
17749         
17750         
17751     },
17752     
17753     onLoad: function () 
17754     {   
17755         this.calevents = [];
17756         var cal = this;
17757         
17758         if(this.store.getCount() > 0){
17759             this.store.data.each(function(d){
17760                cal.addItem({
17761                     id : d.data.id,
17762                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17763                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17764                     time : d.data.start_time,
17765                     title : d.data.title,
17766                     description : d.data.description,
17767                     venue : d.data.venue
17768                 });
17769             });
17770         }
17771         
17772         this.renderEvents();
17773         
17774         if(this.calevents.length && this.loadMask){
17775             this.maskEl.hide();
17776         }
17777     },
17778     
17779     onBeforeLoad: function()
17780     {
17781         this.clearEvents();
17782         if(this.loadMask){
17783             this.maskEl.show();
17784         }
17785     }
17786 });
17787
17788  
17789  /*
17790  * - LGPL
17791  *
17792  * element
17793  * 
17794  */
17795
17796 /**
17797  * @class Roo.bootstrap.Popover
17798  * @extends Roo.bootstrap.Component
17799  * Bootstrap Popover class
17800  * @cfg {String} html contents of the popover   (or false to use children..)
17801  * @cfg {String} title of popover (or false to hide)
17802  * @cfg {String} placement how it is placed
17803  * @cfg {String} trigger click || hover (or false to trigger manually)
17804  * @cfg {String} over what (parent or false to trigger manually.)
17805  * @cfg {Number} delay - delay before showing
17806  
17807  * @constructor
17808  * Create a new Popover
17809  * @param {Object} config The config object
17810  */
17811
17812 Roo.bootstrap.Popover = function(config){
17813     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17814     
17815     this.addEvents({
17816         // raw events
17817          /**
17818          * @event show
17819          * After the popover show
17820          * 
17821          * @param {Roo.bootstrap.Popover} this
17822          */
17823         "show" : true,
17824         /**
17825          * @event hide
17826          * After the popover hide
17827          * 
17828          * @param {Roo.bootstrap.Popover} this
17829          */
17830         "hide" : true
17831     });
17832 };
17833
17834 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17835     
17836     title: 'Fill in a title',
17837     html: false,
17838     
17839     placement : 'right',
17840     trigger : 'hover', // hover
17841     
17842     delay : 0,
17843     
17844     over: 'parent',
17845     
17846     can_build_overlaid : false,
17847     
17848     getChildContainer : function()
17849     {
17850         return this.el.select('.popover-content',true).first();
17851     },
17852     
17853     getAutoCreate : function(){
17854          
17855         var cfg = {
17856            cls : 'popover roo-dynamic',
17857            style: 'display:block',
17858            cn : [
17859                 {
17860                     cls : 'arrow'
17861                 },
17862                 {
17863                     cls : 'popover-inner',
17864                     cn : [
17865                         {
17866                             tag: 'h3',
17867                             cls: 'popover-title popover-header',
17868                             html : this.title
17869                         },
17870                         {
17871                             cls : 'popover-content popover-body',
17872                             html : this.html
17873                         }
17874                     ]
17875                     
17876                 }
17877            ]
17878         };
17879         
17880         return cfg;
17881     },
17882     setTitle: function(str)
17883     {
17884         this.title = str;
17885         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17886     },
17887     setContent: function(str)
17888     {
17889         this.html = str;
17890         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17891     },
17892     // as it get's added to the bottom of the page.
17893     onRender : function(ct, position)
17894     {
17895         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17896         if(!this.el){
17897             var cfg = Roo.apply({},  this.getAutoCreate());
17898             cfg.id = Roo.id();
17899             
17900             if (this.cls) {
17901                 cfg.cls += ' ' + this.cls;
17902             }
17903             if (this.style) {
17904                 cfg.style = this.style;
17905             }
17906             //Roo.log("adding to ");
17907             this.el = Roo.get(document.body).createChild(cfg, position);
17908 //            Roo.log(this.el);
17909         }
17910         this.initEvents();
17911     },
17912     
17913     initEvents : function()
17914     {
17915         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17916         this.el.enableDisplayMode('block');
17917         this.el.hide();
17918         if (this.over === false) {
17919             return; 
17920         }
17921         if (this.triggers === false) {
17922             return;
17923         }
17924         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17925         var triggers = this.trigger ? this.trigger.split(' ') : [];
17926         Roo.each(triggers, function(trigger) {
17927         
17928             if (trigger == 'click') {
17929                 on_el.on('click', this.toggle, this);
17930             } else if (trigger != 'manual') {
17931                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17932                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17933       
17934                 on_el.on(eventIn  ,this.enter, this);
17935                 on_el.on(eventOut, this.leave, this);
17936             }
17937         }, this);
17938         
17939     },
17940     
17941     
17942     // private
17943     timeout : null,
17944     hoverState : null,
17945     
17946     toggle : function () {
17947         this.hoverState == 'in' ? this.leave() : this.enter();
17948     },
17949     
17950     enter : function () {
17951         
17952         clearTimeout(this.timeout);
17953     
17954         this.hoverState = 'in';
17955     
17956         if (!this.delay || !this.delay.show) {
17957             this.show();
17958             return;
17959         }
17960         var _t = this;
17961         this.timeout = setTimeout(function () {
17962             if (_t.hoverState == 'in') {
17963                 _t.show();
17964             }
17965         }, this.delay.show)
17966     },
17967     
17968     leave : function() {
17969         clearTimeout(this.timeout);
17970     
17971         this.hoverState = 'out';
17972     
17973         if (!this.delay || !this.delay.hide) {
17974             this.hide();
17975             return;
17976         }
17977         var _t = this;
17978         this.timeout = setTimeout(function () {
17979             if (_t.hoverState == 'out') {
17980                 _t.hide();
17981             }
17982         }, this.delay.hide)
17983     },
17984     
17985     show : function (on_el)
17986     {
17987         if (!on_el) {
17988             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17989         }
17990         
17991         // set content.
17992         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17993         if (this.html !== false) {
17994             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17995         }
17996         this.el.removeClass([
17997             'fade','top','bottom', 'left', 'right','in',
17998             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
17999         ]);
18000         if (!this.title.length) {
18001             this.el.select('.popover-title',true).hide();
18002         }
18003         
18004         var placement = typeof this.placement == 'function' ?
18005             this.placement.call(this, this.el, on_el) :
18006             this.placement;
18007             
18008         var autoToken = /\s?auto?\s?/i;
18009         var autoPlace = autoToken.test(placement);
18010         if (autoPlace) {
18011             placement = placement.replace(autoToken, '') || 'top';
18012         }
18013         
18014         //this.el.detach()
18015         //this.el.setXY([0,0]);
18016         this.el.show();
18017         this.el.dom.style.display='block';
18018         this.el.addClass(placement);
18019         
18020         //this.el.appendTo(on_el);
18021         
18022         var p = this.getPosition();
18023         var box = this.el.getBox();
18024         
18025         if (autoPlace) {
18026             // fixme..
18027         }
18028         var align = Roo.bootstrap.Popover.alignment[placement];
18029         
18030 //        Roo.log(align);
18031         this.el.alignTo(on_el, align[0],align[1]);
18032         //var arrow = this.el.select('.arrow',true).first();
18033         //arrow.set(align[2], 
18034         
18035         this.el.addClass('in');
18036         
18037         
18038         if (this.el.hasClass('fade')) {
18039             // fade it?
18040         }
18041         
18042         this.hoverState = 'in';
18043         
18044         this.fireEvent('show', this);
18045         
18046     },
18047     hide : function()
18048     {
18049         this.el.setXY([0,0]);
18050         this.el.removeClass('in');
18051         this.el.hide();
18052         this.hoverState = null;
18053         
18054         this.fireEvent('hide', this);
18055     }
18056     
18057 });
18058
18059 Roo.bootstrap.Popover.alignment = {
18060     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
18061     'right' : ['l-r', [10,0], 'left bs-popover-left'],
18062     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
18063     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
18064 };
18065
18066  /*
18067  * - LGPL
18068  *
18069  * Progress
18070  * 
18071  */
18072
18073 /**
18074  * @class Roo.bootstrap.Progress
18075  * @extends Roo.bootstrap.Component
18076  * Bootstrap Progress class
18077  * @cfg {Boolean} striped striped of the progress bar
18078  * @cfg {Boolean} active animated of the progress bar
18079  * 
18080  * 
18081  * @constructor
18082  * Create a new Progress
18083  * @param {Object} config The config object
18084  */
18085
18086 Roo.bootstrap.Progress = function(config){
18087     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
18088 };
18089
18090 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
18091     
18092     striped : false,
18093     active: false,
18094     
18095     getAutoCreate : function(){
18096         var cfg = {
18097             tag: 'div',
18098             cls: 'progress'
18099         };
18100         
18101         
18102         if(this.striped){
18103             cfg.cls += ' progress-striped';
18104         }
18105       
18106         if(this.active){
18107             cfg.cls += ' active';
18108         }
18109         
18110         
18111         return cfg;
18112     }
18113    
18114 });
18115
18116  
18117
18118  /*
18119  * - LGPL
18120  *
18121  * ProgressBar
18122  * 
18123  */
18124
18125 /**
18126  * @class Roo.bootstrap.ProgressBar
18127  * @extends Roo.bootstrap.Component
18128  * Bootstrap ProgressBar class
18129  * @cfg {Number} aria_valuenow aria-value now
18130  * @cfg {Number} aria_valuemin aria-value min
18131  * @cfg {Number} aria_valuemax aria-value max
18132  * @cfg {String} label label for the progress bar
18133  * @cfg {String} panel (success | info | warning | danger )
18134  * @cfg {String} role role of the progress bar
18135  * @cfg {String} sr_only text
18136  * 
18137  * 
18138  * @constructor
18139  * Create a new ProgressBar
18140  * @param {Object} config The config object
18141  */
18142
18143 Roo.bootstrap.ProgressBar = function(config){
18144     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
18145 };
18146
18147 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
18148     
18149     aria_valuenow : 0,
18150     aria_valuemin : 0,
18151     aria_valuemax : 100,
18152     label : false,
18153     panel : false,
18154     role : false,
18155     sr_only: false,
18156     
18157     getAutoCreate : function()
18158     {
18159         
18160         var cfg = {
18161             tag: 'div',
18162             cls: 'progress-bar',
18163             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18164         };
18165         
18166         if(this.sr_only){
18167             cfg.cn = {
18168                 tag: 'span',
18169                 cls: 'sr-only',
18170                 html: this.sr_only
18171             }
18172         }
18173         
18174         if(this.role){
18175             cfg.role = this.role;
18176         }
18177         
18178         if(this.aria_valuenow){
18179             cfg['aria-valuenow'] = this.aria_valuenow;
18180         }
18181         
18182         if(this.aria_valuemin){
18183             cfg['aria-valuemin'] = this.aria_valuemin;
18184         }
18185         
18186         if(this.aria_valuemax){
18187             cfg['aria-valuemax'] = this.aria_valuemax;
18188         }
18189         
18190         if(this.label && !this.sr_only){
18191             cfg.html = this.label;
18192         }
18193         
18194         if(this.panel){
18195             cfg.cls += ' progress-bar-' + this.panel;
18196         }
18197         
18198         return cfg;
18199     },
18200     
18201     update : function(aria_valuenow)
18202     {
18203         this.aria_valuenow = aria_valuenow;
18204         
18205         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18206     }
18207    
18208 });
18209
18210  
18211
18212  /*
18213  * - LGPL
18214  *
18215  * column
18216  * 
18217  */
18218
18219 /**
18220  * @class Roo.bootstrap.TabGroup
18221  * @extends Roo.bootstrap.Column
18222  * Bootstrap Column class
18223  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18224  * @cfg {Boolean} carousel true to make the group behave like a carousel
18225  * @cfg {Boolean} bullets show bullets for the panels
18226  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18227  * @cfg {Number} timer auto slide timer .. default 0 millisecond
18228  * @cfg {Boolean} showarrow (true|false) show arrow default true
18229  * 
18230  * @constructor
18231  * Create a new TabGroup
18232  * @param {Object} config The config object
18233  */
18234
18235 Roo.bootstrap.TabGroup = function(config){
18236     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18237     if (!this.navId) {
18238         this.navId = Roo.id();
18239     }
18240     this.tabs = [];
18241     Roo.bootstrap.TabGroup.register(this);
18242     
18243 };
18244
18245 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
18246     
18247     carousel : false,
18248     transition : false,
18249     bullets : 0,
18250     timer : 0,
18251     autoslide : false,
18252     slideFn : false,
18253     slideOnTouch : false,
18254     showarrow : true,
18255     
18256     getAutoCreate : function()
18257     {
18258         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18259         
18260         cfg.cls += ' tab-content';
18261         
18262         if (this.carousel) {
18263             cfg.cls += ' carousel slide';
18264             
18265             cfg.cn = [{
18266                cls : 'carousel-inner',
18267                cn : []
18268             }];
18269         
18270             if(this.bullets  && !Roo.isTouch){
18271                 
18272                 var bullets = {
18273                     cls : 'carousel-bullets',
18274                     cn : []
18275                 };
18276                
18277                 if(this.bullets_cls){
18278                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18279                 }
18280                 
18281                 bullets.cn.push({
18282                     cls : 'clear'
18283                 });
18284                 
18285                 cfg.cn[0].cn.push(bullets);
18286             }
18287             
18288             if(this.showarrow){
18289                 cfg.cn[0].cn.push({
18290                     tag : 'div',
18291                     class : 'carousel-arrow',
18292                     cn : [
18293                         {
18294                             tag : 'div',
18295                             class : 'carousel-prev',
18296                             cn : [
18297                                 {
18298                                     tag : 'i',
18299                                     class : 'fa fa-chevron-left'
18300                                 }
18301                             ]
18302                         },
18303                         {
18304                             tag : 'div',
18305                             class : 'carousel-next',
18306                             cn : [
18307                                 {
18308                                     tag : 'i',
18309                                     class : 'fa fa-chevron-right'
18310                                 }
18311                             ]
18312                         }
18313                     ]
18314                 });
18315             }
18316             
18317         }
18318         
18319         return cfg;
18320     },
18321     
18322     initEvents:  function()
18323     {
18324 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18325 //            this.el.on("touchstart", this.onTouchStart, this);
18326 //        }
18327         
18328         if(this.autoslide){
18329             var _this = this;
18330             
18331             this.slideFn = window.setInterval(function() {
18332                 _this.showPanelNext();
18333             }, this.timer);
18334         }
18335         
18336         if(this.showarrow){
18337             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18338             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18339         }
18340         
18341         
18342     },
18343     
18344 //    onTouchStart : function(e, el, o)
18345 //    {
18346 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18347 //            return;
18348 //        }
18349 //        
18350 //        this.showPanelNext();
18351 //    },
18352     
18353     
18354     getChildContainer : function()
18355     {
18356         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18357     },
18358     
18359     /**
18360     * register a Navigation item
18361     * @param {Roo.bootstrap.NavItem} the navitem to add
18362     */
18363     register : function(item)
18364     {
18365         this.tabs.push( item);
18366         item.navId = this.navId; // not really needed..
18367         this.addBullet();
18368     
18369     },
18370     
18371     getActivePanel : function()
18372     {
18373         var r = false;
18374         Roo.each(this.tabs, function(t) {
18375             if (t.active) {
18376                 r = t;
18377                 return false;
18378             }
18379             return null;
18380         });
18381         return r;
18382         
18383     },
18384     getPanelByName : function(n)
18385     {
18386         var r = false;
18387         Roo.each(this.tabs, function(t) {
18388             if (t.tabId == n) {
18389                 r = t;
18390                 return false;
18391             }
18392             return null;
18393         });
18394         return r;
18395     },
18396     indexOfPanel : function(p)
18397     {
18398         var r = false;
18399         Roo.each(this.tabs, function(t,i) {
18400             if (t.tabId == p.tabId) {
18401                 r = i;
18402                 return false;
18403             }
18404             return null;
18405         });
18406         return r;
18407     },
18408     /**
18409      * show a specific panel
18410      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18411      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18412      */
18413     showPanel : function (pan)
18414     {
18415         if(this.transition || typeof(pan) == 'undefined'){
18416             Roo.log("waiting for the transitionend");
18417             return false;
18418         }
18419         
18420         if (typeof(pan) == 'number') {
18421             pan = this.tabs[pan];
18422         }
18423         
18424         if (typeof(pan) == 'string') {
18425             pan = this.getPanelByName(pan);
18426         }
18427         
18428         var cur = this.getActivePanel();
18429         
18430         if(!pan || !cur){
18431             Roo.log('pan or acitve pan is undefined');
18432             return false;
18433         }
18434         
18435         if (pan.tabId == this.getActivePanel().tabId) {
18436             return true;
18437         }
18438         
18439         if (false === cur.fireEvent('beforedeactivate')) {
18440             return false;
18441         }
18442         
18443         if(this.bullets > 0 && !Roo.isTouch){
18444             this.setActiveBullet(this.indexOfPanel(pan));
18445         }
18446         
18447         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18448             
18449             //class="carousel-item carousel-item-next carousel-item-left"
18450             
18451             this.transition = true;
18452             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18453             var lr = dir == 'next' ? 'left' : 'right';
18454             pan.el.addClass(dir); // or prev
18455             pan.el.addClass('carousel-item-' + dir); // or prev
18456             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18457             cur.el.addClass(lr); // or right
18458             pan.el.addClass(lr);
18459             cur.el.addClass('carousel-item-' +lr); // or right
18460             pan.el.addClass('carousel-item-' +lr);
18461             
18462             
18463             var _this = this;
18464             cur.el.on('transitionend', function() {
18465                 Roo.log("trans end?");
18466                 
18467                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
18468                 pan.setActive(true);
18469                 
18470                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
18471                 cur.setActive(false);
18472                 
18473                 _this.transition = false;
18474                 
18475             }, this, { single:  true } );
18476             
18477             return true;
18478         }
18479         
18480         cur.setActive(false);
18481         pan.setActive(true);
18482         
18483         return true;
18484         
18485     },
18486     showPanelNext : function()
18487     {
18488         var i = this.indexOfPanel(this.getActivePanel());
18489         
18490         if (i >= this.tabs.length - 1 && !this.autoslide) {
18491             return;
18492         }
18493         
18494         if (i >= this.tabs.length - 1 && this.autoslide) {
18495             i = -1;
18496         }
18497         
18498         this.showPanel(this.tabs[i+1]);
18499     },
18500     
18501     showPanelPrev : function()
18502     {
18503         var i = this.indexOfPanel(this.getActivePanel());
18504         
18505         if (i  < 1 && !this.autoslide) {
18506             return;
18507         }
18508         
18509         if (i < 1 && this.autoslide) {
18510             i = this.tabs.length;
18511         }
18512         
18513         this.showPanel(this.tabs[i-1]);
18514     },
18515     
18516     
18517     addBullet: function()
18518     {
18519         if(!this.bullets || Roo.isTouch){
18520             return;
18521         }
18522         var ctr = this.el.select('.carousel-bullets',true).first();
18523         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18524         var bullet = ctr.createChild({
18525             cls : 'bullet bullet-' + i
18526         },ctr.dom.lastChild);
18527         
18528         
18529         var _this = this;
18530         
18531         bullet.on('click', (function(e, el, o, ii, t){
18532
18533             e.preventDefault();
18534
18535             this.showPanel(ii);
18536
18537             if(this.autoslide && this.slideFn){
18538                 clearInterval(this.slideFn);
18539                 this.slideFn = window.setInterval(function() {
18540                     _this.showPanelNext();
18541                 }, this.timer);
18542             }
18543
18544         }).createDelegate(this, [i, bullet], true));
18545                 
18546         
18547     },
18548      
18549     setActiveBullet : function(i)
18550     {
18551         if(Roo.isTouch){
18552             return;
18553         }
18554         
18555         Roo.each(this.el.select('.bullet', true).elements, function(el){
18556             el.removeClass('selected');
18557         });
18558
18559         var bullet = this.el.select('.bullet-' + i, true).first();
18560         
18561         if(!bullet){
18562             return;
18563         }
18564         
18565         bullet.addClass('selected');
18566     }
18567     
18568     
18569   
18570 });
18571
18572  
18573
18574  
18575  
18576 Roo.apply(Roo.bootstrap.TabGroup, {
18577     
18578     groups: {},
18579      /**
18580     * register a Navigation Group
18581     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18582     */
18583     register : function(navgrp)
18584     {
18585         this.groups[navgrp.navId] = navgrp;
18586         
18587     },
18588     /**
18589     * fetch a Navigation Group based on the navigation ID
18590     * if one does not exist , it will get created.
18591     * @param {string} the navgroup to add
18592     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18593     */
18594     get: function(navId) {
18595         if (typeof(this.groups[navId]) == 'undefined') {
18596             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18597         }
18598         return this.groups[navId] ;
18599     }
18600     
18601     
18602     
18603 });
18604
18605  /*
18606  * - LGPL
18607  *
18608  * TabPanel
18609  * 
18610  */
18611
18612 /**
18613  * @class Roo.bootstrap.TabPanel
18614  * @extends Roo.bootstrap.Component
18615  * Bootstrap TabPanel class
18616  * @cfg {Boolean} active panel active
18617  * @cfg {String} html panel content
18618  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18619  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18620  * @cfg {String} href click to link..
18621  * 
18622  * 
18623  * @constructor
18624  * Create a new TabPanel
18625  * @param {Object} config The config object
18626  */
18627
18628 Roo.bootstrap.TabPanel = function(config){
18629     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18630     this.addEvents({
18631         /**
18632              * @event changed
18633              * Fires when the active status changes
18634              * @param {Roo.bootstrap.TabPanel} this
18635              * @param {Boolean} state the new state
18636             
18637          */
18638         'changed': true,
18639         /**
18640              * @event beforedeactivate
18641              * Fires before a tab is de-activated - can be used to do validation on a form.
18642              * @param {Roo.bootstrap.TabPanel} this
18643              * @return {Boolean} false if there is an error
18644             
18645          */
18646         'beforedeactivate': true
18647      });
18648     
18649     this.tabId = this.tabId || Roo.id();
18650   
18651 };
18652
18653 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18654     
18655     active: false,
18656     html: false,
18657     tabId: false,
18658     navId : false,
18659     href : '',
18660     
18661     getAutoCreate : function(){
18662         
18663         
18664         var cfg = {
18665             tag: 'div',
18666             // item is needed for carousel - not sure if it has any effect otherwise
18667             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18668             html: this.html || ''
18669         };
18670         
18671         if(this.active){
18672             cfg.cls += ' active';
18673         }
18674         
18675         if(this.tabId){
18676             cfg.tabId = this.tabId;
18677         }
18678         
18679         
18680         
18681         return cfg;
18682     },
18683     
18684     initEvents:  function()
18685     {
18686         var p = this.parent();
18687         
18688         this.navId = this.navId || p.navId;
18689         
18690         if (typeof(this.navId) != 'undefined') {
18691             // not really needed.. but just in case.. parent should be a NavGroup.
18692             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18693             
18694             tg.register(this);
18695             
18696             var i = tg.tabs.length - 1;
18697             
18698             if(this.active && tg.bullets > 0 && i < tg.bullets){
18699                 tg.setActiveBullet(i);
18700             }
18701         }
18702         
18703         this.el.on('click', this.onClick, this);
18704         
18705         if(Roo.isTouch){
18706             this.el.on("touchstart", this.onTouchStart, this);
18707             this.el.on("touchmove", this.onTouchMove, this);
18708             this.el.on("touchend", this.onTouchEnd, this);
18709         }
18710         
18711     },
18712     
18713     onRender : function(ct, position)
18714     {
18715         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18716     },
18717     
18718     setActive : function(state)
18719     {
18720         Roo.log("panel - set active " + this.tabId + "=" + state);
18721         
18722         this.active = state;
18723         if (!state) {
18724             this.el.removeClass('active');
18725             
18726         } else  if (!this.el.hasClass('active')) {
18727             this.el.addClass('active');
18728         }
18729         
18730         this.fireEvent('changed', this, state);
18731     },
18732     
18733     onClick : function(e)
18734     {
18735         e.preventDefault();
18736         
18737         if(!this.href.length){
18738             return;
18739         }
18740         
18741         window.location.href = this.href;
18742     },
18743     
18744     startX : 0,
18745     startY : 0,
18746     endX : 0,
18747     endY : 0,
18748     swiping : false,
18749     
18750     onTouchStart : function(e)
18751     {
18752         this.swiping = false;
18753         
18754         this.startX = e.browserEvent.touches[0].clientX;
18755         this.startY = e.browserEvent.touches[0].clientY;
18756     },
18757     
18758     onTouchMove : function(e)
18759     {
18760         this.swiping = true;
18761         
18762         this.endX = e.browserEvent.touches[0].clientX;
18763         this.endY = e.browserEvent.touches[0].clientY;
18764     },
18765     
18766     onTouchEnd : function(e)
18767     {
18768         if(!this.swiping){
18769             this.onClick(e);
18770             return;
18771         }
18772         
18773         var tabGroup = this.parent();
18774         
18775         if(this.endX > this.startX){ // swiping right
18776             tabGroup.showPanelPrev();
18777             return;
18778         }
18779         
18780         if(this.startX > this.endX){ // swiping left
18781             tabGroup.showPanelNext();
18782             return;
18783         }
18784     }
18785     
18786     
18787 });
18788  
18789
18790  
18791
18792  /*
18793  * - LGPL
18794  *
18795  * DateField
18796  * 
18797  */
18798
18799 /**
18800  * @class Roo.bootstrap.DateField
18801  * @extends Roo.bootstrap.Input
18802  * Bootstrap DateField class
18803  * @cfg {Number} weekStart default 0
18804  * @cfg {String} viewMode default empty, (months|years)
18805  * @cfg {String} minViewMode default empty, (months|years)
18806  * @cfg {Number} startDate default -Infinity
18807  * @cfg {Number} endDate default Infinity
18808  * @cfg {Boolean} todayHighlight default false
18809  * @cfg {Boolean} todayBtn default false
18810  * @cfg {Boolean} calendarWeeks default false
18811  * @cfg {Object} daysOfWeekDisabled default empty
18812  * @cfg {Boolean} singleMode default false (true | false)
18813  * 
18814  * @cfg {Boolean} keyboardNavigation default true
18815  * @cfg {String} language default en
18816  * 
18817  * @constructor
18818  * Create a new DateField
18819  * @param {Object} config The config object
18820  */
18821
18822 Roo.bootstrap.DateField = function(config){
18823     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18824      this.addEvents({
18825             /**
18826              * @event show
18827              * Fires when this field show.
18828              * @param {Roo.bootstrap.DateField} this
18829              * @param {Mixed} date The date value
18830              */
18831             show : true,
18832             /**
18833              * @event show
18834              * Fires when this field hide.
18835              * @param {Roo.bootstrap.DateField} this
18836              * @param {Mixed} date The date value
18837              */
18838             hide : true,
18839             /**
18840              * @event select
18841              * Fires when select a date.
18842              * @param {Roo.bootstrap.DateField} this
18843              * @param {Mixed} date The date value
18844              */
18845             select : true,
18846             /**
18847              * @event beforeselect
18848              * Fires when before select a date.
18849              * @param {Roo.bootstrap.DateField} this
18850              * @param {Mixed} date The date value
18851              */
18852             beforeselect : true
18853         });
18854 };
18855
18856 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18857     
18858     /**
18859      * @cfg {String} format
18860      * The default date format string which can be overriden for localization support.  The format must be
18861      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18862      */
18863     format : "m/d/y",
18864     /**
18865      * @cfg {String} altFormats
18866      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18867      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18868      */
18869     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18870     
18871     weekStart : 0,
18872     
18873     viewMode : '',
18874     
18875     minViewMode : '',
18876     
18877     todayHighlight : false,
18878     
18879     todayBtn: false,
18880     
18881     language: 'en',
18882     
18883     keyboardNavigation: true,
18884     
18885     calendarWeeks: false,
18886     
18887     startDate: -Infinity,
18888     
18889     endDate: Infinity,
18890     
18891     daysOfWeekDisabled: [],
18892     
18893     _events: [],
18894     
18895     singleMode : false,
18896     
18897     UTCDate: function()
18898     {
18899         return new Date(Date.UTC.apply(Date, arguments));
18900     },
18901     
18902     UTCToday: function()
18903     {
18904         var today = new Date();
18905         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18906     },
18907     
18908     getDate: function() {
18909             var d = this.getUTCDate();
18910             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18911     },
18912     
18913     getUTCDate: function() {
18914             return this.date;
18915     },
18916     
18917     setDate: function(d) {
18918             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18919     },
18920     
18921     setUTCDate: function(d) {
18922             this.date = d;
18923             this.setValue(this.formatDate(this.date));
18924     },
18925         
18926     onRender: function(ct, position)
18927     {
18928         
18929         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18930         
18931         this.language = this.language || 'en';
18932         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18933         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18934         
18935         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18936         this.format = this.format || 'm/d/y';
18937         this.isInline = false;
18938         this.isInput = true;
18939         this.component = this.el.select('.add-on', true).first() || false;
18940         this.component = (this.component && this.component.length === 0) ? false : this.component;
18941         this.hasInput = this.component && this.inputEl().length;
18942         
18943         if (typeof(this.minViewMode === 'string')) {
18944             switch (this.minViewMode) {
18945                 case 'months':
18946                     this.minViewMode = 1;
18947                     break;
18948                 case 'years':
18949                     this.minViewMode = 2;
18950                     break;
18951                 default:
18952                     this.minViewMode = 0;
18953                     break;
18954             }
18955         }
18956         
18957         if (typeof(this.viewMode === 'string')) {
18958             switch (this.viewMode) {
18959                 case 'months':
18960                     this.viewMode = 1;
18961                     break;
18962                 case 'years':
18963                     this.viewMode = 2;
18964                     break;
18965                 default:
18966                     this.viewMode = 0;
18967                     break;
18968             }
18969         }
18970                 
18971         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18972         
18973 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18974         
18975         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18976         
18977         this.picker().on('mousedown', this.onMousedown, this);
18978         this.picker().on('click', this.onClick, this);
18979         
18980         this.picker().addClass('datepicker-dropdown');
18981         
18982         this.startViewMode = this.viewMode;
18983         
18984         if(this.singleMode){
18985             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18986                 v.setVisibilityMode(Roo.Element.DISPLAY);
18987                 v.hide();
18988             });
18989             
18990             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18991                 v.setStyle('width', '189px');
18992             });
18993         }
18994         
18995         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18996             if(!this.calendarWeeks){
18997                 v.remove();
18998                 return;
18999             }
19000             
19001             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19002             v.attr('colspan', function(i, val){
19003                 return parseInt(val) + 1;
19004             });
19005         });
19006                         
19007         
19008         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
19009         
19010         this.setStartDate(this.startDate);
19011         this.setEndDate(this.endDate);
19012         
19013         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
19014         
19015         this.fillDow();
19016         this.fillMonths();
19017         this.update();
19018         this.showMode();
19019         
19020         if(this.isInline) {
19021             this.showPopup();
19022         }
19023     },
19024     
19025     picker : function()
19026     {
19027         return this.pickerEl;
19028 //        return this.el.select('.datepicker', true).first();
19029     },
19030     
19031     fillDow: function()
19032     {
19033         var dowCnt = this.weekStart;
19034         
19035         var dow = {
19036             tag: 'tr',
19037             cn: [
19038                 
19039             ]
19040         };
19041         
19042         if(this.calendarWeeks){
19043             dow.cn.push({
19044                 tag: 'th',
19045                 cls: 'cw',
19046                 html: '&nbsp;'
19047             })
19048         }
19049         
19050         while (dowCnt < this.weekStart + 7) {
19051             dow.cn.push({
19052                 tag: 'th',
19053                 cls: 'dow',
19054                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
19055             });
19056         }
19057         
19058         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
19059     },
19060     
19061     fillMonths: function()
19062     {    
19063         var i = 0;
19064         var months = this.picker().select('>.datepicker-months td', true).first();
19065         
19066         months.dom.innerHTML = '';
19067         
19068         while (i < 12) {
19069             var month = {
19070                 tag: 'span',
19071                 cls: 'month',
19072                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
19073             };
19074             
19075             months.createChild(month);
19076         }
19077         
19078     },
19079     
19080     update: function()
19081     {
19082         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;
19083         
19084         if (this.date < this.startDate) {
19085             this.viewDate = new Date(this.startDate);
19086         } else if (this.date > this.endDate) {
19087             this.viewDate = new Date(this.endDate);
19088         } else {
19089             this.viewDate = new Date(this.date);
19090         }
19091         
19092         this.fill();
19093     },
19094     
19095     fill: function() 
19096     {
19097         var d = new Date(this.viewDate),
19098                 year = d.getUTCFullYear(),
19099                 month = d.getUTCMonth(),
19100                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
19101                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
19102                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
19103                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
19104                 currentDate = this.date && this.date.valueOf(),
19105                 today = this.UTCToday();
19106         
19107         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
19108         
19109 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19110         
19111 //        this.picker.select('>tfoot th.today').
19112 //                                              .text(dates[this.language].today)
19113 //                                              .toggle(this.todayBtn !== false);
19114     
19115         this.updateNavArrows();
19116         this.fillMonths();
19117                                                 
19118         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
19119         
19120         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
19121          
19122         prevMonth.setUTCDate(day);
19123         
19124         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
19125         
19126         var nextMonth = new Date(prevMonth);
19127         
19128         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
19129         
19130         nextMonth = nextMonth.valueOf();
19131         
19132         var fillMonths = false;
19133         
19134         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
19135         
19136         while(prevMonth.valueOf() <= nextMonth) {
19137             var clsName = '';
19138             
19139             if (prevMonth.getUTCDay() === this.weekStart) {
19140                 if(fillMonths){
19141                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
19142                 }
19143                     
19144                 fillMonths = {
19145                     tag: 'tr',
19146                     cn: []
19147                 };
19148                 
19149                 if(this.calendarWeeks){
19150                     // ISO 8601: First week contains first thursday.
19151                     // ISO also states week starts on Monday, but we can be more abstract here.
19152                     var
19153                     // Start of current week: based on weekstart/current date
19154                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
19155                     // Thursday of this week
19156                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
19157                     // First Thursday of year, year from thursday
19158                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
19159                     // Calendar week: ms between thursdays, div ms per day, div 7 days
19160                     calWeek =  (th - yth) / 864e5 / 7 + 1;
19161                     
19162                     fillMonths.cn.push({
19163                         tag: 'td',
19164                         cls: 'cw',
19165                         html: calWeek
19166                     });
19167                 }
19168             }
19169             
19170             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
19171                 clsName += ' old';
19172             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19173                 clsName += ' new';
19174             }
19175             if (this.todayHighlight &&
19176                 prevMonth.getUTCFullYear() == today.getFullYear() &&
19177                 prevMonth.getUTCMonth() == today.getMonth() &&
19178                 prevMonth.getUTCDate() == today.getDate()) {
19179                 clsName += ' today';
19180             }
19181             
19182             if (currentDate && prevMonth.valueOf() === currentDate) {
19183                 clsName += ' active';
19184             }
19185             
19186             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19187                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19188                     clsName += ' disabled';
19189             }
19190             
19191             fillMonths.cn.push({
19192                 tag: 'td',
19193                 cls: 'day ' + clsName,
19194                 html: prevMonth.getDate()
19195             });
19196             
19197             prevMonth.setDate(prevMonth.getDate()+1);
19198         }
19199           
19200         var currentYear = this.date && this.date.getUTCFullYear();
19201         var currentMonth = this.date && this.date.getUTCMonth();
19202         
19203         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19204         
19205         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19206             v.removeClass('active');
19207             
19208             if(currentYear === year && k === currentMonth){
19209                 v.addClass('active');
19210             }
19211             
19212             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19213                 v.addClass('disabled');
19214             }
19215             
19216         });
19217         
19218         
19219         year = parseInt(year/10, 10) * 10;
19220         
19221         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19222         
19223         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19224         
19225         year -= 1;
19226         for (var i = -1; i < 11; i++) {
19227             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19228                 tag: 'span',
19229                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19230                 html: year
19231             });
19232             
19233             year += 1;
19234         }
19235     },
19236     
19237     showMode: function(dir) 
19238     {
19239         if (dir) {
19240             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19241         }
19242         
19243         Roo.each(this.picker().select('>div',true).elements, function(v){
19244             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19245             v.hide();
19246         });
19247         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19248     },
19249     
19250     place: function()
19251     {
19252         if(this.isInline) {
19253             return;
19254         }
19255         
19256         this.picker().removeClass(['bottom', 'top']);
19257         
19258         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19259             /*
19260              * place to the top of element!
19261              *
19262              */
19263             
19264             this.picker().addClass('top');
19265             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19266             
19267             return;
19268         }
19269         
19270         this.picker().addClass('bottom');
19271         
19272         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19273     },
19274     
19275     parseDate : function(value)
19276     {
19277         if(!value || value instanceof Date){
19278             return value;
19279         }
19280         var v = Date.parseDate(value, this.format);
19281         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19282             v = Date.parseDate(value, 'Y-m-d');
19283         }
19284         if(!v && this.altFormats){
19285             if(!this.altFormatsArray){
19286                 this.altFormatsArray = this.altFormats.split("|");
19287             }
19288             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19289                 v = Date.parseDate(value, this.altFormatsArray[i]);
19290             }
19291         }
19292         return v;
19293     },
19294     
19295     formatDate : function(date, fmt)
19296     {   
19297         return (!date || !(date instanceof Date)) ?
19298         date : date.dateFormat(fmt || this.format);
19299     },
19300     
19301     onFocus : function()
19302     {
19303         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19304         this.showPopup();
19305     },
19306     
19307     onBlur : function()
19308     {
19309         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19310         
19311         var d = this.inputEl().getValue();
19312         
19313         this.setValue(d);
19314                 
19315         this.hidePopup();
19316     },
19317     
19318     showPopup : function()
19319     {
19320         this.picker().show();
19321         this.update();
19322         this.place();
19323         
19324         this.fireEvent('showpopup', this, this.date);
19325     },
19326     
19327     hidePopup : function()
19328     {
19329         if(this.isInline) {
19330             return;
19331         }
19332         this.picker().hide();
19333         this.viewMode = this.startViewMode;
19334         this.showMode();
19335         
19336         this.fireEvent('hidepopup', this, this.date);
19337         
19338     },
19339     
19340     onMousedown: function(e)
19341     {
19342         e.stopPropagation();
19343         e.preventDefault();
19344     },
19345     
19346     keyup: function(e)
19347     {
19348         Roo.bootstrap.DateField.superclass.keyup.call(this);
19349         this.update();
19350     },
19351
19352     setValue: function(v)
19353     {
19354         if(this.fireEvent('beforeselect', this, v) !== false){
19355             var d = new Date(this.parseDate(v) ).clearTime();
19356         
19357             if(isNaN(d.getTime())){
19358                 this.date = this.viewDate = '';
19359                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19360                 return;
19361             }
19362
19363             v = this.formatDate(d);
19364
19365             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19366
19367             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19368
19369             this.update();
19370
19371             this.fireEvent('select', this, this.date);
19372         }
19373     },
19374     
19375     getValue: function()
19376     {
19377         return this.formatDate(this.date);
19378     },
19379     
19380     fireKey: function(e)
19381     {
19382         if (!this.picker().isVisible()){
19383             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19384                 this.showPopup();
19385             }
19386             return;
19387         }
19388         
19389         var dateChanged = false,
19390         dir, day, month,
19391         newDate, newViewDate;
19392         
19393         switch(e.keyCode){
19394             case 27: // escape
19395                 this.hidePopup();
19396                 e.preventDefault();
19397                 break;
19398             case 37: // left
19399             case 39: // right
19400                 if (!this.keyboardNavigation) {
19401                     break;
19402                 }
19403                 dir = e.keyCode == 37 ? -1 : 1;
19404                 
19405                 if (e.ctrlKey){
19406                     newDate = this.moveYear(this.date, dir);
19407                     newViewDate = this.moveYear(this.viewDate, dir);
19408                 } else if (e.shiftKey){
19409                     newDate = this.moveMonth(this.date, dir);
19410                     newViewDate = this.moveMonth(this.viewDate, dir);
19411                 } else {
19412                     newDate = new Date(this.date);
19413                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19414                     newViewDate = new Date(this.viewDate);
19415                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19416                 }
19417                 if (this.dateWithinRange(newDate)){
19418                     this.date = newDate;
19419                     this.viewDate = newViewDate;
19420                     this.setValue(this.formatDate(this.date));
19421 //                    this.update();
19422                     e.preventDefault();
19423                     dateChanged = true;
19424                 }
19425                 break;
19426             case 38: // up
19427             case 40: // down
19428                 if (!this.keyboardNavigation) {
19429                     break;
19430                 }
19431                 dir = e.keyCode == 38 ? -1 : 1;
19432                 if (e.ctrlKey){
19433                     newDate = this.moveYear(this.date, dir);
19434                     newViewDate = this.moveYear(this.viewDate, dir);
19435                 } else if (e.shiftKey){
19436                     newDate = this.moveMonth(this.date, dir);
19437                     newViewDate = this.moveMonth(this.viewDate, dir);
19438                 } else {
19439                     newDate = new Date(this.date);
19440                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19441                     newViewDate = new Date(this.viewDate);
19442                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19443                 }
19444                 if (this.dateWithinRange(newDate)){
19445                     this.date = newDate;
19446                     this.viewDate = newViewDate;
19447                     this.setValue(this.formatDate(this.date));
19448 //                    this.update();
19449                     e.preventDefault();
19450                     dateChanged = true;
19451                 }
19452                 break;
19453             case 13: // enter
19454                 this.setValue(this.formatDate(this.date));
19455                 this.hidePopup();
19456                 e.preventDefault();
19457                 break;
19458             case 9: // tab
19459                 this.setValue(this.formatDate(this.date));
19460                 this.hidePopup();
19461                 break;
19462             case 16: // shift
19463             case 17: // ctrl
19464             case 18: // alt
19465                 break;
19466             default :
19467                 this.hidePopup();
19468                 
19469         }
19470     },
19471     
19472     
19473     onClick: function(e) 
19474     {
19475         e.stopPropagation();
19476         e.preventDefault();
19477         
19478         var target = e.getTarget();
19479         
19480         if(target.nodeName.toLowerCase() === 'i'){
19481             target = Roo.get(target).dom.parentNode;
19482         }
19483         
19484         var nodeName = target.nodeName;
19485         var className = target.className;
19486         var html = target.innerHTML;
19487         //Roo.log(nodeName);
19488         
19489         switch(nodeName.toLowerCase()) {
19490             case 'th':
19491                 switch(className) {
19492                     case 'switch':
19493                         this.showMode(1);
19494                         break;
19495                     case 'prev':
19496                     case 'next':
19497                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19498                         switch(this.viewMode){
19499                                 case 0:
19500                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19501                                         break;
19502                                 case 1:
19503                                 case 2:
19504                                         this.viewDate = this.moveYear(this.viewDate, dir);
19505                                         break;
19506                         }
19507                         this.fill();
19508                         break;
19509                     case 'today':
19510                         var date = new Date();
19511                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19512 //                        this.fill()
19513                         this.setValue(this.formatDate(this.date));
19514                         
19515                         this.hidePopup();
19516                         break;
19517                 }
19518                 break;
19519             case 'span':
19520                 if (className.indexOf('disabled') < 0) {
19521                     this.viewDate.setUTCDate(1);
19522                     if (className.indexOf('month') > -1) {
19523                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19524                     } else {
19525                         var year = parseInt(html, 10) || 0;
19526                         this.viewDate.setUTCFullYear(year);
19527                         
19528                     }
19529                     
19530                     if(this.singleMode){
19531                         this.setValue(this.formatDate(this.viewDate));
19532                         this.hidePopup();
19533                         return;
19534                     }
19535                     
19536                     this.showMode(-1);
19537                     this.fill();
19538                 }
19539                 break;
19540                 
19541             case 'td':
19542                 //Roo.log(className);
19543                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19544                     var day = parseInt(html, 10) || 1;
19545                     var year = this.viewDate.getUTCFullYear(),
19546                         month = this.viewDate.getUTCMonth();
19547
19548                     if (className.indexOf('old') > -1) {
19549                         if(month === 0 ){
19550                             month = 11;
19551                             year -= 1;
19552                         }else{
19553                             month -= 1;
19554                         }
19555                     } else if (className.indexOf('new') > -1) {
19556                         if (month == 11) {
19557                             month = 0;
19558                             year += 1;
19559                         } else {
19560                             month += 1;
19561                         }
19562                     }
19563                     //Roo.log([year,month,day]);
19564                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19565                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19566 //                    this.fill();
19567                     //Roo.log(this.formatDate(this.date));
19568                     this.setValue(this.formatDate(this.date));
19569                     this.hidePopup();
19570                 }
19571                 break;
19572         }
19573     },
19574     
19575     setStartDate: function(startDate)
19576     {
19577         this.startDate = startDate || -Infinity;
19578         if (this.startDate !== -Infinity) {
19579             this.startDate = this.parseDate(this.startDate);
19580         }
19581         this.update();
19582         this.updateNavArrows();
19583     },
19584
19585     setEndDate: function(endDate)
19586     {
19587         this.endDate = endDate || Infinity;
19588         if (this.endDate !== Infinity) {
19589             this.endDate = this.parseDate(this.endDate);
19590         }
19591         this.update();
19592         this.updateNavArrows();
19593     },
19594     
19595     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19596     {
19597         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19598         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19599             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19600         }
19601         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19602             return parseInt(d, 10);
19603         });
19604         this.update();
19605         this.updateNavArrows();
19606     },
19607     
19608     updateNavArrows: function() 
19609     {
19610         if(this.singleMode){
19611             return;
19612         }
19613         
19614         var d = new Date(this.viewDate),
19615         year = d.getUTCFullYear(),
19616         month = d.getUTCMonth();
19617         
19618         Roo.each(this.picker().select('.prev', true).elements, function(v){
19619             v.show();
19620             switch (this.viewMode) {
19621                 case 0:
19622
19623                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19624                         v.hide();
19625                     }
19626                     break;
19627                 case 1:
19628                 case 2:
19629                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19630                         v.hide();
19631                     }
19632                     break;
19633             }
19634         });
19635         
19636         Roo.each(this.picker().select('.next', true).elements, function(v){
19637             v.show();
19638             switch (this.viewMode) {
19639                 case 0:
19640
19641                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19642                         v.hide();
19643                     }
19644                     break;
19645                 case 1:
19646                 case 2:
19647                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19648                         v.hide();
19649                     }
19650                     break;
19651             }
19652         })
19653     },
19654     
19655     moveMonth: function(date, dir)
19656     {
19657         if (!dir) {
19658             return date;
19659         }
19660         var new_date = new Date(date.valueOf()),
19661         day = new_date.getUTCDate(),
19662         month = new_date.getUTCMonth(),
19663         mag = Math.abs(dir),
19664         new_month, test;
19665         dir = dir > 0 ? 1 : -1;
19666         if (mag == 1){
19667             test = dir == -1
19668             // If going back one month, make sure month is not current month
19669             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19670             ? function(){
19671                 return new_date.getUTCMonth() == month;
19672             }
19673             // If going forward one month, make sure month is as expected
19674             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19675             : function(){
19676                 return new_date.getUTCMonth() != new_month;
19677             };
19678             new_month = month + dir;
19679             new_date.setUTCMonth(new_month);
19680             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19681             if (new_month < 0 || new_month > 11) {
19682                 new_month = (new_month + 12) % 12;
19683             }
19684         } else {
19685             // For magnitudes >1, move one month at a time...
19686             for (var i=0; i<mag; i++) {
19687                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19688                 new_date = this.moveMonth(new_date, dir);
19689             }
19690             // ...then reset the day, keeping it in the new month
19691             new_month = new_date.getUTCMonth();
19692             new_date.setUTCDate(day);
19693             test = function(){
19694                 return new_month != new_date.getUTCMonth();
19695             };
19696         }
19697         // Common date-resetting loop -- if date is beyond end of month, make it
19698         // end of month
19699         while (test()){
19700             new_date.setUTCDate(--day);
19701             new_date.setUTCMonth(new_month);
19702         }
19703         return new_date;
19704     },
19705
19706     moveYear: function(date, dir)
19707     {
19708         return this.moveMonth(date, dir*12);
19709     },
19710
19711     dateWithinRange: function(date)
19712     {
19713         return date >= this.startDate && date <= this.endDate;
19714     },
19715
19716     
19717     remove: function() 
19718     {
19719         this.picker().remove();
19720     },
19721     
19722     validateValue : function(value)
19723     {
19724         if(this.getVisibilityEl().hasClass('hidden')){
19725             return true;
19726         }
19727         
19728         if(value.length < 1)  {
19729             if(this.allowBlank){
19730                 return true;
19731             }
19732             return false;
19733         }
19734         
19735         if(value.length < this.minLength){
19736             return false;
19737         }
19738         if(value.length > this.maxLength){
19739             return false;
19740         }
19741         if(this.vtype){
19742             var vt = Roo.form.VTypes;
19743             if(!vt[this.vtype](value, this)){
19744                 return false;
19745             }
19746         }
19747         if(typeof this.validator == "function"){
19748             var msg = this.validator(value);
19749             if(msg !== true){
19750                 return false;
19751             }
19752         }
19753         
19754         if(this.regex && !this.regex.test(value)){
19755             return false;
19756         }
19757         
19758         if(typeof(this.parseDate(value)) == 'undefined'){
19759             return false;
19760         }
19761         
19762         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19763             return false;
19764         }      
19765         
19766         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19767             return false;
19768         } 
19769         
19770         
19771         return true;
19772     },
19773     
19774     reset : function()
19775     {
19776         this.date = this.viewDate = '';
19777         
19778         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19779     }
19780    
19781 });
19782
19783 Roo.apply(Roo.bootstrap.DateField,  {
19784     
19785     head : {
19786         tag: 'thead',
19787         cn: [
19788         {
19789             tag: 'tr',
19790             cn: [
19791             {
19792                 tag: 'th',
19793                 cls: 'prev',
19794                 html: '<i class="fa fa-arrow-left"/>'
19795             },
19796             {
19797                 tag: 'th',
19798                 cls: 'switch',
19799                 colspan: '5'
19800             },
19801             {
19802                 tag: 'th',
19803                 cls: 'next',
19804                 html: '<i class="fa fa-arrow-right"/>'
19805             }
19806
19807             ]
19808         }
19809         ]
19810     },
19811     
19812     content : {
19813         tag: 'tbody',
19814         cn: [
19815         {
19816             tag: 'tr',
19817             cn: [
19818             {
19819                 tag: 'td',
19820                 colspan: '7'
19821             }
19822             ]
19823         }
19824         ]
19825     },
19826     
19827     footer : {
19828         tag: 'tfoot',
19829         cn: [
19830         {
19831             tag: 'tr',
19832             cn: [
19833             {
19834                 tag: 'th',
19835                 colspan: '7',
19836                 cls: 'today'
19837             }
19838                     
19839             ]
19840         }
19841         ]
19842     },
19843     
19844     dates:{
19845         en: {
19846             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19847             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19848             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19849             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19850             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19851             today: "Today"
19852         }
19853     },
19854     
19855     modes: [
19856     {
19857         clsName: 'days',
19858         navFnc: 'Month',
19859         navStep: 1
19860     },
19861     {
19862         clsName: 'months',
19863         navFnc: 'FullYear',
19864         navStep: 1
19865     },
19866     {
19867         clsName: 'years',
19868         navFnc: 'FullYear',
19869         navStep: 10
19870     }]
19871 });
19872
19873 Roo.apply(Roo.bootstrap.DateField,  {
19874   
19875     template : {
19876         tag: 'div',
19877         cls: 'datepicker dropdown-menu roo-dynamic',
19878         cn: [
19879         {
19880             tag: 'div',
19881             cls: 'datepicker-days',
19882             cn: [
19883             {
19884                 tag: 'table',
19885                 cls: 'table-condensed',
19886                 cn:[
19887                 Roo.bootstrap.DateField.head,
19888                 {
19889                     tag: 'tbody'
19890                 },
19891                 Roo.bootstrap.DateField.footer
19892                 ]
19893             }
19894             ]
19895         },
19896         {
19897             tag: 'div',
19898             cls: 'datepicker-months',
19899             cn: [
19900             {
19901                 tag: 'table',
19902                 cls: 'table-condensed',
19903                 cn:[
19904                 Roo.bootstrap.DateField.head,
19905                 Roo.bootstrap.DateField.content,
19906                 Roo.bootstrap.DateField.footer
19907                 ]
19908             }
19909             ]
19910         },
19911         {
19912             tag: 'div',
19913             cls: 'datepicker-years',
19914             cn: [
19915             {
19916                 tag: 'table',
19917                 cls: 'table-condensed',
19918                 cn:[
19919                 Roo.bootstrap.DateField.head,
19920                 Roo.bootstrap.DateField.content,
19921                 Roo.bootstrap.DateField.footer
19922                 ]
19923             }
19924             ]
19925         }
19926         ]
19927     }
19928 });
19929
19930  
19931
19932  /*
19933  * - LGPL
19934  *
19935  * TimeField
19936  * 
19937  */
19938
19939 /**
19940  * @class Roo.bootstrap.TimeField
19941  * @extends Roo.bootstrap.Input
19942  * Bootstrap DateField class
19943  * 
19944  * 
19945  * @constructor
19946  * Create a new TimeField
19947  * @param {Object} config The config object
19948  */
19949
19950 Roo.bootstrap.TimeField = function(config){
19951     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19952     this.addEvents({
19953             /**
19954              * @event show
19955              * Fires when this field show.
19956              * @param {Roo.bootstrap.DateField} thisthis
19957              * @param {Mixed} date The date value
19958              */
19959             show : true,
19960             /**
19961              * @event show
19962              * Fires when this field hide.
19963              * @param {Roo.bootstrap.DateField} this
19964              * @param {Mixed} date The date value
19965              */
19966             hide : true,
19967             /**
19968              * @event select
19969              * Fires when select a date.
19970              * @param {Roo.bootstrap.DateField} this
19971              * @param {Mixed} date The date value
19972              */
19973             select : true
19974         });
19975 };
19976
19977 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19978     
19979     /**
19980      * @cfg {String} format
19981      * The default time format string which can be overriden for localization support.  The format must be
19982      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19983      */
19984     format : "H:i",
19985        
19986     onRender: function(ct, position)
19987     {
19988         
19989         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19990                 
19991         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19992         
19993         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19994         
19995         this.pop = this.picker().select('>.datepicker-time',true).first();
19996         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19997         
19998         this.picker().on('mousedown', this.onMousedown, this);
19999         this.picker().on('click', this.onClick, this);
20000         
20001         this.picker().addClass('datepicker-dropdown');
20002     
20003         this.fillTime();
20004         this.update();
20005             
20006         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
20007         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
20008         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
20009         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
20010         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
20011         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
20012
20013     },
20014     
20015     fireKey: function(e){
20016         if (!this.picker().isVisible()){
20017             if (e.keyCode == 27) { // allow escape to hide and re-show picker
20018                 this.show();
20019             }
20020             return;
20021         }
20022
20023         e.preventDefault();
20024         
20025         switch(e.keyCode){
20026             case 27: // escape
20027                 this.hide();
20028                 break;
20029             case 37: // left
20030             case 39: // right
20031                 this.onTogglePeriod();
20032                 break;
20033             case 38: // up
20034                 this.onIncrementMinutes();
20035                 break;
20036             case 40: // down
20037                 this.onDecrementMinutes();
20038                 break;
20039             case 13: // enter
20040             case 9: // tab
20041                 this.setTime();
20042                 break;
20043         }
20044     },
20045     
20046     onClick: function(e) {
20047         e.stopPropagation();
20048         e.preventDefault();
20049     },
20050     
20051     picker : function()
20052     {
20053         return this.el.select('.datepicker', true).first();
20054     },
20055     
20056     fillTime: function()
20057     {    
20058         var time = this.pop.select('tbody', true).first();
20059         
20060         time.dom.innerHTML = '';
20061         
20062         time.createChild({
20063             tag: 'tr',
20064             cn: [
20065                 {
20066                     tag: 'td',
20067                     cn: [
20068                         {
20069                             tag: 'a',
20070                             href: '#',
20071                             cls: 'btn',
20072                             cn: [
20073                                 {
20074                                     tag: 'span',
20075                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
20076                                 }
20077                             ]
20078                         } 
20079                     ]
20080                 },
20081                 {
20082                     tag: 'td',
20083                     cls: 'separator'
20084                 },
20085                 {
20086                     tag: 'td',
20087                     cn: [
20088                         {
20089                             tag: 'a',
20090                             href: '#',
20091                             cls: 'btn',
20092                             cn: [
20093                                 {
20094                                     tag: 'span',
20095                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
20096                                 }
20097                             ]
20098                         }
20099                     ]
20100                 },
20101                 {
20102                     tag: 'td',
20103                     cls: 'separator'
20104                 }
20105             ]
20106         });
20107         
20108         time.createChild({
20109             tag: 'tr',
20110             cn: [
20111                 {
20112                     tag: 'td',
20113                     cn: [
20114                         {
20115                             tag: 'span',
20116                             cls: 'timepicker-hour',
20117                             html: '00'
20118                         }  
20119                     ]
20120                 },
20121                 {
20122                     tag: 'td',
20123                     cls: 'separator',
20124                     html: ':'
20125                 },
20126                 {
20127                     tag: 'td',
20128                     cn: [
20129                         {
20130                             tag: 'span',
20131                             cls: 'timepicker-minute',
20132                             html: '00'
20133                         }  
20134                     ]
20135                 },
20136                 {
20137                     tag: 'td',
20138                     cls: 'separator'
20139                 },
20140                 {
20141                     tag: 'td',
20142                     cn: [
20143                         {
20144                             tag: 'button',
20145                             type: 'button',
20146                             cls: 'btn btn-primary period',
20147                             html: 'AM'
20148                             
20149                         }
20150                     ]
20151                 }
20152             ]
20153         });
20154         
20155         time.createChild({
20156             tag: 'tr',
20157             cn: [
20158                 {
20159                     tag: 'td',
20160                     cn: [
20161                         {
20162                             tag: 'a',
20163                             href: '#',
20164                             cls: 'btn',
20165                             cn: [
20166                                 {
20167                                     tag: 'span',
20168                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
20169                                 }
20170                             ]
20171                         }
20172                     ]
20173                 },
20174                 {
20175                     tag: 'td',
20176                     cls: 'separator'
20177                 },
20178                 {
20179                     tag: 'td',
20180                     cn: [
20181                         {
20182                             tag: 'a',
20183                             href: '#',
20184                             cls: 'btn',
20185                             cn: [
20186                                 {
20187                                     tag: 'span',
20188                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
20189                                 }
20190                             ]
20191                         }
20192                     ]
20193                 },
20194                 {
20195                     tag: 'td',
20196                     cls: 'separator'
20197                 }
20198             ]
20199         });
20200         
20201     },
20202     
20203     update: function()
20204     {
20205         
20206         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20207         
20208         this.fill();
20209     },
20210     
20211     fill: function() 
20212     {
20213         var hours = this.time.getHours();
20214         var minutes = this.time.getMinutes();
20215         var period = 'AM';
20216         
20217         if(hours > 11){
20218             period = 'PM';
20219         }
20220         
20221         if(hours == 0){
20222             hours = 12;
20223         }
20224         
20225         
20226         if(hours > 12){
20227             hours = hours - 12;
20228         }
20229         
20230         if(hours < 10){
20231             hours = '0' + hours;
20232         }
20233         
20234         if(minutes < 10){
20235             minutes = '0' + minutes;
20236         }
20237         
20238         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20239         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20240         this.pop.select('button', true).first().dom.innerHTML = period;
20241         
20242     },
20243     
20244     place: function()
20245     {   
20246         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20247         
20248         var cls = ['bottom'];
20249         
20250         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20251             cls.pop();
20252             cls.push('top');
20253         }
20254         
20255         cls.push('right');
20256         
20257         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20258             cls.pop();
20259             cls.push('left');
20260         }
20261         
20262         this.picker().addClass(cls.join('-'));
20263         
20264         var _this = this;
20265         
20266         Roo.each(cls, function(c){
20267             if(c == 'bottom'){
20268                 _this.picker().setTop(_this.inputEl().getHeight());
20269                 return;
20270             }
20271             if(c == 'top'){
20272                 _this.picker().setTop(0 - _this.picker().getHeight());
20273                 return;
20274             }
20275             
20276             if(c == 'left'){
20277                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20278                 return;
20279             }
20280             if(c == 'right'){
20281                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20282                 return;
20283             }
20284         });
20285         
20286     },
20287   
20288     onFocus : function()
20289     {
20290         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20291         this.show();
20292     },
20293     
20294     onBlur : function()
20295     {
20296         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20297         this.hide();
20298     },
20299     
20300     show : function()
20301     {
20302         this.picker().show();
20303         this.pop.show();
20304         this.update();
20305         this.place();
20306         
20307         this.fireEvent('show', this, this.date);
20308     },
20309     
20310     hide : function()
20311     {
20312         this.picker().hide();
20313         this.pop.hide();
20314         
20315         this.fireEvent('hide', this, this.date);
20316     },
20317     
20318     setTime : function()
20319     {
20320         this.hide();
20321         this.setValue(this.time.format(this.format));
20322         
20323         this.fireEvent('select', this, this.date);
20324         
20325         
20326     },
20327     
20328     onMousedown: function(e){
20329         e.stopPropagation();
20330         e.preventDefault();
20331     },
20332     
20333     onIncrementHours: function()
20334     {
20335         Roo.log('onIncrementHours');
20336         this.time = this.time.add(Date.HOUR, 1);
20337         this.update();
20338         
20339     },
20340     
20341     onDecrementHours: function()
20342     {
20343         Roo.log('onDecrementHours');
20344         this.time = this.time.add(Date.HOUR, -1);
20345         this.update();
20346     },
20347     
20348     onIncrementMinutes: function()
20349     {
20350         Roo.log('onIncrementMinutes');
20351         this.time = this.time.add(Date.MINUTE, 1);
20352         this.update();
20353     },
20354     
20355     onDecrementMinutes: function()
20356     {
20357         Roo.log('onDecrementMinutes');
20358         this.time = this.time.add(Date.MINUTE, -1);
20359         this.update();
20360     },
20361     
20362     onTogglePeriod: function()
20363     {
20364         Roo.log('onTogglePeriod');
20365         this.time = this.time.add(Date.HOUR, 12);
20366         this.update();
20367     }
20368     
20369    
20370 });
20371
20372 Roo.apply(Roo.bootstrap.TimeField,  {
20373     
20374     content : {
20375         tag: 'tbody',
20376         cn: [
20377             {
20378                 tag: 'tr',
20379                 cn: [
20380                 {
20381                     tag: 'td',
20382                     colspan: '7'
20383                 }
20384                 ]
20385             }
20386         ]
20387     },
20388     
20389     footer : {
20390         tag: 'tfoot',
20391         cn: [
20392             {
20393                 tag: 'tr',
20394                 cn: [
20395                 {
20396                     tag: 'th',
20397                     colspan: '7',
20398                     cls: '',
20399                     cn: [
20400                         {
20401                             tag: 'button',
20402                             cls: 'btn btn-info ok',
20403                             html: 'OK'
20404                         }
20405                     ]
20406                 }
20407
20408                 ]
20409             }
20410         ]
20411     }
20412 });
20413
20414 Roo.apply(Roo.bootstrap.TimeField,  {
20415   
20416     template : {
20417         tag: 'div',
20418         cls: 'datepicker dropdown-menu',
20419         cn: [
20420             {
20421                 tag: 'div',
20422                 cls: 'datepicker-time',
20423                 cn: [
20424                 {
20425                     tag: 'table',
20426                     cls: 'table-condensed',
20427                     cn:[
20428                     Roo.bootstrap.TimeField.content,
20429                     Roo.bootstrap.TimeField.footer
20430                     ]
20431                 }
20432                 ]
20433             }
20434         ]
20435     }
20436 });
20437
20438  
20439
20440  /*
20441  * - LGPL
20442  *
20443  * MonthField
20444  * 
20445  */
20446
20447 /**
20448  * @class Roo.bootstrap.MonthField
20449  * @extends Roo.bootstrap.Input
20450  * Bootstrap MonthField class
20451  * 
20452  * @cfg {String} language default en
20453  * 
20454  * @constructor
20455  * Create a new MonthField
20456  * @param {Object} config The config object
20457  */
20458
20459 Roo.bootstrap.MonthField = function(config){
20460     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20461     
20462     this.addEvents({
20463         /**
20464          * @event show
20465          * Fires when this field show.
20466          * @param {Roo.bootstrap.MonthField} this
20467          * @param {Mixed} date The date value
20468          */
20469         show : true,
20470         /**
20471          * @event show
20472          * Fires when this field hide.
20473          * @param {Roo.bootstrap.MonthField} this
20474          * @param {Mixed} date The date value
20475          */
20476         hide : true,
20477         /**
20478          * @event select
20479          * Fires when select a date.
20480          * @param {Roo.bootstrap.MonthField} this
20481          * @param {String} oldvalue The old value
20482          * @param {String} newvalue The new value
20483          */
20484         select : true
20485     });
20486 };
20487
20488 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20489     
20490     onRender: function(ct, position)
20491     {
20492         
20493         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20494         
20495         this.language = this.language || 'en';
20496         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20497         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20498         
20499         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20500         this.isInline = false;
20501         this.isInput = true;
20502         this.component = this.el.select('.add-on', true).first() || false;
20503         this.component = (this.component && this.component.length === 0) ? false : this.component;
20504         this.hasInput = this.component && this.inputEL().length;
20505         
20506         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20507         
20508         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20509         
20510         this.picker().on('mousedown', this.onMousedown, this);
20511         this.picker().on('click', this.onClick, this);
20512         
20513         this.picker().addClass('datepicker-dropdown');
20514         
20515         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20516             v.setStyle('width', '189px');
20517         });
20518         
20519         this.fillMonths();
20520         
20521         this.update();
20522         
20523         if(this.isInline) {
20524             this.show();
20525         }
20526         
20527     },
20528     
20529     setValue: function(v, suppressEvent)
20530     {   
20531         var o = this.getValue();
20532         
20533         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20534         
20535         this.update();
20536
20537         if(suppressEvent !== true){
20538             this.fireEvent('select', this, o, v);
20539         }
20540         
20541     },
20542     
20543     getValue: function()
20544     {
20545         return this.value;
20546     },
20547     
20548     onClick: function(e) 
20549     {
20550         e.stopPropagation();
20551         e.preventDefault();
20552         
20553         var target = e.getTarget();
20554         
20555         if(target.nodeName.toLowerCase() === 'i'){
20556             target = Roo.get(target).dom.parentNode;
20557         }
20558         
20559         var nodeName = target.nodeName;
20560         var className = target.className;
20561         var html = target.innerHTML;
20562         
20563         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20564             return;
20565         }
20566         
20567         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20568         
20569         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20570         
20571         this.hide();
20572                         
20573     },
20574     
20575     picker : function()
20576     {
20577         return this.pickerEl;
20578     },
20579     
20580     fillMonths: function()
20581     {    
20582         var i = 0;
20583         var months = this.picker().select('>.datepicker-months td', true).first();
20584         
20585         months.dom.innerHTML = '';
20586         
20587         while (i < 12) {
20588             var month = {
20589                 tag: 'span',
20590                 cls: 'month',
20591                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20592             };
20593             
20594             months.createChild(month);
20595         }
20596         
20597     },
20598     
20599     update: function()
20600     {
20601         var _this = this;
20602         
20603         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20604             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20605         }
20606         
20607         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20608             e.removeClass('active');
20609             
20610             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20611                 e.addClass('active');
20612             }
20613         })
20614     },
20615     
20616     place: function()
20617     {
20618         if(this.isInline) {
20619             return;
20620         }
20621         
20622         this.picker().removeClass(['bottom', 'top']);
20623         
20624         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20625             /*
20626              * place to the top of element!
20627              *
20628              */
20629             
20630             this.picker().addClass('top');
20631             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20632             
20633             return;
20634         }
20635         
20636         this.picker().addClass('bottom');
20637         
20638         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20639     },
20640     
20641     onFocus : function()
20642     {
20643         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20644         this.show();
20645     },
20646     
20647     onBlur : function()
20648     {
20649         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20650         
20651         var d = this.inputEl().getValue();
20652         
20653         this.setValue(d);
20654                 
20655         this.hide();
20656     },
20657     
20658     show : function()
20659     {
20660         this.picker().show();
20661         this.picker().select('>.datepicker-months', true).first().show();
20662         this.update();
20663         this.place();
20664         
20665         this.fireEvent('show', this, this.date);
20666     },
20667     
20668     hide : function()
20669     {
20670         if(this.isInline) {
20671             return;
20672         }
20673         this.picker().hide();
20674         this.fireEvent('hide', this, this.date);
20675         
20676     },
20677     
20678     onMousedown: function(e)
20679     {
20680         e.stopPropagation();
20681         e.preventDefault();
20682     },
20683     
20684     keyup: function(e)
20685     {
20686         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20687         this.update();
20688     },
20689
20690     fireKey: function(e)
20691     {
20692         if (!this.picker().isVisible()){
20693             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20694                 this.show();
20695             }
20696             return;
20697         }
20698         
20699         var dir;
20700         
20701         switch(e.keyCode){
20702             case 27: // escape
20703                 this.hide();
20704                 e.preventDefault();
20705                 break;
20706             case 37: // left
20707             case 39: // right
20708                 dir = e.keyCode == 37 ? -1 : 1;
20709                 
20710                 this.vIndex = this.vIndex + dir;
20711                 
20712                 if(this.vIndex < 0){
20713                     this.vIndex = 0;
20714                 }
20715                 
20716                 if(this.vIndex > 11){
20717                     this.vIndex = 11;
20718                 }
20719                 
20720                 if(isNaN(this.vIndex)){
20721                     this.vIndex = 0;
20722                 }
20723                 
20724                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20725                 
20726                 break;
20727             case 38: // up
20728             case 40: // down
20729                 
20730                 dir = e.keyCode == 38 ? -1 : 1;
20731                 
20732                 this.vIndex = this.vIndex + dir * 4;
20733                 
20734                 if(this.vIndex < 0){
20735                     this.vIndex = 0;
20736                 }
20737                 
20738                 if(this.vIndex > 11){
20739                     this.vIndex = 11;
20740                 }
20741                 
20742                 if(isNaN(this.vIndex)){
20743                     this.vIndex = 0;
20744                 }
20745                 
20746                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20747                 break;
20748                 
20749             case 13: // enter
20750                 
20751                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20752                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20753                 }
20754                 
20755                 this.hide();
20756                 e.preventDefault();
20757                 break;
20758             case 9: // tab
20759                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20760                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20761                 }
20762                 this.hide();
20763                 break;
20764             case 16: // shift
20765             case 17: // ctrl
20766             case 18: // alt
20767                 break;
20768             default :
20769                 this.hide();
20770                 
20771         }
20772     },
20773     
20774     remove: function() 
20775     {
20776         this.picker().remove();
20777     }
20778    
20779 });
20780
20781 Roo.apply(Roo.bootstrap.MonthField,  {
20782     
20783     content : {
20784         tag: 'tbody',
20785         cn: [
20786         {
20787             tag: 'tr',
20788             cn: [
20789             {
20790                 tag: 'td',
20791                 colspan: '7'
20792             }
20793             ]
20794         }
20795         ]
20796     },
20797     
20798     dates:{
20799         en: {
20800             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20801             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20802         }
20803     }
20804 });
20805
20806 Roo.apply(Roo.bootstrap.MonthField,  {
20807   
20808     template : {
20809         tag: 'div',
20810         cls: 'datepicker dropdown-menu roo-dynamic',
20811         cn: [
20812             {
20813                 tag: 'div',
20814                 cls: 'datepicker-months',
20815                 cn: [
20816                 {
20817                     tag: 'table',
20818                     cls: 'table-condensed',
20819                     cn:[
20820                         Roo.bootstrap.DateField.content
20821                     ]
20822                 }
20823                 ]
20824             }
20825         ]
20826     }
20827 });
20828
20829  
20830
20831  
20832  /*
20833  * - LGPL
20834  *
20835  * CheckBox
20836  * 
20837  */
20838
20839 /**
20840  * @class Roo.bootstrap.CheckBox
20841  * @extends Roo.bootstrap.Input
20842  * Bootstrap CheckBox class
20843  * 
20844  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20845  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20846  * @cfg {String} boxLabel The text that appears beside the checkbox
20847  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20848  * @cfg {Boolean} checked initnal the element
20849  * @cfg {Boolean} inline inline the element (default false)
20850  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20851  * @cfg {String} tooltip label tooltip
20852  * 
20853  * @constructor
20854  * Create a new CheckBox
20855  * @param {Object} config The config object
20856  */
20857
20858 Roo.bootstrap.CheckBox = function(config){
20859     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20860    
20861     this.addEvents({
20862         /**
20863         * @event check
20864         * Fires when the element is checked or unchecked.
20865         * @param {Roo.bootstrap.CheckBox} this This input
20866         * @param {Boolean} checked The new checked value
20867         */
20868        check : true,
20869        /**
20870         * @event click
20871         * Fires when the element is click.
20872         * @param {Roo.bootstrap.CheckBox} this This input
20873         */
20874        click : true
20875     });
20876     
20877 };
20878
20879 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20880   
20881     inputType: 'checkbox',
20882     inputValue: 1,
20883     valueOff: 0,
20884     boxLabel: false,
20885     checked: false,
20886     weight : false,
20887     inline: false,
20888     tooltip : '',
20889     
20890     getAutoCreate : function()
20891     {
20892         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20893         
20894         var id = Roo.id();
20895         
20896         var cfg = {};
20897         
20898         cfg.cls = 'form-group ' + this.inputType; //input-group
20899         
20900         if(this.inline){
20901             cfg.cls += ' ' + this.inputType + '-inline';
20902         }
20903         
20904         var input =  {
20905             tag: 'input',
20906             id : id,
20907             type : this.inputType,
20908             value : this.inputValue,
20909             cls : 'roo-' + this.inputType, //'form-box',
20910             placeholder : this.placeholder || ''
20911             
20912         };
20913         
20914         if(this.inputType != 'radio'){
20915             var hidden =  {
20916                 tag: 'input',
20917                 type : 'hidden',
20918                 cls : 'roo-hidden-value',
20919                 value : this.checked ? this.inputValue : this.valueOff
20920             };
20921         }
20922         
20923             
20924         if (this.weight) { // Validity check?
20925             cfg.cls += " " + this.inputType + "-" + this.weight;
20926         }
20927         
20928         if (this.disabled) {
20929             input.disabled=true;
20930         }
20931         
20932         if(this.checked){
20933             input.checked = this.checked;
20934         }
20935         
20936         if (this.name) {
20937             
20938             input.name = this.name;
20939             
20940             if(this.inputType != 'radio'){
20941                 hidden.name = this.name;
20942                 input.name = '_hidden_' + this.name;
20943             }
20944         }
20945         
20946         if (this.size) {
20947             input.cls += ' input-' + this.size;
20948         }
20949         
20950         var settings=this;
20951         
20952         ['xs','sm','md','lg'].map(function(size){
20953             if (settings[size]) {
20954                 cfg.cls += ' col-' + size + '-' + settings[size];
20955             }
20956         });
20957         
20958         var inputblock = input;
20959          
20960         if (this.before || this.after) {
20961             
20962             inputblock = {
20963                 cls : 'input-group',
20964                 cn :  [] 
20965             };
20966             
20967             if (this.before) {
20968                 inputblock.cn.push({
20969                     tag :'span',
20970                     cls : 'input-group-addon',
20971                     html : this.before
20972                 });
20973             }
20974             
20975             inputblock.cn.push(input);
20976             
20977             if(this.inputType != 'radio'){
20978                 inputblock.cn.push(hidden);
20979             }
20980             
20981             if (this.after) {
20982                 inputblock.cn.push({
20983                     tag :'span',
20984                     cls : 'input-group-addon',
20985                     html : this.after
20986                 });
20987             }
20988             
20989         }
20990         
20991         if (align ==='left' && this.fieldLabel.length) {
20992 //                Roo.log("left and has label");
20993             cfg.cn = [
20994                 {
20995                     tag: 'label',
20996                     'for' :  id,
20997                     cls : 'control-label',
20998                     html : this.fieldLabel
20999                 },
21000                 {
21001                     cls : "", 
21002                     cn: [
21003                         inputblock
21004                     ]
21005                 }
21006             ];
21007             
21008             if(this.labelWidth > 12){
21009                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
21010             }
21011             
21012             if(this.labelWidth < 13 && this.labelmd == 0){
21013                 this.labelmd = this.labelWidth;
21014             }
21015             
21016             if(this.labellg > 0){
21017                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
21018                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
21019             }
21020             
21021             if(this.labelmd > 0){
21022                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
21023                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
21024             }
21025             
21026             if(this.labelsm > 0){
21027                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
21028                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
21029             }
21030             
21031             if(this.labelxs > 0){
21032                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
21033                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
21034             }
21035             
21036         } else if ( this.fieldLabel.length) {
21037 //                Roo.log(" label");
21038                 cfg.cn = [
21039                    
21040                     {
21041                         tag: this.boxLabel ? 'span' : 'label',
21042                         'for': id,
21043                         cls: 'control-label box-input-label',
21044                         //cls : 'input-group-addon',
21045                         html : this.fieldLabel
21046                     },
21047                     
21048                     inputblock
21049                     
21050                 ];
21051
21052         } else {
21053             
21054 //                Roo.log(" no label && no align");
21055                 cfg.cn = [  inputblock ] ;
21056                 
21057                 
21058         }
21059         
21060         if(this.boxLabel){
21061              var boxLabelCfg = {
21062                 tag: 'label',
21063                 //'for': id, // box label is handled by onclick - so no for...
21064                 cls: 'box-label',
21065                 html: this.boxLabel
21066             };
21067             
21068             if(this.tooltip){
21069                 boxLabelCfg.tooltip = this.tooltip;
21070             }
21071              
21072             cfg.cn.push(boxLabelCfg);
21073         }
21074         
21075         if(this.inputType != 'radio'){
21076             cfg.cn.push(hidden);
21077         }
21078         
21079         return cfg;
21080         
21081     },
21082     
21083     /**
21084      * return the real input element.
21085      */
21086     inputEl: function ()
21087     {
21088         return this.el.select('input.roo-' + this.inputType,true).first();
21089     },
21090     hiddenEl: function ()
21091     {
21092         return this.el.select('input.roo-hidden-value',true).first();
21093     },
21094     
21095     labelEl: function()
21096     {
21097         return this.el.select('label.control-label',true).first();
21098     },
21099     /* depricated... */
21100     
21101     label: function()
21102     {
21103         return this.labelEl();
21104     },
21105     
21106     boxLabelEl: function()
21107     {
21108         return this.el.select('label.box-label',true).first();
21109     },
21110     
21111     initEvents : function()
21112     {
21113 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
21114         
21115         this.inputEl().on('click', this.onClick,  this);
21116         
21117         if (this.boxLabel) { 
21118             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
21119         }
21120         
21121         this.startValue = this.getValue();
21122         
21123         if(this.groupId){
21124             Roo.bootstrap.CheckBox.register(this);
21125         }
21126     },
21127     
21128     onClick : function(e)
21129     {   
21130         if(this.fireEvent('click', this, e) !== false){
21131             this.setChecked(!this.checked);
21132         }
21133         
21134     },
21135     
21136     setChecked : function(state,suppressEvent)
21137     {
21138         this.startValue = this.getValue();
21139
21140         if(this.inputType == 'radio'){
21141             
21142             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21143                 e.dom.checked = false;
21144             });
21145             
21146             this.inputEl().dom.checked = true;
21147             
21148             this.inputEl().dom.value = this.inputValue;
21149             
21150             if(suppressEvent !== true){
21151                 this.fireEvent('check', this, true);
21152             }
21153             
21154             this.validate();
21155             
21156             return;
21157         }
21158         
21159         this.checked = state;
21160         
21161         this.inputEl().dom.checked = state;
21162         
21163         
21164         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
21165         
21166         if(suppressEvent !== true){
21167             this.fireEvent('check', this, state);
21168         }
21169         
21170         this.validate();
21171     },
21172     
21173     getValue : function()
21174     {
21175         if(this.inputType == 'radio'){
21176             return this.getGroupValue();
21177         }
21178         
21179         return this.hiddenEl().dom.value;
21180         
21181     },
21182     
21183     getGroupValue : function()
21184     {
21185         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21186             return '';
21187         }
21188         
21189         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21190     },
21191     
21192     setValue : function(v,suppressEvent)
21193     {
21194         if(this.inputType == 'radio'){
21195             this.setGroupValue(v, suppressEvent);
21196             return;
21197         }
21198         
21199         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21200         
21201         this.validate();
21202     },
21203     
21204     setGroupValue : function(v, suppressEvent)
21205     {
21206         this.startValue = this.getValue();
21207         
21208         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21209             e.dom.checked = false;
21210             
21211             if(e.dom.value == v){
21212                 e.dom.checked = true;
21213             }
21214         });
21215         
21216         if(suppressEvent !== true){
21217             this.fireEvent('check', this, true);
21218         }
21219
21220         this.validate();
21221         
21222         return;
21223     },
21224     
21225     validate : function()
21226     {
21227         if(this.getVisibilityEl().hasClass('hidden')){
21228             return true;
21229         }
21230         
21231         if(
21232                 this.disabled || 
21233                 (this.inputType == 'radio' && this.validateRadio()) ||
21234                 (this.inputType == 'checkbox' && this.validateCheckbox())
21235         ){
21236             this.markValid();
21237             return true;
21238         }
21239         
21240         this.markInvalid();
21241         return false;
21242     },
21243     
21244     validateRadio : function()
21245     {
21246         if(this.getVisibilityEl().hasClass('hidden')){
21247             return true;
21248         }
21249         
21250         if(this.allowBlank){
21251             return true;
21252         }
21253         
21254         var valid = false;
21255         
21256         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21257             if(!e.dom.checked){
21258                 return;
21259             }
21260             
21261             valid = true;
21262             
21263             return false;
21264         });
21265         
21266         return valid;
21267     },
21268     
21269     validateCheckbox : function()
21270     {
21271         if(!this.groupId){
21272             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21273             //return (this.getValue() == this.inputValue) ? true : false;
21274         }
21275         
21276         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21277         
21278         if(!group){
21279             return false;
21280         }
21281         
21282         var r = false;
21283         
21284         for(var i in group){
21285             if(group[i].el.isVisible(true)){
21286                 r = false;
21287                 break;
21288             }
21289             
21290             r = true;
21291         }
21292         
21293         for(var i in group){
21294             if(r){
21295                 break;
21296             }
21297             
21298             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21299         }
21300         
21301         return r;
21302     },
21303     
21304     /**
21305      * Mark this field as valid
21306      */
21307     markValid : function()
21308     {
21309         var _this = this;
21310         
21311         this.fireEvent('valid', this);
21312         
21313         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21314         
21315         if(this.groupId){
21316             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21317         }
21318         
21319         if(label){
21320             label.markValid();
21321         }
21322
21323         if(this.inputType == 'radio'){
21324             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21325                 var fg = e.findParent('.form-group', false, true);
21326                 if (Roo.bootstrap.version == 3) {
21327                     fg.removeClass([_this.invalidClass, _this.validClass]);
21328                     fg.addClass(_this.validClass);
21329                 } else {
21330                     fg.removeClass(['is-valid', 'is-invalid']);
21331                     fg.addClass('is-valid');
21332                 }
21333             });
21334             
21335             return;
21336         }
21337
21338         if(!this.groupId){
21339             var fg = this.el.findParent('.form-group', false, true);
21340             if (Roo.bootstrap.version == 3) {
21341                 fg.removeClass([this.invalidClass, this.validClass]);
21342                 fg.addClass(this.validClass);
21343             } else {
21344                 fg.removeClass(['is-valid', 'is-invalid']);
21345                 fg.addClass('is-valid');
21346             }
21347             return;
21348         }
21349         
21350         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21351         
21352         if(!group){
21353             return;
21354         }
21355         
21356         for(var i in group){
21357             var fg = group[i].el.findParent('.form-group', false, true);
21358             if (Roo.bootstrap.version == 3) {
21359                 fg.removeClass([this.invalidClass, this.validClass]);
21360                 fg.addClass(this.validClass);
21361             } else {
21362                 fg.removeClass(['is-valid', 'is-invalid']);
21363                 fg.addClass('is-valid');
21364             }
21365         }
21366     },
21367     
21368      /**
21369      * Mark this field as invalid
21370      * @param {String} msg The validation message
21371      */
21372     markInvalid : function(msg)
21373     {
21374         if(this.allowBlank){
21375             return;
21376         }
21377         
21378         var _this = this;
21379         
21380         this.fireEvent('invalid', this, msg);
21381         
21382         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21383         
21384         if(this.groupId){
21385             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21386         }
21387         
21388         if(label){
21389             label.markInvalid();
21390         }
21391             
21392         if(this.inputType == 'radio'){
21393             
21394             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21395                 var fg = e.findParent('.form-group', false, true);
21396                 if (Roo.bootstrap.version == 3) {
21397                     fg.removeClass([_this.invalidClass, _this.validClass]);
21398                     fg.addClass(_this.invalidClass);
21399                 } else {
21400                     fg.removeClass(['is-invalid', 'is-valid']);
21401                     fg.addClass('is-invalid');
21402                 }
21403             });
21404             
21405             return;
21406         }
21407         
21408         if(!this.groupId){
21409             var fg = this.el.findParent('.form-group', false, true);
21410             if (Roo.bootstrap.version == 3) {
21411                 fg.removeClass([_this.invalidClass, _this.validClass]);
21412                 fg.addClass(_this.invalidClass);
21413             } else {
21414                 fg.removeClass(['is-invalid', 'is-valid']);
21415                 fg.addClass('is-invalid');
21416             }
21417             return;
21418         }
21419         
21420         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21421         
21422         if(!group){
21423             return;
21424         }
21425         
21426         for(var i in group){
21427             var fg = group[i].el.findParent('.form-group', false, true);
21428             if (Roo.bootstrap.version == 3) {
21429                 fg.removeClass([_this.invalidClass, _this.validClass]);
21430                 fg.addClass(_this.invalidClass);
21431             } else {
21432                 fg.removeClass(['is-invalid', 'is-valid']);
21433                 fg.addClass('is-invalid');
21434             }
21435         }
21436         
21437     },
21438     
21439     clearInvalid : function()
21440     {
21441         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21442         
21443         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21444         
21445         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21446         
21447         if (label && label.iconEl) {
21448             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
21449             label.iconEl.removeClass(['is-invalid', 'is-valid']);
21450         }
21451     },
21452     
21453     disable : function()
21454     {
21455         if(this.inputType != 'radio'){
21456             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21457             return;
21458         }
21459         
21460         var _this = this;
21461         
21462         if(this.rendered){
21463             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21464                 _this.getActionEl().addClass(this.disabledClass);
21465                 e.dom.disabled = true;
21466             });
21467         }
21468         
21469         this.disabled = true;
21470         this.fireEvent("disable", this);
21471         return this;
21472     },
21473
21474     enable : function()
21475     {
21476         if(this.inputType != 'radio'){
21477             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21478             return;
21479         }
21480         
21481         var _this = this;
21482         
21483         if(this.rendered){
21484             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21485                 _this.getActionEl().removeClass(this.disabledClass);
21486                 e.dom.disabled = false;
21487             });
21488         }
21489         
21490         this.disabled = false;
21491         this.fireEvent("enable", this);
21492         return this;
21493     },
21494     
21495     setBoxLabel : function(v)
21496     {
21497         this.boxLabel = v;
21498         
21499         if(this.rendered){
21500             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21501         }
21502     }
21503
21504 });
21505
21506 Roo.apply(Roo.bootstrap.CheckBox, {
21507     
21508     groups: {},
21509     
21510      /**
21511     * register a CheckBox Group
21512     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21513     */
21514     register : function(checkbox)
21515     {
21516         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21517             this.groups[checkbox.groupId] = {};
21518         }
21519         
21520         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21521             return;
21522         }
21523         
21524         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21525         
21526     },
21527     /**
21528     * fetch a CheckBox Group based on the group ID
21529     * @param {string} the group ID
21530     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21531     */
21532     get: function(groupId) {
21533         if (typeof(this.groups[groupId]) == 'undefined') {
21534             return false;
21535         }
21536         
21537         return this.groups[groupId] ;
21538     }
21539     
21540     
21541 });
21542 /*
21543  * - LGPL
21544  *
21545  * RadioItem
21546  * 
21547  */
21548
21549 /**
21550  * @class Roo.bootstrap.Radio
21551  * @extends Roo.bootstrap.Component
21552  * Bootstrap Radio class
21553  * @cfg {String} boxLabel - the label associated
21554  * @cfg {String} value - the value of radio
21555  * 
21556  * @constructor
21557  * Create a new Radio
21558  * @param {Object} config The config object
21559  */
21560 Roo.bootstrap.Radio = function(config){
21561     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21562     
21563 };
21564
21565 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21566     
21567     boxLabel : '',
21568     
21569     value : '',
21570     
21571     getAutoCreate : function()
21572     {
21573         var cfg = {
21574             tag : 'div',
21575             cls : 'form-group radio',
21576             cn : [
21577                 {
21578                     tag : 'label',
21579                     cls : 'box-label',
21580                     html : this.boxLabel
21581                 }
21582             ]
21583         };
21584         
21585         return cfg;
21586     },
21587     
21588     initEvents : function() 
21589     {
21590         this.parent().register(this);
21591         
21592         this.el.on('click', this.onClick, this);
21593         
21594     },
21595     
21596     onClick : function(e)
21597     {
21598         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21599             this.setChecked(true);
21600         }
21601     },
21602     
21603     setChecked : function(state, suppressEvent)
21604     {
21605         this.parent().setValue(this.value, suppressEvent);
21606         
21607     },
21608     
21609     setBoxLabel : function(v)
21610     {
21611         this.boxLabel = v;
21612         
21613         if(this.rendered){
21614             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21615         }
21616     }
21617     
21618 });
21619  
21620
21621  /*
21622  * - LGPL
21623  *
21624  * Input
21625  * 
21626  */
21627
21628 /**
21629  * @class Roo.bootstrap.SecurePass
21630  * @extends Roo.bootstrap.Input
21631  * Bootstrap SecurePass class
21632  *
21633  * 
21634  * @constructor
21635  * Create a new SecurePass
21636  * @param {Object} config The config object
21637  */
21638  
21639 Roo.bootstrap.SecurePass = function (config) {
21640     // these go here, so the translation tool can replace them..
21641     this.errors = {
21642         PwdEmpty: "Please type a password, and then retype it to confirm.",
21643         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21644         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21645         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21646         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21647         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21648         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21649         TooWeak: "Your password is Too Weak."
21650     },
21651     this.meterLabel = "Password strength:";
21652     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21653     this.meterClass = [
21654         "roo-password-meter-tooweak", 
21655         "roo-password-meter-weak", 
21656         "roo-password-meter-medium", 
21657         "roo-password-meter-strong", 
21658         "roo-password-meter-grey"
21659     ];
21660     
21661     this.errors = {};
21662     
21663     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21664 }
21665
21666 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21667     /**
21668      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21669      * {
21670      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21671      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21672      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21673      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21674      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21675      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21676      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21677      * })
21678      */
21679     // private
21680     
21681     meterWidth: 300,
21682     errorMsg :'',    
21683     errors: false,
21684     imageRoot: '/',
21685     /**
21686      * @cfg {String/Object} Label for the strength meter (defaults to
21687      * 'Password strength:')
21688      */
21689     // private
21690     meterLabel: '',
21691     /**
21692      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21693      * ['Weak', 'Medium', 'Strong'])
21694      */
21695     // private    
21696     pwdStrengths: false,    
21697     // private
21698     strength: 0,
21699     // private
21700     _lastPwd: null,
21701     // private
21702     kCapitalLetter: 0,
21703     kSmallLetter: 1,
21704     kDigit: 2,
21705     kPunctuation: 3,
21706     
21707     insecure: false,
21708     // private
21709     initEvents: function ()
21710     {
21711         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21712
21713         if (this.el.is('input[type=password]') && Roo.isSafari) {
21714             this.el.on('keydown', this.SafariOnKeyDown, this);
21715         }
21716
21717         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21718     },
21719     // private
21720     onRender: function (ct, position)
21721     {
21722         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21723         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21724         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21725
21726         this.trigger.createChild({
21727                    cn: [
21728                     {
21729                     //id: 'PwdMeter',
21730                     tag: 'div',
21731                     cls: 'roo-password-meter-grey col-xs-12',
21732                     style: {
21733                         //width: 0,
21734                         //width: this.meterWidth + 'px'                                                
21735                         }
21736                     },
21737                     {                            
21738                          cls: 'roo-password-meter-text'                          
21739                     }
21740                 ]            
21741         });
21742
21743          
21744         if (this.hideTrigger) {
21745             this.trigger.setDisplayed(false);
21746         }
21747         this.setSize(this.width || '', this.height || '');
21748     },
21749     // private
21750     onDestroy: function ()
21751     {
21752         if (this.trigger) {
21753             this.trigger.removeAllListeners();
21754             this.trigger.remove();
21755         }
21756         if (this.wrap) {
21757             this.wrap.remove();
21758         }
21759         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21760     },
21761     // private
21762     checkStrength: function ()
21763     {
21764         var pwd = this.inputEl().getValue();
21765         if (pwd == this._lastPwd) {
21766             return;
21767         }
21768
21769         var strength;
21770         if (this.ClientSideStrongPassword(pwd)) {
21771             strength = 3;
21772         } else if (this.ClientSideMediumPassword(pwd)) {
21773             strength = 2;
21774         } else if (this.ClientSideWeakPassword(pwd)) {
21775             strength = 1;
21776         } else {
21777             strength = 0;
21778         }
21779         
21780         Roo.log('strength1: ' + strength);
21781         
21782         //var pm = this.trigger.child('div/div/div').dom;
21783         var pm = this.trigger.child('div/div');
21784         pm.removeClass(this.meterClass);
21785         pm.addClass(this.meterClass[strength]);
21786                 
21787         
21788         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21789                 
21790         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21791         
21792         this._lastPwd = pwd;
21793     },
21794     reset: function ()
21795     {
21796         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21797         
21798         this._lastPwd = '';
21799         
21800         var pm = this.trigger.child('div/div');
21801         pm.removeClass(this.meterClass);
21802         pm.addClass('roo-password-meter-grey');        
21803         
21804         
21805         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21806         
21807         pt.innerHTML = '';
21808         this.inputEl().dom.type='password';
21809     },
21810     // private
21811     validateValue: function (value)
21812     {
21813         
21814         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21815             return false;
21816         }
21817         if (value.length == 0) {
21818             if (this.allowBlank) {
21819                 this.clearInvalid();
21820                 return true;
21821             }
21822
21823             this.markInvalid(this.errors.PwdEmpty);
21824             this.errorMsg = this.errors.PwdEmpty;
21825             return false;
21826         }
21827         
21828         if(this.insecure){
21829             return true;
21830         }
21831         
21832         if ('[\x21-\x7e]*'.match(value)) {
21833             this.markInvalid(this.errors.PwdBadChar);
21834             this.errorMsg = this.errors.PwdBadChar;
21835             return false;
21836         }
21837         if (value.length < 6) {
21838             this.markInvalid(this.errors.PwdShort);
21839             this.errorMsg = this.errors.PwdShort;
21840             return false;
21841         }
21842         if (value.length > 16) {
21843             this.markInvalid(this.errors.PwdLong);
21844             this.errorMsg = this.errors.PwdLong;
21845             return false;
21846         }
21847         var strength;
21848         if (this.ClientSideStrongPassword(value)) {
21849             strength = 3;
21850         } else if (this.ClientSideMediumPassword(value)) {
21851             strength = 2;
21852         } else if (this.ClientSideWeakPassword(value)) {
21853             strength = 1;
21854         } else {
21855             strength = 0;
21856         }
21857
21858         
21859         if (strength < 2) {
21860             //this.markInvalid(this.errors.TooWeak);
21861             this.errorMsg = this.errors.TooWeak;
21862             //return false;
21863         }
21864         
21865         
21866         console.log('strength2: ' + strength);
21867         
21868         //var pm = this.trigger.child('div/div/div').dom;
21869         
21870         var pm = this.trigger.child('div/div');
21871         pm.removeClass(this.meterClass);
21872         pm.addClass(this.meterClass[strength]);
21873                 
21874         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21875                 
21876         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21877         
21878         this.errorMsg = ''; 
21879         return true;
21880     },
21881     // private
21882     CharacterSetChecks: function (type)
21883     {
21884         this.type = type;
21885         this.fResult = false;
21886     },
21887     // private
21888     isctype: function (character, type)
21889     {
21890         switch (type) {  
21891             case this.kCapitalLetter:
21892                 if (character >= 'A' && character <= 'Z') {
21893                     return true;
21894                 }
21895                 break;
21896             
21897             case this.kSmallLetter:
21898                 if (character >= 'a' && character <= 'z') {
21899                     return true;
21900                 }
21901                 break;
21902             
21903             case this.kDigit:
21904                 if (character >= '0' && character <= '9') {
21905                     return true;
21906                 }
21907                 break;
21908             
21909             case this.kPunctuation:
21910                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21911                     return true;
21912                 }
21913                 break;
21914             
21915             default:
21916                 return false;
21917         }
21918
21919     },
21920     // private
21921     IsLongEnough: function (pwd, size)
21922     {
21923         return !(pwd == null || isNaN(size) || pwd.length < size);
21924     },
21925     // private
21926     SpansEnoughCharacterSets: function (word, nb)
21927     {
21928         if (!this.IsLongEnough(word, nb))
21929         {
21930             return false;
21931         }
21932
21933         var characterSetChecks = new Array(
21934             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21935             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21936         );
21937         
21938         for (var index = 0; index < word.length; ++index) {
21939             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21940                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21941                     characterSetChecks[nCharSet].fResult = true;
21942                     break;
21943                 }
21944             }
21945         }
21946
21947         var nCharSets = 0;
21948         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21949             if (characterSetChecks[nCharSet].fResult) {
21950                 ++nCharSets;
21951             }
21952         }
21953
21954         if (nCharSets < nb) {
21955             return false;
21956         }
21957         return true;
21958     },
21959     // private
21960     ClientSideStrongPassword: function (pwd)
21961     {
21962         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21963     },
21964     // private
21965     ClientSideMediumPassword: function (pwd)
21966     {
21967         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21968     },
21969     // private
21970     ClientSideWeakPassword: function (pwd)
21971     {
21972         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21973     }
21974           
21975 })//<script type="text/javascript">
21976
21977 /*
21978  * Based  Ext JS Library 1.1.1
21979  * Copyright(c) 2006-2007, Ext JS, LLC.
21980  * LGPL
21981  *
21982  */
21983  
21984 /**
21985  * @class Roo.HtmlEditorCore
21986  * @extends Roo.Component
21987  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21988  *
21989  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21990  */
21991
21992 Roo.HtmlEditorCore = function(config){
21993     
21994     
21995     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21996     
21997     
21998     this.addEvents({
21999         /**
22000          * @event initialize
22001          * Fires when the editor is fully initialized (including the iframe)
22002          * @param {Roo.HtmlEditorCore} this
22003          */
22004         initialize: true,
22005         /**
22006          * @event activate
22007          * Fires when the editor is first receives the focus. Any insertion must wait
22008          * until after this event.
22009          * @param {Roo.HtmlEditorCore} this
22010          */
22011         activate: true,
22012          /**
22013          * @event beforesync
22014          * Fires before the textarea is updated with content from the editor iframe. Return false
22015          * to cancel the sync.
22016          * @param {Roo.HtmlEditorCore} this
22017          * @param {String} html
22018          */
22019         beforesync: true,
22020          /**
22021          * @event beforepush
22022          * Fires before the iframe editor is updated with content from the textarea. Return false
22023          * to cancel the push.
22024          * @param {Roo.HtmlEditorCore} this
22025          * @param {String} html
22026          */
22027         beforepush: true,
22028          /**
22029          * @event sync
22030          * Fires when the textarea is updated with content from the editor iframe.
22031          * @param {Roo.HtmlEditorCore} this
22032          * @param {String} html
22033          */
22034         sync: true,
22035          /**
22036          * @event push
22037          * Fires when the iframe editor is updated with content from the textarea.
22038          * @param {Roo.HtmlEditorCore} this
22039          * @param {String} html
22040          */
22041         push: true,
22042         
22043         /**
22044          * @event editorevent
22045          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22046          * @param {Roo.HtmlEditorCore} this
22047          */
22048         editorevent: true
22049         
22050     });
22051     
22052     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
22053     
22054     // defaults : white / black...
22055     this.applyBlacklists();
22056     
22057     
22058     
22059 };
22060
22061
22062 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
22063
22064
22065      /**
22066      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
22067      */
22068     
22069     owner : false,
22070     
22071      /**
22072      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22073      *                        Roo.resizable.
22074      */
22075     resizable : false,
22076      /**
22077      * @cfg {Number} height (in pixels)
22078      */   
22079     height: 300,
22080    /**
22081      * @cfg {Number} width (in pixels)
22082      */   
22083     width: 500,
22084     
22085     /**
22086      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22087      * 
22088      */
22089     stylesheets: false,
22090     
22091     // id of frame..
22092     frameId: false,
22093     
22094     // private properties
22095     validationEvent : false,
22096     deferHeight: true,
22097     initialized : false,
22098     activated : false,
22099     sourceEditMode : false,
22100     onFocus : Roo.emptyFn,
22101     iframePad:3,
22102     hideMode:'offsets',
22103     
22104     clearUp: true,
22105     
22106     // blacklist + whitelisted elements..
22107     black: false,
22108     white: false,
22109      
22110     bodyCls : '',
22111
22112     /**
22113      * Protected method that will not generally be called directly. It
22114      * is called when the editor initializes the iframe with HTML contents. Override this method if you
22115      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
22116      */
22117     getDocMarkup : function(){
22118         // body styles..
22119         var st = '';
22120         
22121         // inherit styels from page...?? 
22122         if (this.stylesheets === false) {
22123             
22124             Roo.get(document.head).select('style').each(function(node) {
22125                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22126             });
22127             
22128             Roo.get(document.head).select('link').each(function(node) { 
22129                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22130             });
22131             
22132         } else if (!this.stylesheets.length) {
22133                 // simple..
22134                 st = '<style type="text/css">' +
22135                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22136                    '</style>';
22137         } else { 
22138             st = '<style type="text/css">' +
22139                     this.stylesheets +
22140                 '</style>';
22141         }
22142         
22143         st +=  '<style type="text/css">' +
22144             'IMG { cursor: pointer } ' +
22145         '</style>';
22146
22147         var cls = 'roo-htmleditor-body';
22148         
22149         if(this.bodyCls.length){
22150             cls += ' ' + this.bodyCls;
22151         }
22152         
22153         return '<html><head>' + st  +
22154             //<style type="text/css">' +
22155             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22156             //'</style>' +
22157             ' </head><body class="' +  cls + '"></body></html>';
22158     },
22159
22160     // private
22161     onRender : function(ct, position)
22162     {
22163         var _t = this;
22164         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
22165         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
22166         
22167         
22168         this.el.dom.style.border = '0 none';
22169         this.el.dom.setAttribute('tabIndex', -1);
22170         this.el.addClass('x-hidden hide');
22171         
22172         
22173         
22174         if(Roo.isIE){ // fix IE 1px bogus margin
22175             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
22176         }
22177        
22178         
22179         this.frameId = Roo.id();
22180         
22181          
22182         
22183         var iframe = this.owner.wrap.createChild({
22184             tag: 'iframe',
22185             cls: 'form-control', // bootstrap..
22186             id: this.frameId,
22187             name: this.frameId,
22188             frameBorder : 'no',
22189             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
22190         }, this.el
22191         );
22192         
22193         
22194         this.iframe = iframe.dom;
22195
22196          this.assignDocWin();
22197         
22198         this.doc.designMode = 'on';
22199        
22200         this.doc.open();
22201         this.doc.write(this.getDocMarkup());
22202         this.doc.close();
22203
22204         
22205         var task = { // must defer to wait for browser to be ready
22206             run : function(){
22207                 //console.log("run task?" + this.doc.readyState);
22208                 this.assignDocWin();
22209                 if(this.doc.body || this.doc.readyState == 'complete'){
22210                     try {
22211                         this.doc.designMode="on";
22212                     } catch (e) {
22213                         return;
22214                     }
22215                     Roo.TaskMgr.stop(task);
22216                     this.initEditor.defer(10, this);
22217                 }
22218             },
22219             interval : 10,
22220             duration: 10000,
22221             scope: this
22222         };
22223         Roo.TaskMgr.start(task);
22224
22225     },
22226
22227     // private
22228     onResize : function(w, h)
22229     {
22230          Roo.log('resize: ' +w + ',' + h );
22231         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22232         if(!this.iframe){
22233             return;
22234         }
22235         if(typeof w == 'number'){
22236             
22237             this.iframe.style.width = w + 'px';
22238         }
22239         if(typeof h == 'number'){
22240             
22241             this.iframe.style.height = h + 'px';
22242             if(this.doc){
22243                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22244             }
22245         }
22246         
22247     },
22248
22249     /**
22250      * Toggles the editor between standard and source edit mode.
22251      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22252      */
22253     toggleSourceEdit : function(sourceEditMode){
22254         
22255         this.sourceEditMode = sourceEditMode === true;
22256         
22257         if(this.sourceEditMode){
22258  
22259             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
22260             
22261         }else{
22262             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22263             //this.iframe.className = '';
22264             this.deferFocus();
22265         }
22266         //this.setSize(this.owner.wrap.getSize());
22267         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22268     },
22269
22270     
22271   
22272
22273     /**
22274      * Protected method that will not generally be called directly. If you need/want
22275      * custom HTML cleanup, this is the method you should override.
22276      * @param {String} html The HTML to be cleaned
22277      * return {String} The cleaned HTML
22278      */
22279     cleanHtml : function(html){
22280         html = String(html);
22281         if(html.length > 5){
22282             if(Roo.isSafari){ // strip safari nonsense
22283                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22284             }
22285         }
22286         if(html == '&nbsp;'){
22287             html = '';
22288         }
22289         return html;
22290     },
22291
22292     /**
22293      * HTML Editor -> Textarea
22294      * Protected method that will not generally be called directly. Syncs the contents
22295      * of the editor iframe with the textarea.
22296      */
22297     syncValue : function(){
22298         if(this.initialized){
22299             var bd = (this.doc.body || this.doc.documentElement);
22300             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22301             var html = bd.innerHTML;
22302             if(Roo.isSafari){
22303                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22304                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22305                 if(m && m[1]){
22306                     html = '<div style="'+m[0]+'">' + html + '</div>';
22307                 }
22308             }
22309             html = this.cleanHtml(html);
22310             // fix up the special chars.. normaly like back quotes in word...
22311             // however we do not want to do this with chinese..
22312             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22313                 var cc = b.charCodeAt();
22314                 if (
22315                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22316                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22317                     (cc >= 0xf900 && cc < 0xfb00 )
22318                 ) {
22319                         return b;
22320                 }
22321                 return "&#"+cc+";" 
22322             });
22323             if(this.owner.fireEvent('beforesync', this, html) !== false){
22324                 this.el.dom.value = html;
22325                 this.owner.fireEvent('sync', this, html);
22326             }
22327         }
22328     },
22329
22330     /**
22331      * Protected method that will not generally be called directly. Pushes the value of the textarea
22332      * into the iframe editor.
22333      */
22334     pushValue : function(){
22335         if(this.initialized){
22336             var v = this.el.dom.value.trim();
22337             
22338 //            if(v.length < 1){
22339 //                v = '&#160;';
22340 //            }
22341             
22342             if(this.owner.fireEvent('beforepush', this, v) !== false){
22343                 var d = (this.doc.body || this.doc.documentElement);
22344                 d.innerHTML = v;
22345                 this.cleanUpPaste();
22346                 this.el.dom.value = d.innerHTML;
22347                 this.owner.fireEvent('push', this, v);
22348             }
22349         }
22350     },
22351
22352     // private
22353     deferFocus : function(){
22354         this.focus.defer(10, this);
22355     },
22356
22357     // doc'ed in Field
22358     focus : function(){
22359         if(this.win && !this.sourceEditMode){
22360             this.win.focus();
22361         }else{
22362             this.el.focus();
22363         }
22364     },
22365     
22366     assignDocWin: function()
22367     {
22368         var iframe = this.iframe;
22369         
22370          if(Roo.isIE){
22371             this.doc = iframe.contentWindow.document;
22372             this.win = iframe.contentWindow;
22373         } else {
22374 //            if (!Roo.get(this.frameId)) {
22375 //                return;
22376 //            }
22377 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22378 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22379             
22380             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22381                 return;
22382             }
22383             
22384             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22385             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22386         }
22387     },
22388     
22389     // private
22390     initEditor : function(){
22391         //console.log("INIT EDITOR");
22392         this.assignDocWin();
22393         
22394         
22395         
22396         this.doc.designMode="on";
22397         this.doc.open();
22398         this.doc.write(this.getDocMarkup());
22399         this.doc.close();
22400         
22401         var dbody = (this.doc.body || this.doc.documentElement);
22402         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22403         // this copies styles from the containing element into thsi one..
22404         // not sure why we need all of this..
22405         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22406         
22407         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22408         //ss['background-attachment'] = 'fixed'; // w3c
22409         dbody.bgProperties = 'fixed'; // ie
22410         //Roo.DomHelper.applyStyles(dbody, ss);
22411         Roo.EventManager.on(this.doc, {
22412             //'mousedown': this.onEditorEvent,
22413             'mouseup': this.onEditorEvent,
22414             'dblclick': this.onEditorEvent,
22415             'click': this.onEditorEvent,
22416             'keyup': this.onEditorEvent,
22417             buffer:100,
22418             scope: this
22419         });
22420         if(Roo.isGecko){
22421             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22422         }
22423         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22424             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22425         }
22426         this.initialized = true;
22427
22428         this.owner.fireEvent('initialize', this);
22429         this.pushValue();
22430     },
22431
22432     // private
22433     onDestroy : function(){
22434         
22435         
22436         
22437         if(this.rendered){
22438             
22439             //for (var i =0; i < this.toolbars.length;i++) {
22440             //    // fixme - ask toolbars for heights?
22441             //    this.toolbars[i].onDestroy();
22442            // }
22443             
22444             //this.wrap.dom.innerHTML = '';
22445             //this.wrap.remove();
22446         }
22447     },
22448
22449     // private
22450     onFirstFocus : function(){
22451         
22452         this.assignDocWin();
22453         
22454         
22455         this.activated = true;
22456          
22457     
22458         if(Roo.isGecko){ // prevent silly gecko errors
22459             this.win.focus();
22460             var s = this.win.getSelection();
22461             if(!s.focusNode || s.focusNode.nodeType != 3){
22462                 var r = s.getRangeAt(0);
22463                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22464                 r.collapse(true);
22465                 this.deferFocus();
22466             }
22467             try{
22468                 this.execCmd('useCSS', true);
22469                 this.execCmd('styleWithCSS', false);
22470             }catch(e){}
22471         }
22472         this.owner.fireEvent('activate', this);
22473     },
22474
22475     // private
22476     adjustFont: function(btn){
22477         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22478         //if(Roo.isSafari){ // safari
22479         //    adjust *= 2;
22480        // }
22481         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22482         if(Roo.isSafari){ // safari
22483             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22484             v =  (v < 10) ? 10 : v;
22485             v =  (v > 48) ? 48 : v;
22486             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22487             
22488         }
22489         
22490         
22491         v = Math.max(1, v+adjust);
22492         
22493         this.execCmd('FontSize', v  );
22494     },
22495
22496     onEditorEvent : function(e)
22497     {
22498         this.owner.fireEvent('editorevent', this, e);
22499       //  this.updateToolbar();
22500         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22501     },
22502
22503     insertTag : function(tg)
22504     {
22505         // could be a bit smarter... -> wrap the current selected tRoo..
22506         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22507             
22508             range = this.createRange(this.getSelection());
22509             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22510             wrappingNode.appendChild(range.extractContents());
22511             range.insertNode(wrappingNode);
22512
22513             return;
22514             
22515             
22516             
22517         }
22518         this.execCmd("formatblock",   tg);
22519         
22520     },
22521     
22522     insertText : function(txt)
22523     {
22524         
22525         
22526         var range = this.createRange();
22527         range.deleteContents();
22528                //alert(Sender.getAttribute('label'));
22529                
22530         range.insertNode(this.doc.createTextNode(txt));
22531     } ,
22532     
22533      
22534
22535     /**
22536      * Executes a Midas editor command on the editor document and performs necessary focus and
22537      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22538      * @param {String} cmd The Midas command
22539      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22540      */
22541     relayCmd : function(cmd, value){
22542         this.win.focus();
22543         this.execCmd(cmd, value);
22544         this.owner.fireEvent('editorevent', this);
22545         //this.updateToolbar();
22546         this.owner.deferFocus();
22547     },
22548
22549     /**
22550      * Executes a Midas editor command directly on the editor document.
22551      * For visual commands, you should use {@link #relayCmd} instead.
22552      * <b>This should only be called after the editor is initialized.</b>
22553      * @param {String} cmd The Midas command
22554      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22555      */
22556     execCmd : function(cmd, value){
22557         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22558         this.syncValue();
22559     },
22560  
22561  
22562    
22563     /**
22564      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22565      * to insert tRoo.
22566      * @param {String} text | dom node.. 
22567      */
22568     insertAtCursor : function(text)
22569     {
22570         
22571         if(!this.activated){
22572             return;
22573         }
22574         /*
22575         if(Roo.isIE){
22576             this.win.focus();
22577             var r = this.doc.selection.createRange();
22578             if(r){
22579                 r.collapse(true);
22580                 r.pasteHTML(text);
22581                 this.syncValue();
22582                 this.deferFocus();
22583             
22584             }
22585             return;
22586         }
22587         */
22588         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22589             this.win.focus();
22590             
22591             
22592             // from jquery ui (MIT licenced)
22593             var range, node;
22594             var win = this.win;
22595             
22596             if (win.getSelection && win.getSelection().getRangeAt) {
22597                 range = win.getSelection().getRangeAt(0);
22598                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22599                 range.insertNode(node);
22600             } else if (win.document.selection && win.document.selection.createRange) {
22601                 // no firefox support
22602                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22603                 win.document.selection.createRange().pasteHTML(txt);
22604             } else {
22605                 // no firefox support
22606                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22607                 this.execCmd('InsertHTML', txt);
22608             } 
22609             
22610             this.syncValue();
22611             
22612             this.deferFocus();
22613         }
22614     },
22615  // private
22616     mozKeyPress : function(e){
22617         if(e.ctrlKey){
22618             var c = e.getCharCode(), cmd;
22619           
22620             if(c > 0){
22621                 c = String.fromCharCode(c).toLowerCase();
22622                 switch(c){
22623                     case 'b':
22624                         cmd = 'bold';
22625                         break;
22626                     case 'i':
22627                         cmd = 'italic';
22628                         break;
22629                     
22630                     case 'u':
22631                         cmd = 'underline';
22632                         break;
22633                     
22634                     case 'v':
22635                         this.cleanUpPaste.defer(100, this);
22636                         return;
22637                         
22638                 }
22639                 if(cmd){
22640                     this.win.focus();
22641                     this.execCmd(cmd);
22642                     this.deferFocus();
22643                     e.preventDefault();
22644                 }
22645                 
22646             }
22647         }
22648     },
22649
22650     // private
22651     fixKeys : function(){ // load time branching for fastest keydown performance
22652         if(Roo.isIE){
22653             return function(e){
22654                 var k = e.getKey(), r;
22655                 if(k == e.TAB){
22656                     e.stopEvent();
22657                     r = this.doc.selection.createRange();
22658                     if(r){
22659                         r.collapse(true);
22660                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22661                         this.deferFocus();
22662                     }
22663                     return;
22664                 }
22665                 
22666                 if(k == e.ENTER){
22667                     r = this.doc.selection.createRange();
22668                     if(r){
22669                         var target = r.parentElement();
22670                         if(!target || target.tagName.toLowerCase() != 'li'){
22671                             e.stopEvent();
22672                             r.pasteHTML('<br />');
22673                             r.collapse(false);
22674                             r.select();
22675                         }
22676                     }
22677                 }
22678                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22679                     this.cleanUpPaste.defer(100, this);
22680                     return;
22681                 }
22682                 
22683                 
22684             };
22685         }else if(Roo.isOpera){
22686             return function(e){
22687                 var k = e.getKey();
22688                 if(k == e.TAB){
22689                     e.stopEvent();
22690                     this.win.focus();
22691                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22692                     this.deferFocus();
22693                 }
22694                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22695                     this.cleanUpPaste.defer(100, this);
22696                     return;
22697                 }
22698                 
22699             };
22700         }else if(Roo.isSafari){
22701             return function(e){
22702                 var k = e.getKey();
22703                 
22704                 if(k == e.TAB){
22705                     e.stopEvent();
22706                     this.execCmd('InsertText','\t');
22707                     this.deferFocus();
22708                     return;
22709                 }
22710                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22711                     this.cleanUpPaste.defer(100, this);
22712                     return;
22713                 }
22714                 
22715              };
22716         }
22717     }(),
22718     
22719     getAllAncestors: function()
22720     {
22721         var p = this.getSelectedNode();
22722         var a = [];
22723         if (!p) {
22724             a.push(p); // push blank onto stack..
22725             p = this.getParentElement();
22726         }
22727         
22728         
22729         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22730             a.push(p);
22731             p = p.parentNode;
22732         }
22733         a.push(this.doc.body);
22734         return a;
22735     },
22736     lastSel : false,
22737     lastSelNode : false,
22738     
22739     
22740     getSelection : function() 
22741     {
22742         this.assignDocWin();
22743         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22744     },
22745     
22746     getSelectedNode: function() 
22747     {
22748         // this may only work on Gecko!!!
22749         
22750         // should we cache this!!!!
22751         
22752         
22753         
22754          
22755         var range = this.createRange(this.getSelection()).cloneRange();
22756         
22757         if (Roo.isIE) {
22758             var parent = range.parentElement();
22759             while (true) {
22760                 var testRange = range.duplicate();
22761                 testRange.moveToElementText(parent);
22762                 if (testRange.inRange(range)) {
22763                     break;
22764                 }
22765                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22766                     break;
22767                 }
22768                 parent = parent.parentElement;
22769             }
22770             return parent;
22771         }
22772         
22773         // is ancestor a text element.
22774         var ac =  range.commonAncestorContainer;
22775         if (ac.nodeType == 3) {
22776             ac = ac.parentNode;
22777         }
22778         
22779         var ar = ac.childNodes;
22780          
22781         var nodes = [];
22782         var other_nodes = [];
22783         var has_other_nodes = false;
22784         for (var i=0;i<ar.length;i++) {
22785             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22786                 continue;
22787             }
22788             // fullly contained node.
22789             
22790             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22791                 nodes.push(ar[i]);
22792                 continue;
22793             }
22794             
22795             // probably selected..
22796             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22797                 other_nodes.push(ar[i]);
22798                 continue;
22799             }
22800             // outer..
22801             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22802                 continue;
22803             }
22804             
22805             
22806             has_other_nodes = true;
22807         }
22808         if (!nodes.length && other_nodes.length) {
22809             nodes= other_nodes;
22810         }
22811         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22812             return false;
22813         }
22814         
22815         return nodes[0];
22816     },
22817     createRange: function(sel)
22818     {
22819         // this has strange effects when using with 
22820         // top toolbar - not sure if it's a great idea.
22821         //this.editor.contentWindow.focus();
22822         if (typeof sel != "undefined") {
22823             try {
22824                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22825             } catch(e) {
22826                 return this.doc.createRange();
22827             }
22828         } else {
22829             return this.doc.createRange();
22830         }
22831     },
22832     getParentElement: function()
22833     {
22834         
22835         this.assignDocWin();
22836         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22837         
22838         var range = this.createRange(sel);
22839          
22840         try {
22841             var p = range.commonAncestorContainer;
22842             while (p.nodeType == 3) { // text node
22843                 p = p.parentNode;
22844             }
22845             return p;
22846         } catch (e) {
22847             return null;
22848         }
22849     
22850     },
22851     /***
22852      *
22853      * Range intersection.. the hard stuff...
22854      *  '-1' = before
22855      *  '0' = hits..
22856      *  '1' = after.
22857      *         [ -- selected range --- ]
22858      *   [fail]                        [fail]
22859      *
22860      *    basically..
22861      *      if end is before start or  hits it. fail.
22862      *      if start is after end or hits it fail.
22863      *
22864      *   if either hits (but other is outside. - then it's not 
22865      *   
22866      *    
22867      **/
22868     
22869     
22870     // @see http://www.thismuchiknow.co.uk/?p=64.
22871     rangeIntersectsNode : function(range, node)
22872     {
22873         var nodeRange = node.ownerDocument.createRange();
22874         try {
22875             nodeRange.selectNode(node);
22876         } catch (e) {
22877             nodeRange.selectNodeContents(node);
22878         }
22879     
22880         var rangeStartRange = range.cloneRange();
22881         rangeStartRange.collapse(true);
22882     
22883         var rangeEndRange = range.cloneRange();
22884         rangeEndRange.collapse(false);
22885     
22886         var nodeStartRange = nodeRange.cloneRange();
22887         nodeStartRange.collapse(true);
22888     
22889         var nodeEndRange = nodeRange.cloneRange();
22890         nodeEndRange.collapse(false);
22891     
22892         return rangeStartRange.compareBoundaryPoints(
22893                  Range.START_TO_START, nodeEndRange) == -1 &&
22894                rangeEndRange.compareBoundaryPoints(
22895                  Range.START_TO_START, nodeStartRange) == 1;
22896         
22897          
22898     },
22899     rangeCompareNode : function(range, node)
22900     {
22901         var nodeRange = node.ownerDocument.createRange();
22902         try {
22903             nodeRange.selectNode(node);
22904         } catch (e) {
22905             nodeRange.selectNodeContents(node);
22906         }
22907         
22908         
22909         range.collapse(true);
22910     
22911         nodeRange.collapse(true);
22912      
22913         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22914         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22915          
22916         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22917         
22918         var nodeIsBefore   =  ss == 1;
22919         var nodeIsAfter    = ee == -1;
22920         
22921         if (nodeIsBefore && nodeIsAfter) {
22922             return 0; // outer
22923         }
22924         if (!nodeIsBefore && nodeIsAfter) {
22925             return 1; //right trailed.
22926         }
22927         
22928         if (nodeIsBefore && !nodeIsAfter) {
22929             return 2;  // left trailed.
22930         }
22931         // fully contined.
22932         return 3;
22933     },
22934
22935     // private? - in a new class?
22936     cleanUpPaste :  function()
22937     {
22938         // cleans up the whole document..
22939         Roo.log('cleanuppaste');
22940         
22941         this.cleanUpChildren(this.doc.body);
22942         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22943         if (clean != this.doc.body.innerHTML) {
22944             this.doc.body.innerHTML = clean;
22945         }
22946         
22947     },
22948     
22949     cleanWordChars : function(input) {// change the chars to hex code
22950         var he = Roo.HtmlEditorCore;
22951         
22952         var output = input;
22953         Roo.each(he.swapCodes, function(sw) { 
22954             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22955             
22956             output = output.replace(swapper, sw[1]);
22957         });
22958         
22959         return output;
22960     },
22961     
22962     
22963     cleanUpChildren : function (n)
22964     {
22965         if (!n.childNodes.length) {
22966             return;
22967         }
22968         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22969            this.cleanUpChild(n.childNodes[i]);
22970         }
22971     },
22972     
22973     
22974         
22975     
22976     cleanUpChild : function (node)
22977     {
22978         var ed = this;
22979         //console.log(node);
22980         if (node.nodeName == "#text") {
22981             // clean up silly Windows -- stuff?
22982             return; 
22983         }
22984         if (node.nodeName == "#comment") {
22985             node.parentNode.removeChild(node);
22986             // clean up silly Windows -- stuff?
22987             return; 
22988         }
22989         var lcname = node.tagName.toLowerCase();
22990         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22991         // whitelist of tags..
22992         
22993         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22994             // remove node.
22995             node.parentNode.removeChild(node);
22996             return;
22997             
22998         }
22999         
23000         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
23001         
23002         // remove <a name=....> as rendering on yahoo mailer is borked with this.
23003         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
23004         
23005         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
23006         //    remove_keep_children = true;
23007         //}
23008         
23009         if (remove_keep_children) {
23010             this.cleanUpChildren(node);
23011             // inserts everything just before this node...
23012             while (node.childNodes.length) {
23013                 var cn = node.childNodes[0];
23014                 node.removeChild(cn);
23015                 node.parentNode.insertBefore(cn, node);
23016             }
23017             node.parentNode.removeChild(node);
23018             return;
23019         }
23020         
23021         if (!node.attributes || !node.attributes.length) {
23022             this.cleanUpChildren(node);
23023             return;
23024         }
23025         
23026         function cleanAttr(n,v)
23027         {
23028             
23029             if (v.match(/^\./) || v.match(/^\//)) {
23030                 return;
23031             }
23032             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
23033                 return;
23034             }
23035             if (v.match(/^#/)) {
23036                 return;
23037             }
23038 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
23039             node.removeAttribute(n);
23040             
23041         }
23042         
23043         var cwhite = this.cwhite;
23044         var cblack = this.cblack;
23045             
23046         function cleanStyle(n,v)
23047         {
23048             if (v.match(/expression/)) { //XSS?? should we even bother..
23049                 node.removeAttribute(n);
23050                 return;
23051             }
23052             
23053             var parts = v.split(/;/);
23054             var clean = [];
23055             
23056             Roo.each(parts, function(p) {
23057                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
23058                 if (!p.length) {
23059                     return true;
23060                 }
23061                 var l = p.split(':').shift().replace(/\s+/g,'');
23062                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
23063                 
23064                 if ( cwhite.length && cblack.indexOf(l) > -1) {
23065 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23066                     //node.removeAttribute(n);
23067                     return true;
23068                 }
23069                 //Roo.log()
23070                 // only allow 'c whitelisted system attributes'
23071                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
23072 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23073                     //node.removeAttribute(n);
23074                     return true;
23075                 }
23076                 
23077                 
23078                  
23079                 
23080                 clean.push(p);
23081                 return true;
23082             });
23083             if (clean.length) { 
23084                 node.setAttribute(n, clean.join(';'));
23085             } else {
23086                 node.removeAttribute(n);
23087             }
23088             
23089         }
23090         
23091         
23092         for (var i = node.attributes.length-1; i > -1 ; i--) {
23093             var a = node.attributes[i];
23094             //console.log(a);
23095             
23096             if (a.name.toLowerCase().substr(0,2)=='on')  {
23097                 node.removeAttribute(a.name);
23098                 continue;
23099             }
23100             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
23101                 node.removeAttribute(a.name);
23102                 continue;
23103             }
23104             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
23105                 cleanAttr(a.name,a.value); // fixme..
23106                 continue;
23107             }
23108             if (a.name == 'style') {
23109                 cleanStyle(a.name,a.value);
23110                 continue;
23111             }
23112             /// clean up MS crap..
23113             // tecnically this should be a list of valid class'es..
23114             
23115             
23116             if (a.name == 'class') {
23117                 if (a.value.match(/^Mso/)) {
23118                     node.className = '';
23119                 }
23120                 
23121                 if (a.value.match(/^body$/)) {
23122                     node.className = '';
23123                 }
23124                 continue;
23125             }
23126             
23127             // style cleanup!?
23128             // class cleanup?
23129             
23130         }
23131         
23132         
23133         this.cleanUpChildren(node);
23134         
23135         
23136     },
23137     
23138     /**
23139      * Clean up MS wordisms...
23140      */
23141     cleanWord : function(node)
23142     {
23143         
23144         
23145         if (!node) {
23146             this.cleanWord(this.doc.body);
23147             return;
23148         }
23149         if (node.nodeName == "#text") {
23150             // clean up silly Windows -- stuff?
23151             return; 
23152         }
23153         if (node.nodeName == "#comment") {
23154             node.parentNode.removeChild(node);
23155             // clean up silly Windows -- stuff?
23156             return; 
23157         }
23158         
23159         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
23160             node.parentNode.removeChild(node);
23161             return;
23162         }
23163         
23164         // remove - but keep children..
23165         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
23166             while (node.childNodes.length) {
23167                 var cn = node.childNodes[0];
23168                 node.removeChild(cn);
23169                 node.parentNode.insertBefore(cn, node);
23170             }
23171             node.parentNode.removeChild(node);
23172             this.iterateChildren(node, this.cleanWord);
23173             return;
23174         }
23175         // clean styles
23176         if (node.className.length) {
23177             
23178             var cn = node.className.split(/\W+/);
23179             var cna = [];
23180             Roo.each(cn, function(cls) {
23181                 if (cls.match(/Mso[a-zA-Z]+/)) {
23182                     return;
23183                 }
23184                 cna.push(cls);
23185             });
23186             node.className = cna.length ? cna.join(' ') : '';
23187             if (!cna.length) {
23188                 node.removeAttribute("class");
23189             }
23190         }
23191         
23192         if (node.hasAttribute("lang")) {
23193             node.removeAttribute("lang");
23194         }
23195         
23196         if (node.hasAttribute("style")) {
23197             
23198             var styles = node.getAttribute("style").split(";");
23199             var nstyle = [];
23200             Roo.each(styles, function(s) {
23201                 if (!s.match(/:/)) {
23202                     return;
23203                 }
23204                 var kv = s.split(":");
23205                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
23206                     return;
23207                 }
23208                 // what ever is left... we allow.
23209                 nstyle.push(s);
23210             });
23211             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23212             if (!nstyle.length) {
23213                 node.removeAttribute('style');
23214             }
23215         }
23216         this.iterateChildren(node, this.cleanWord);
23217         
23218         
23219         
23220     },
23221     /**
23222      * iterateChildren of a Node, calling fn each time, using this as the scole..
23223      * @param {DomNode} node node to iterate children of.
23224      * @param {Function} fn method of this class to call on each item.
23225      */
23226     iterateChildren : function(node, fn)
23227     {
23228         if (!node.childNodes.length) {
23229                 return;
23230         }
23231         for (var i = node.childNodes.length-1; i > -1 ; i--) {
23232            fn.call(this, node.childNodes[i])
23233         }
23234     },
23235     
23236     
23237     /**
23238      * cleanTableWidths.
23239      *
23240      * Quite often pasting from word etc.. results in tables with column and widths.
23241      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23242      *
23243      */
23244     cleanTableWidths : function(node)
23245     {
23246          
23247          
23248         if (!node) {
23249             this.cleanTableWidths(this.doc.body);
23250             return;
23251         }
23252         
23253         // ignore list...
23254         if (node.nodeName == "#text" || node.nodeName == "#comment") {
23255             return; 
23256         }
23257         Roo.log(node.tagName);
23258         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23259             this.iterateChildren(node, this.cleanTableWidths);
23260             return;
23261         }
23262         if (node.hasAttribute('width')) {
23263             node.removeAttribute('width');
23264         }
23265         
23266          
23267         if (node.hasAttribute("style")) {
23268             // pretty basic...
23269             
23270             var styles = node.getAttribute("style").split(";");
23271             var nstyle = [];
23272             Roo.each(styles, function(s) {
23273                 if (!s.match(/:/)) {
23274                     return;
23275                 }
23276                 var kv = s.split(":");
23277                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23278                     return;
23279                 }
23280                 // what ever is left... we allow.
23281                 nstyle.push(s);
23282             });
23283             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23284             if (!nstyle.length) {
23285                 node.removeAttribute('style');
23286             }
23287         }
23288         
23289         this.iterateChildren(node, this.cleanTableWidths);
23290         
23291         
23292     },
23293     
23294     
23295     
23296     
23297     domToHTML : function(currentElement, depth, nopadtext) {
23298         
23299         depth = depth || 0;
23300         nopadtext = nopadtext || false;
23301     
23302         if (!currentElement) {
23303             return this.domToHTML(this.doc.body);
23304         }
23305         
23306         //Roo.log(currentElement);
23307         var j;
23308         var allText = false;
23309         var nodeName = currentElement.nodeName;
23310         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23311         
23312         if  (nodeName == '#text') {
23313             
23314             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23315         }
23316         
23317         
23318         var ret = '';
23319         if (nodeName != 'BODY') {
23320              
23321             var i = 0;
23322             // Prints the node tagName, such as <A>, <IMG>, etc
23323             if (tagName) {
23324                 var attr = [];
23325                 for(i = 0; i < currentElement.attributes.length;i++) {
23326                     // quoting?
23327                     var aname = currentElement.attributes.item(i).name;
23328                     if (!currentElement.attributes.item(i).value.length) {
23329                         continue;
23330                     }
23331                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23332                 }
23333                 
23334                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23335             } 
23336             else {
23337                 
23338                 // eack
23339             }
23340         } else {
23341             tagName = false;
23342         }
23343         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23344             return ret;
23345         }
23346         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23347             nopadtext = true;
23348         }
23349         
23350         
23351         // Traverse the tree
23352         i = 0;
23353         var currentElementChild = currentElement.childNodes.item(i);
23354         var allText = true;
23355         var innerHTML  = '';
23356         lastnode = '';
23357         while (currentElementChild) {
23358             // Formatting code (indent the tree so it looks nice on the screen)
23359             var nopad = nopadtext;
23360             if (lastnode == 'SPAN') {
23361                 nopad  = true;
23362             }
23363             // text
23364             if  (currentElementChild.nodeName == '#text') {
23365                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23366                 toadd = nopadtext ? toadd : toadd.trim();
23367                 if (!nopad && toadd.length > 80) {
23368                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23369                 }
23370                 innerHTML  += toadd;
23371                 
23372                 i++;
23373                 currentElementChild = currentElement.childNodes.item(i);
23374                 lastNode = '';
23375                 continue;
23376             }
23377             allText = false;
23378             
23379             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23380                 
23381             // Recursively traverse the tree structure of the child node
23382             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23383             lastnode = currentElementChild.nodeName;
23384             i++;
23385             currentElementChild=currentElement.childNodes.item(i);
23386         }
23387         
23388         ret += innerHTML;
23389         
23390         if (!allText) {
23391                 // The remaining code is mostly for formatting the tree
23392             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23393         }
23394         
23395         
23396         if (tagName) {
23397             ret+= "</"+tagName+">";
23398         }
23399         return ret;
23400         
23401     },
23402         
23403     applyBlacklists : function()
23404     {
23405         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23406         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23407         
23408         this.white = [];
23409         this.black = [];
23410         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23411             if (b.indexOf(tag) > -1) {
23412                 return;
23413             }
23414             this.white.push(tag);
23415             
23416         }, this);
23417         
23418         Roo.each(w, function(tag) {
23419             if (b.indexOf(tag) > -1) {
23420                 return;
23421             }
23422             if (this.white.indexOf(tag) > -1) {
23423                 return;
23424             }
23425             this.white.push(tag);
23426             
23427         }, this);
23428         
23429         
23430         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23431             if (w.indexOf(tag) > -1) {
23432                 return;
23433             }
23434             this.black.push(tag);
23435             
23436         }, this);
23437         
23438         Roo.each(b, function(tag) {
23439             if (w.indexOf(tag) > -1) {
23440                 return;
23441             }
23442             if (this.black.indexOf(tag) > -1) {
23443                 return;
23444             }
23445             this.black.push(tag);
23446             
23447         }, this);
23448         
23449         
23450         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23451         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23452         
23453         this.cwhite = [];
23454         this.cblack = [];
23455         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23456             if (b.indexOf(tag) > -1) {
23457                 return;
23458             }
23459             this.cwhite.push(tag);
23460             
23461         }, this);
23462         
23463         Roo.each(w, function(tag) {
23464             if (b.indexOf(tag) > -1) {
23465                 return;
23466             }
23467             if (this.cwhite.indexOf(tag) > -1) {
23468                 return;
23469             }
23470             this.cwhite.push(tag);
23471             
23472         }, this);
23473         
23474         
23475         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23476             if (w.indexOf(tag) > -1) {
23477                 return;
23478             }
23479             this.cblack.push(tag);
23480             
23481         }, this);
23482         
23483         Roo.each(b, function(tag) {
23484             if (w.indexOf(tag) > -1) {
23485                 return;
23486             }
23487             if (this.cblack.indexOf(tag) > -1) {
23488                 return;
23489             }
23490             this.cblack.push(tag);
23491             
23492         }, this);
23493     },
23494     
23495     setStylesheets : function(stylesheets)
23496     {
23497         if(typeof(stylesheets) == 'string'){
23498             Roo.get(this.iframe.contentDocument.head).createChild({
23499                 tag : 'link',
23500                 rel : 'stylesheet',
23501                 type : 'text/css',
23502                 href : stylesheets
23503             });
23504             
23505             return;
23506         }
23507         var _this = this;
23508      
23509         Roo.each(stylesheets, function(s) {
23510             if(!s.length){
23511                 return;
23512             }
23513             
23514             Roo.get(_this.iframe.contentDocument.head).createChild({
23515                 tag : 'link',
23516                 rel : 'stylesheet',
23517                 type : 'text/css',
23518                 href : s
23519             });
23520         });
23521
23522         
23523     },
23524     
23525     removeStylesheets : function()
23526     {
23527         var _this = this;
23528         
23529         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23530             s.remove();
23531         });
23532     },
23533     
23534     setStyle : function(style)
23535     {
23536         Roo.get(this.iframe.contentDocument.head).createChild({
23537             tag : 'style',
23538             type : 'text/css',
23539             html : style
23540         });
23541
23542         return;
23543     }
23544     
23545     // hide stuff that is not compatible
23546     /**
23547      * @event blur
23548      * @hide
23549      */
23550     /**
23551      * @event change
23552      * @hide
23553      */
23554     /**
23555      * @event focus
23556      * @hide
23557      */
23558     /**
23559      * @event specialkey
23560      * @hide
23561      */
23562     /**
23563      * @cfg {String} fieldClass @hide
23564      */
23565     /**
23566      * @cfg {String} focusClass @hide
23567      */
23568     /**
23569      * @cfg {String} autoCreate @hide
23570      */
23571     /**
23572      * @cfg {String} inputType @hide
23573      */
23574     /**
23575      * @cfg {String} invalidClass @hide
23576      */
23577     /**
23578      * @cfg {String} invalidText @hide
23579      */
23580     /**
23581      * @cfg {String} msgFx @hide
23582      */
23583     /**
23584      * @cfg {String} validateOnBlur @hide
23585      */
23586 });
23587
23588 Roo.HtmlEditorCore.white = [
23589         'area', 'br', 'img', 'input', 'hr', 'wbr',
23590         
23591        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23592        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23593        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23594        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23595        'table',   'ul',         'xmp', 
23596        
23597        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23598       'thead',   'tr', 
23599      
23600       'dir', 'menu', 'ol', 'ul', 'dl',
23601        
23602       'embed',  'object'
23603 ];
23604
23605
23606 Roo.HtmlEditorCore.black = [
23607     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23608         'applet', // 
23609         'base',   'basefont', 'bgsound', 'blink',  'body', 
23610         'frame',  'frameset', 'head',    'html',   'ilayer', 
23611         'iframe', 'layer',  'link',     'meta',    'object',   
23612         'script', 'style' ,'title',  'xml' // clean later..
23613 ];
23614 Roo.HtmlEditorCore.clean = [
23615     'script', 'style', 'title', 'xml'
23616 ];
23617 Roo.HtmlEditorCore.remove = [
23618     'font'
23619 ];
23620 // attributes..
23621
23622 Roo.HtmlEditorCore.ablack = [
23623     'on'
23624 ];
23625     
23626 Roo.HtmlEditorCore.aclean = [ 
23627     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23628 ];
23629
23630 // protocols..
23631 Roo.HtmlEditorCore.pwhite= [
23632         'http',  'https',  'mailto'
23633 ];
23634
23635 // white listed style attributes.
23636 Roo.HtmlEditorCore.cwhite= [
23637       //  'text-align', /// default is to allow most things..
23638       
23639          
23640 //        'font-size'//??
23641 ];
23642
23643 // black listed style attributes.
23644 Roo.HtmlEditorCore.cblack= [
23645       //  'font-size' -- this can be set by the project 
23646 ];
23647
23648
23649 Roo.HtmlEditorCore.swapCodes   =[ 
23650     [    8211, "--" ], 
23651     [    8212, "--" ], 
23652     [    8216,  "'" ],  
23653     [    8217, "'" ],  
23654     [    8220, '"' ],  
23655     [    8221, '"' ],  
23656     [    8226, "*" ],  
23657     [    8230, "..." ]
23658 ]; 
23659
23660     /*
23661  * - LGPL
23662  *
23663  * HtmlEditor
23664  * 
23665  */
23666
23667 /**
23668  * @class Roo.bootstrap.HtmlEditor
23669  * @extends Roo.bootstrap.TextArea
23670  * Bootstrap HtmlEditor class
23671
23672  * @constructor
23673  * Create a new HtmlEditor
23674  * @param {Object} config The config object
23675  */
23676
23677 Roo.bootstrap.HtmlEditor = function(config){
23678     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23679     if (!this.toolbars) {
23680         this.toolbars = [];
23681     }
23682     
23683     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23684     this.addEvents({
23685             /**
23686              * @event initialize
23687              * Fires when the editor is fully initialized (including the iframe)
23688              * @param {HtmlEditor} this
23689              */
23690             initialize: true,
23691             /**
23692              * @event activate
23693              * Fires when the editor is first receives the focus. Any insertion must wait
23694              * until after this event.
23695              * @param {HtmlEditor} this
23696              */
23697             activate: true,
23698              /**
23699              * @event beforesync
23700              * Fires before the textarea is updated with content from the editor iframe. Return false
23701              * to cancel the sync.
23702              * @param {HtmlEditor} this
23703              * @param {String} html
23704              */
23705             beforesync: true,
23706              /**
23707              * @event beforepush
23708              * Fires before the iframe editor is updated with content from the textarea. Return false
23709              * to cancel the push.
23710              * @param {HtmlEditor} this
23711              * @param {String} html
23712              */
23713             beforepush: true,
23714              /**
23715              * @event sync
23716              * Fires when the textarea is updated with content from the editor iframe.
23717              * @param {HtmlEditor} this
23718              * @param {String} html
23719              */
23720             sync: true,
23721              /**
23722              * @event push
23723              * Fires when the iframe editor is updated with content from the textarea.
23724              * @param {HtmlEditor} this
23725              * @param {String} html
23726              */
23727             push: true,
23728              /**
23729              * @event editmodechange
23730              * Fires when the editor switches edit modes
23731              * @param {HtmlEditor} this
23732              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23733              */
23734             editmodechange: true,
23735             /**
23736              * @event editorevent
23737              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23738              * @param {HtmlEditor} this
23739              */
23740             editorevent: true,
23741             /**
23742              * @event firstfocus
23743              * Fires when on first focus - needed by toolbars..
23744              * @param {HtmlEditor} this
23745              */
23746             firstfocus: true,
23747             /**
23748              * @event autosave
23749              * Auto save the htmlEditor value as a file into Events
23750              * @param {HtmlEditor} this
23751              */
23752             autosave: true,
23753             /**
23754              * @event savedpreview
23755              * preview the saved version of htmlEditor
23756              * @param {HtmlEditor} this
23757              */
23758             savedpreview: true
23759         });
23760 };
23761
23762
23763 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23764     
23765     
23766       /**
23767      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23768      */
23769     toolbars : false,
23770     
23771      /**
23772     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23773     */
23774     btns : [],
23775    
23776      /**
23777      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23778      *                        Roo.resizable.
23779      */
23780     resizable : false,
23781      /**
23782      * @cfg {Number} height (in pixels)
23783      */   
23784     height: 300,
23785    /**
23786      * @cfg {Number} width (in pixels)
23787      */   
23788     width: false,
23789     
23790     /**
23791      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23792      * 
23793      */
23794     stylesheets: false,
23795     
23796     // id of frame..
23797     frameId: false,
23798     
23799     // private properties
23800     validationEvent : false,
23801     deferHeight: true,
23802     initialized : false,
23803     activated : false,
23804     
23805     onFocus : Roo.emptyFn,
23806     iframePad:3,
23807     hideMode:'offsets',
23808     
23809     tbContainer : false,
23810     
23811     bodyCls : '',
23812     
23813     toolbarContainer :function() {
23814         return this.wrap.select('.x-html-editor-tb',true).first();
23815     },
23816
23817     /**
23818      * Protected method that will not generally be called directly. It
23819      * is called when the editor creates its toolbar. Override this method if you need to
23820      * add custom toolbar buttons.
23821      * @param {HtmlEditor} editor
23822      */
23823     createToolbar : function(){
23824         Roo.log('renewing');
23825         Roo.log("create toolbars");
23826         
23827         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23828         this.toolbars[0].render(this.toolbarContainer());
23829         
23830         return;
23831         
23832 //        if (!editor.toolbars || !editor.toolbars.length) {
23833 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23834 //        }
23835 //        
23836 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23837 //            editor.toolbars[i] = Roo.factory(
23838 //                    typeof(editor.toolbars[i]) == 'string' ?
23839 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23840 //                Roo.bootstrap.HtmlEditor);
23841 //            editor.toolbars[i].init(editor);
23842 //        }
23843     },
23844
23845      
23846     // private
23847     onRender : function(ct, position)
23848     {
23849        // Roo.log("Call onRender: " + this.xtype);
23850         var _t = this;
23851         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23852       
23853         this.wrap = this.inputEl().wrap({
23854             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23855         });
23856         
23857         this.editorcore.onRender(ct, position);
23858          
23859         if (this.resizable) {
23860             this.resizeEl = new Roo.Resizable(this.wrap, {
23861                 pinned : true,
23862                 wrap: true,
23863                 dynamic : true,
23864                 minHeight : this.height,
23865                 height: this.height,
23866                 handles : this.resizable,
23867                 width: this.width,
23868                 listeners : {
23869                     resize : function(r, w, h) {
23870                         _t.onResize(w,h); // -something
23871                     }
23872                 }
23873             });
23874             
23875         }
23876         this.createToolbar(this);
23877        
23878         
23879         if(!this.width && this.resizable){
23880             this.setSize(this.wrap.getSize());
23881         }
23882         if (this.resizeEl) {
23883             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23884             // should trigger onReize..
23885         }
23886         
23887     },
23888
23889     // private
23890     onResize : function(w, h)
23891     {
23892         Roo.log('resize: ' +w + ',' + h );
23893         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23894         var ew = false;
23895         var eh = false;
23896         
23897         if(this.inputEl() ){
23898             if(typeof w == 'number'){
23899                 var aw = w - this.wrap.getFrameWidth('lr');
23900                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23901                 ew = aw;
23902             }
23903             if(typeof h == 'number'){
23904                  var tbh = -11;  // fixme it needs to tool bar size!
23905                 for (var i =0; i < this.toolbars.length;i++) {
23906                     // fixme - ask toolbars for heights?
23907                     tbh += this.toolbars[i].el.getHeight();
23908                     //if (this.toolbars[i].footer) {
23909                     //    tbh += this.toolbars[i].footer.el.getHeight();
23910                     //}
23911                 }
23912               
23913                 
23914                 
23915                 
23916                 
23917                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23918                 ah -= 5; // knock a few pixes off for look..
23919                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23920                 var eh = ah;
23921             }
23922         }
23923         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23924         this.editorcore.onResize(ew,eh);
23925         
23926     },
23927
23928     /**
23929      * Toggles the editor between standard and source edit mode.
23930      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23931      */
23932     toggleSourceEdit : function(sourceEditMode)
23933     {
23934         this.editorcore.toggleSourceEdit(sourceEditMode);
23935         
23936         if(this.editorcore.sourceEditMode){
23937             Roo.log('editor - showing textarea');
23938             
23939 //            Roo.log('in');
23940 //            Roo.log(this.syncValue());
23941             this.syncValue();
23942             this.inputEl().removeClass(['hide', 'x-hidden']);
23943             this.inputEl().dom.removeAttribute('tabIndex');
23944             this.inputEl().focus();
23945         }else{
23946             Roo.log('editor - hiding textarea');
23947 //            Roo.log('out')
23948 //            Roo.log(this.pushValue()); 
23949             this.pushValue();
23950             
23951             this.inputEl().addClass(['hide', 'x-hidden']);
23952             this.inputEl().dom.setAttribute('tabIndex', -1);
23953             //this.deferFocus();
23954         }
23955          
23956         if(this.resizable){
23957             this.setSize(this.wrap.getSize());
23958         }
23959         
23960         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23961     },
23962  
23963     // private (for BoxComponent)
23964     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23965
23966     // private (for BoxComponent)
23967     getResizeEl : function(){
23968         return this.wrap;
23969     },
23970
23971     // private (for BoxComponent)
23972     getPositionEl : function(){
23973         return this.wrap;
23974     },
23975
23976     // private
23977     initEvents : function(){
23978         this.originalValue = this.getValue();
23979     },
23980
23981 //    /**
23982 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23983 //     * @method
23984 //     */
23985 //    markInvalid : Roo.emptyFn,
23986 //    /**
23987 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23988 //     * @method
23989 //     */
23990 //    clearInvalid : Roo.emptyFn,
23991
23992     setValue : function(v){
23993         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23994         this.editorcore.pushValue();
23995     },
23996
23997      
23998     // private
23999     deferFocus : function(){
24000         this.focus.defer(10, this);
24001     },
24002
24003     // doc'ed in Field
24004     focus : function(){
24005         this.editorcore.focus();
24006         
24007     },
24008       
24009
24010     // private
24011     onDestroy : function(){
24012         
24013         
24014         
24015         if(this.rendered){
24016             
24017             for (var i =0; i < this.toolbars.length;i++) {
24018                 // fixme - ask toolbars for heights?
24019                 this.toolbars[i].onDestroy();
24020             }
24021             
24022             this.wrap.dom.innerHTML = '';
24023             this.wrap.remove();
24024         }
24025     },
24026
24027     // private
24028     onFirstFocus : function(){
24029         //Roo.log("onFirstFocus");
24030         this.editorcore.onFirstFocus();
24031          for (var i =0; i < this.toolbars.length;i++) {
24032             this.toolbars[i].onFirstFocus();
24033         }
24034         
24035     },
24036     
24037     // private
24038     syncValue : function()
24039     {   
24040         this.editorcore.syncValue();
24041     },
24042     
24043     pushValue : function()
24044     {   
24045         this.editorcore.pushValue();
24046     }
24047      
24048     
24049     // hide stuff that is not compatible
24050     /**
24051      * @event blur
24052      * @hide
24053      */
24054     /**
24055      * @event change
24056      * @hide
24057      */
24058     /**
24059      * @event focus
24060      * @hide
24061      */
24062     /**
24063      * @event specialkey
24064      * @hide
24065      */
24066     /**
24067      * @cfg {String} fieldClass @hide
24068      */
24069     /**
24070      * @cfg {String} focusClass @hide
24071      */
24072     /**
24073      * @cfg {String} autoCreate @hide
24074      */
24075     /**
24076      * @cfg {String} inputType @hide
24077      */
24078      
24079     /**
24080      * @cfg {String} invalidText @hide
24081      */
24082     /**
24083      * @cfg {String} msgFx @hide
24084      */
24085     /**
24086      * @cfg {String} validateOnBlur @hide
24087      */
24088 });
24089  
24090     
24091    
24092    
24093    
24094       
24095 Roo.namespace('Roo.bootstrap.htmleditor');
24096 /**
24097  * @class Roo.bootstrap.HtmlEditorToolbar1
24098  * Basic Toolbar
24099  * 
24100  * @example
24101  * Usage:
24102  *
24103  new Roo.bootstrap.HtmlEditor({
24104     ....
24105     toolbars : [
24106         new Roo.bootstrap.HtmlEditorToolbar1({
24107             disable : { fonts: 1 , format: 1, ..., ... , ...],
24108             btns : [ .... ]
24109         })
24110     }
24111      
24112  * 
24113  * @cfg {Object} disable List of elements to disable..
24114  * @cfg {Array} btns List of additional buttons.
24115  * 
24116  * 
24117  * NEEDS Extra CSS? 
24118  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24119  */
24120  
24121 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
24122 {
24123     
24124     Roo.apply(this, config);
24125     
24126     // default disabled, based on 'good practice'..
24127     this.disable = this.disable || {};
24128     Roo.applyIf(this.disable, {
24129         fontSize : true,
24130         colors : true,
24131         specialElements : true
24132     });
24133     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
24134     
24135     this.editor = config.editor;
24136     this.editorcore = config.editor.editorcore;
24137     
24138     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
24139     
24140     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24141     // dont call parent... till later.
24142 }
24143 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
24144      
24145     bar : true,
24146     
24147     editor : false,
24148     editorcore : false,
24149     
24150     
24151     formats : [
24152         "p" ,  
24153         "h1","h2","h3","h4","h5","h6", 
24154         "pre", "code", 
24155         "abbr", "acronym", "address", "cite", "samp", "var",
24156         'div','span'
24157     ],
24158     
24159     onRender : function(ct, position)
24160     {
24161        // Roo.log("Call onRender: " + this.xtype);
24162         
24163        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
24164        Roo.log(this.el);
24165        this.el.dom.style.marginBottom = '0';
24166        var _this = this;
24167        var editorcore = this.editorcore;
24168        var editor= this.editor;
24169        
24170        var children = [];
24171        var btn = function(id,cmd , toggle, handler, html){
24172        
24173             var  event = toggle ? 'toggle' : 'click';
24174        
24175             var a = {
24176                 size : 'sm',
24177                 xtype: 'Button',
24178                 xns: Roo.bootstrap,
24179                 //glyphicon : id,
24180                 fa: id,
24181                 cmd : id || cmd,
24182                 enableToggle:toggle !== false,
24183                 html : html || '',
24184                 pressed : toggle ? false : null,
24185                 listeners : {}
24186             };
24187             a.listeners[toggle ? 'toggle' : 'click'] = function() {
24188                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
24189             };
24190             children.push(a);
24191             return a;
24192        }
24193        
24194     //    var cb_box = function...
24195         
24196         var style = {
24197                 xtype: 'Button',
24198                 size : 'sm',
24199                 xns: Roo.bootstrap,
24200                 fa : 'font',
24201                 //html : 'submit'
24202                 menu : {
24203                     xtype: 'Menu',
24204                     xns: Roo.bootstrap,
24205                     items:  []
24206                 }
24207         };
24208         Roo.each(this.formats, function(f) {
24209             style.menu.items.push({
24210                 xtype :'MenuItem',
24211                 xns: Roo.bootstrap,
24212                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24213                 tagname : f,
24214                 listeners : {
24215                     click : function()
24216                     {
24217                         editorcore.insertTag(this.tagname);
24218                         editor.focus();
24219                     }
24220                 }
24221                 
24222             });
24223         });
24224         children.push(style);   
24225         
24226         btn('bold',false,true);
24227         btn('italic',false,true);
24228         btn('align-left', 'justifyleft',true);
24229         btn('align-center', 'justifycenter',true);
24230         btn('align-right' , 'justifyright',true);
24231         btn('link', false, false, function(btn) {
24232             //Roo.log("create link?");
24233             var url = prompt(this.createLinkText, this.defaultLinkValue);
24234             if(url && url != 'http:/'+'/'){
24235                 this.editorcore.relayCmd('createlink', url);
24236             }
24237         }),
24238         btn('list','insertunorderedlist',true);
24239         btn('pencil', false,true, function(btn){
24240                 Roo.log(this);
24241                 this.toggleSourceEdit(btn.pressed);
24242         });
24243         
24244         if (this.editor.btns.length > 0) {
24245             for (var i = 0; i<this.editor.btns.length; i++) {
24246                 children.push(this.editor.btns[i]);
24247             }
24248         }
24249         
24250         /*
24251         var cog = {
24252                 xtype: 'Button',
24253                 size : 'sm',
24254                 xns: Roo.bootstrap,
24255                 glyphicon : 'cog',
24256                 //html : 'submit'
24257                 menu : {
24258                     xtype: 'Menu',
24259                     xns: Roo.bootstrap,
24260                     items:  []
24261                 }
24262         };
24263         
24264         cog.menu.items.push({
24265             xtype :'MenuItem',
24266             xns: Roo.bootstrap,
24267             html : Clean styles,
24268             tagname : f,
24269             listeners : {
24270                 click : function()
24271                 {
24272                     editorcore.insertTag(this.tagname);
24273                     editor.focus();
24274                 }
24275             }
24276             
24277         });
24278        */
24279         
24280          
24281        this.xtype = 'NavSimplebar';
24282         
24283         for(var i=0;i< children.length;i++) {
24284             
24285             this.buttons.add(this.addxtypeChild(children[i]));
24286             
24287         }
24288         
24289         editor.on('editorevent', this.updateToolbar, this);
24290     },
24291     onBtnClick : function(id)
24292     {
24293        this.editorcore.relayCmd(id);
24294        this.editorcore.focus();
24295     },
24296     
24297     /**
24298      * Protected method that will not generally be called directly. It triggers
24299      * a toolbar update by reading the markup state of the current selection in the editor.
24300      */
24301     updateToolbar: function(){
24302
24303         if(!this.editorcore.activated){
24304             this.editor.onFirstFocus(); // is this neeed?
24305             return;
24306         }
24307
24308         var btns = this.buttons; 
24309         var doc = this.editorcore.doc;
24310         btns.get('bold').setActive(doc.queryCommandState('bold'));
24311         btns.get('italic').setActive(doc.queryCommandState('italic'));
24312         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24313         
24314         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24315         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24316         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24317         
24318         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24319         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24320          /*
24321         
24322         var ans = this.editorcore.getAllAncestors();
24323         if (this.formatCombo) {
24324             
24325             
24326             var store = this.formatCombo.store;
24327             this.formatCombo.setValue("");
24328             for (var i =0; i < ans.length;i++) {
24329                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24330                     // select it..
24331                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24332                     break;
24333                 }
24334             }
24335         }
24336         
24337         
24338         
24339         // hides menus... - so this cant be on a menu...
24340         Roo.bootstrap.MenuMgr.hideAll();
24341         */
24342         Roo.bootstrap.MenuMgr.hideAll();
24343         //this.editorsyncValue();
24344     },
24345     onFirstFocus: function() {
24346         this.buttons.each(function(item){
24347            item.enable();
24348         });
24349     },
24350     toggleSourceEdit : function(sourceEditMode){
24351         
24352           
24353         if(sourceEditMode){
24354             Roo.log("disabling buttons");
24355            this.buttons.each( function(item){
24356                 if(item.cmd != 'pencil'){
24357                     item.disable();
24358                 }
24359             });
24360           
24361         }else{
24362             Roo.log("enabling buttons");
24363             if(this.editorcore.initialized){
24364                 this.buttons.each( function(item){
24365                     item.enable();
24366                 });
24367             }
24368             
24369         }
24370         Roo.log("calling toggole on editor");
24371         // tell the editor that it's been pressed..
24372         this.editor.toggleSourceEdit(sourceEditMode);
24373        
24374     }
24375 });
24376
24377
24378
24379
24380
24381 /**
24382  * @class Roo.bootstrap.Table.AbstractSelectionModel
24383  * @extends Roo.util.Observable
24384  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24385  * implemented by descendant classes.  This class should not be directly instantiated.
24386  * @constructor
24387  */
24388 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24389     this.locked = false;
24390     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24391 };
24392
24393
24394 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24395     /** @ignore Called by the grid automatically. Do not call directly. */
24396     init : function(grid){
24397         this.grid = grid;
24398         this.initEvents();
24399     },
24400
24401     /**
24402      * Locks the selections.
24403      */
24404     lock : function(){
24405         this.locked = true;
24406     },
24407
24408     /**
24409      * Unlocks the selections.
24410      */
24411     unlock : function(){
24412         this.locked = false;
24413     },
24414
24415     /**
24416      * Returns true if the selections are locked.
24417      * @return {Boolean}
24418      */
24419     isLocked : function(){
24420         return this.locked;
24421     }
24422 });
24423 /**
24424  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24425  * @class Roo.bootstrap.Table.RowSelectionModel
24426  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24427  * It supports multiple selections and keyboard selection/navigation. 
24428  * @constructor
24429  * @param {Object} config
24430  */
24431
24432 Roo.bootstrap.Table.RowSelectionModel = function(config){
24433     Roo.apply(this, config);
24434     this.selections = new Roo.util.MixedCollection(false, function(o){
24435         return o.id;
24436     });
24437
24438     this.last = false;
24439     this.lastActive = false;
24440
24441     this.addEvents({
24442         /**
24443              * @event selectionchange
24444              * Fires when the selection changes
24445              * @param {SelectionModel} this
24446              */
24447             "selectionchange" : true,
24448         /**
24449              * @event afterselectionchange
24450              * Fires after the selection changes (eg. by key press or clicking)
24451              * @param {SelectionModel} this
24452              */
24453             "afterselectionchange" : true,
24454         /**
24455              * @event beforerowselect
24456              * Fires when a row is selected being selected, return false to cancel.
24457              * @param {SelectionModel} this
24458              * @param {Number} rowIndex The selected index
24459              * @param {Boolean} keepExisting False if other selections will be cleared
24460              */
24461             "beforerowselect" : true,
24462         /**
24463              * @event rowselect
24464              * Fires when a row is selected.
24465              * @param {SelectionModel} this
24466              * @param {Number} rowIndex The selected index
24467              * @param {Roo.data.Record} r The record
24468              */
24469             "rowselect" : true,
24470         /**
24471              * @event rowdeselect
24472              * Fires when a row is deselected.
24473              * @param {SelectionModel} this
24474              * @param {Number} rowIndex The selected index
24475              */
24476         "rowdeselect" : true
24477     });
24478     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24479     this.locked = false;
24480  };
24481
24482 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24483     /**
24484      * @cfg {Boolean} singleSelect
24485      * True to allow selection of only one row at a time (defaults to false)
24486      */
24487     singleSelect : false,
24488
24489     // private
24490     initEvents : function()
24491     {
24492
24493         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24494         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24495         //}else{ // allow click to work like normal
24496          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24497         //}
24498         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24499         this.grid.on("rowclick", this.handleMouseDown, this);
24500         
24501         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24502             "up" : function(e){
24503                 if(!e.shiftKey){
24504                     this.selectPrevious(e.shiftKey);
24505                 }else if(this.last !== false && this.lastActive !== false){
24506                     var last = this.last;
24507                     this.selectRange(this.last,  this.lastActive-1);
24508                     this.grid.getView().focusRow(this.lastActive);
24509                     if(last !== false){
24510                         this.last = last;
24511                     }
24512                 }else{
24513                     this.selectFirstRow();
24514                 }
24515                 this.fireEvent("afterselectionchange", this);
24516             },
24517             "down" : function(e){
24518                 if(!e.shiftKey){
24519                     this.selectNext(e.shiftKey);
24520                 }else if(this.last !== false && this.lastActive !== false){
24521                     var last = this.last;
24522                     this.selectRange(this.last,  this.lastActive+1);
24523                     this.grid.getView().focusRow(this.lastActive);
24524                     if(last !== false){
24525                         this.last = last;
24526                     }
24527                 }else{
24528                     this.selectFirstRow();
24529                 }
24530                 this.fireEvent("afterselectionchange", this);
24531             },
24532             scope: this
24533         });
24534         this.grid.store.on('load', function(){
24535             this.selections.clear();
24536         },this);
24537         /*
24538         var view = this.grid.view;
24539         view.on("refresh", this.onRefresh, this);
24540         view.on("rowupdated", this.onRowUpdated, this);
24541         view.on("rowremoved", this.onRemove, this);
24542         */
24543     },
24544
24545     // private
24546     onRefresh : function()
24547     {
24548         var ds = this.grid.store, i, v = this.grid.view;
24549         var s = this.selections;
24550         s.each(function(r){
24551             if((i = ds.indexOfId(r.id)) != -1){
24552                 v.onRowSelect(i);
24553             }else{
24554                 s.remove(r);
24555             }
24556         });
24557     },
24558
24559     // private
24560     onRemove : function(v, index, r){
24561         this.selections.remove(r);
24562     },
24563
24564     // private
24565     onRowUpdated : function(v, index, r){
24566         if(this.isSelected(r)){
24567             v.onRowSelect(index);
24568         }
24569     },
24570
24571     /**
24572      * Select records.
24573      * @param {Array} records The records to select
24574      * @param {Boolean} keepExisting (optional) True to keep existing selections
24575      */
24576     selectRecords : function(records, keepExisting)
24577     {
24578         if(!keepExisting){
24579             this.clearSelections();
24580         }
24581             var ds = this.grid.store;
24582         for(var i = 0, len = records.length; i < len; i++){
24583             this.selectRow(ds.indexOf(records[i]), true);
24584         }
24585     },
24586
24587     /**
24588      * Gets the number of selected rows.
24589      * @return {Number}
24590      */
24591     getCount : function(){
24592         return this.selections.length;
24593     },
24594
24595     /**
24596      * Selects the first row in the grid.
24597      */
24598     selectFirstRow : function(){
24599         this.selectRow(0);
24600     },
24601
24602     /**
24603      * Select the last row.
24604      * @param {Boolean} keepExisting (optional) True to keep existing selections
24605      */
24606     selectLastRow : function(keepExisting){
24607         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24608         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24609     },
24610
24611     /**
24612      * Selects the row immediately following the last selected row.
24613      * @param {Boolean} keepExisting (optional) True to keep existing selections
24614      */
24615     selectNext : function(keepExisting)
24616     {
24617             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24618             this.selectRow(this.last+1, keepExisting);
24619             this.grid.getView().focusRow(this.last);
24620         }
24621     },
24622
24623     /**
24624      * Selects the row that precedes the last selected row.
24625      * @param {Boolean} keepExisting (optional) True to keep existing selections
24626      */
24627     selectPrevious : function(keepExisting){
24628         if(this.last){
24629             this.selectRow(this.last-1, keepExisting);
24630             this.grid.getView().focusRow(this.last);
24631         }
24632     },
24633
24634     /**
24635      * Returns the selected records
24636      * @return {Array} Array of selected records
24637      */
24638     getSelections : function(){
24639         return [].concat(this.selections.items);
24640     },
24641
24642     /**
24643      * Returns the first selected record.
24644      * @return {Record}
24645      */
24646     getSelected : function(){
24647         return this.selections.itemAt(0);
24648     },
24649
24650
24651     /**
24652      * Clears all selections.
24653      */
24654     clearSelections : function(fast)
24655     {
24656         if(this.locked) {
24657             return;
24658         }
24659         if(fast !== true){
24660                 var ds = this.grid.store;
24661             var s = this.selections;
24662             s.each(function(r){
24663                 this.deselectRow(ds.indexOfId(r.id));
24664             }, this);
24665             s.clear();
24666         }else{
24667             this.selections.clear();
24668         }
24669         this.last = false;
24670     },
24671
24672
24673     /**
24674      * Selects all rows.
24675      */
24676     selectAll : function(){
24677         if(this.locked) {
24678             return;
24679         }
24680         this.selections.clear();
24681         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24682             this.selectRow(i, true);
24683         }
24684     },
24685
24686     /**
24687      * Returns True if there is a selection.
24688      * @return {Boolean}
24689      */
24690     hasSelection : function(){
24691         return this.selections.length > 0;
24692     },
24693
24694     /**
24695      * Returns True if the specified row is selected.
24696      * @param {Number/Record} record The record or index of the record to check
24697      * @return {Boolean}
24698      */
24699     isSelected : function(index){
24700             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24701         return (r && this.selections.key(r.id) ? true : false);
24702     },
24703
24704     /**
24705      * Returns True if the specified record id is selected.
24706      * @param {String} id The id of record to check
24707      * @return {Boolean}
24708      */
24709     isIdSelected : function(id){
24710         return (this.selections.key(id) ? true : false);
24711     },
24712
24713
24714     // private
24715     handleMouseDBClick : function(e, t){
24716         
24717     },
24718     // private
24719     handleMouseDown : function(e, t)
24720     {
24721             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24722         if(this.isLocked() || rowIndex < 0 ){
24723             return;
24724         };
24725         if(e.shiftKey && this.last !== false){
24726             var last = this.last;
24727             this.selectRange(last, rowIndex, e.ctrlKey);
24728             this.last = last; // reset the last
24729             t.focus();
24730     
24731         }else{
24732             var isSelected = this.isSelected(rowIndex);
24733             //Roo.log("select row:" + rowIndex);
24734             if(isSelected){
24735                 this.deselectRow(rowIndex);
24736             } else {
24737                         this.selectRow(rowIndex, true);
24738             }
24739     
24740             /*
24741                 if(e.button !== 0 && isSelected){
24742                 alert('rowIndex 2: ' + rowIndex);
24743                     view.focusRow(rowIndex);
24744                 }else if(e.ctrlKey && isSelected){
24745                     this.deselectRow(rowIndex);
24746                 }else if(!isSelected){
24747                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24748                     view.focusRow(rowIndex);
24749                 }
24750             */
24751         }
24752         this.fireEvent("afterselectionchange", this);
24753     },
24754     // private
24755     handleDragableRowClick :  function(grid, rowIndex, e) 
24756     {
24757         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24758             this.selectRow(rowIndex, false);
24759             grid.view.focusRow(rowIndex);
24760              this.fireEvent("afterselectionchange", this);
24761         }
24762     },
24763     
24764     /**
24765      * Selects multiple rows.
24766      * @param {Array} rows Array of the indexes of the row to select
24767      * @param {Boolean} keepExisting (optional) True to keep existing selections
24768      */
24769     selectRows : function(rows, keepExisting){
24770         if(!keepExisting){
24771             this.clearSelections();
24772         }
24773         for(var i = 0, len = rows.length; i < len; i++){
24774             this.selectRow(rows[i], true);
24775         }
24776     },
24777
24778     /**
24779      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24780      * @param {Number} startRow The index of the first row in the range
24781      * @param {Number} endRow The index of the last row in the range
24782      * @param {Boolean} keepExisting (optional) True to retain existing selections
24783      */
24784     selectRange : function(startRow, endRow, keepExisting){
24785         if(this.locked) {
24786             return;
24787         }
24788         if(!keepExisting){
24789             this.clearSelections();
24790         }
24791         if(startRow <= endRow){
24792             for(var i = startRow; i <= endRow; i++){
24793                 this.selectRow(i, true);
24794             }
24795         }else{
24796             for(var i = startRow; i >= endRow; i--){
24797                 this.selectRow(i, true);
24798             }
24799         }
24800     },
24801
24802     /**
24803      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24804      * @param {Number} startRow The index of the first row in the range
24805      * @param {Number} endRow The index of the last row in the range
24806      */
24807     deselectRange : function(startRow, endRow, preventViewNotify){
24808         if(this.locked) {
24809             return;
24810         }
24811         for(var i = startRow; i <= endRow; i++){
24812             this.deselectRow(i, preventViewNotify);
24813         }
24814     },
24815
24816     /**
24817      * Selects a row.
24818      * @param {Number} row The index of the row to select
24819      * @param {Boolean} keepExisting (optional) True to keep existing selections
24820      */
24821     selectRow : function(index, keepExisting, preventViewNotify)
24822     {
24823             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24824             return;
24825         }
24826         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24827             if(!keepExisting || this.singleSelect){
24828                 this.clearSelections();
24829             }
24830             
24831             var r = this.grid.store.getAt(index);
24832             //console.log('selectRow - record id :' + r.id);
24833             
24834             this.selections.add(r);
24835             this.last = this.lastActive = index;
24836             if(!preventViewNotify){
24837                 var proxy = new Roo.Element(
24838                                 this.grid.getRowDom(index)
24839                 );
24840                 proxy.addClass('bg-info info');
24841             }
24842             this.fireEvent("rowselect", this, index, r);
24843             this.fireEvent("selectionchange", this);
24844         }
24845     },
24846
24847     /**
24848      * Deselects a row.
24849      * @param {Number} row The index of the row to deselect
24850      */
24851     deselectRow : function(index, preventViewNotify)
24852     {
24853         if(this.locked) {
24854             return;
24855         }
24856         if(this.last == index){
24857             this.last = false;
24858         }
24859         if(this.lastActive == index){
24860             this.lastActive = false;
24861         }
24862         
24863         var r = this.grid.store.getAt(index);
24864         if (!r) {
24865             return;
24866         }
24867         
24868         this.selections.remove(r);
24869         //.console.log('deselectRow - record id :' + r.id);
24870         if(!preventViewNotify){
24871         
24872             var proxy = new Roo.Element(
24873                 this.grid.getRowDom(index)
24874             );
24875             proxy.removeClass('bg-info info');
24876         }
24877         this.fireEvent("rowdeselect", this, index);
24878         this.fireEvent("selectionchange", this);
24879     },
24880
24881     // private
24882     restoreLast : function(){
24883         if(this._last){
24884             this.last = this._last;
24885         }
24886     },
24887
24888     // private
24889     acceptsNav : function(row, col, cm){
24890         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24891     },
24892
24893     // private
24894     onEditorKey : function(field, e){
24895         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24896         if(k == e.TAB){
24897             e.stopEvent();
24898             ed.completeEdit();
24899             if(e.shiftKey){
24900                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24901             }else{
24902                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24903             }
24904         }else if(k == e.ENTER && !e.ctrlKey){
24905             e.stopEvent();
24906             ed.completeEdit();
24907             if(e.shiftKey){
24908                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24909             }else{
24910                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24911             }
24912         }else if(k == e.ESC){
24913             ed.cancelEdit();
24914         }
24915         if(newCell){
24916             g.startEditing(newCell[0], newCell[1]);
24917         }
24918     }
24919 });
24920 /*
24921  * Based on:
24922  * Ext JS Library 1.1.1
24923  * Copyright(c) 2006-2007, Ext JS, LLC.
24924  *
24925  * Originally Released Under LGPL - original licence link has changed is not relivant.
24926  *
24927  * Fork - LGPL
24928  * <script type="text/javascript">
24929  */
24930  
24931 /**
24932  * @class Roo.bootstrap.PagingToolbar
24933  * @extends Roo.bootstrap.NavSimplebar
24934  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24935  * @constructor
24936  * Create a new PagingToolbar
24937  * @param {Object} config The config object
24938  * @param {Roo.data.Store} store
24939  */
24940 Roo.bootstrap.PagingToolbar = function(config)
24941 {
24942     // old args format still supported... - xtype is prefered..
24943         // created from xtype...
24944     
24945     this.ds = config.dataSource;
24946     
24947     if (config.store && !this.ds) {
24948         this.store= Roo.factory(config.store, Roo.data);
24949         this.ds = this.store;
24950         this.ds.xmodule = this.xmodule || false;
24951     }
24952     
24953     this.toolbarItems = [];
24954     if (config.items) {
24955         this.toolbarItems = config.items;
24956     }
24957     
24958     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24959     
24960     this.cursor = 0;
24961     
24962     if (this.ds) { 
24963         this.bind(this.ds);
24964     }
24965     
24966     if (Roo.bootstrap.version == 4) {
24967         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24968     } else {
24969         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24970     }
24971     
24972 };
24973
24974 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24975     /**
24976      * @cfg {Roo.data.Store} dataSource
24977      * The underlying data store providing the paged data
24978      */
24979     /**
24980      * @cfg {String/HTMLElement/Element} container
24981      * container The id or element that will contain the toolbar
24982      */
24983     /**
24984      * @cfg {Boolean} displayInfo
24985      * True to display the displayMsg (defaults to false)
24986      */
24987     /**
24988      * @cfg {Number} pageSize
24989      * The number of records to display per page (defaults to 20)
24990      */
24991     pageSize: 20,
24992     /**
24993      * @cfg {String} displayMsg
24994      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24995      */
24996     displayMsg : 'Displaying {0} - {1} of {2}',
24997     /**
24998      * @cfg {String} emptyMsg
24999      * The message to display when no records are found (defaults to "No data to display")
25000      */
25001     emptyMsg : 'No data to display',
25002     /**
25003      * Customizable piece of the default paging text (defaults to "Page")
25004      * @type String
25005      */
25006     beforePageText : "Page",
25007     /**
25008      * Customizable piece of the default paging text (defaults to "of %0")
25009      * @type String
25010      */
25011     afterPageText : "of {0}",
25012     /**
25013      * Customizable piece of the default paging text (defaults to "First Page")
25014      * @type String
25015      */
25016     firstText : "First Page",
25017     /**
25018      * Customizable piece of the default paging text (defaults to "Previous Page")
25019      * @type String
25020      */
25021     prevText : "Previous Page",
25022     /**
25023      * Customizable piece of the default paging text (defaults to "Next Page")
25024      * @type String
25025      */
25026     nextText : "Next Page",
25027     /**
25028      * Customizable piece of the default paging text (defaults to "Last Page")
25029      * @type String
25030      */
25031     lastText : "Last Page",
25032     /**
25033      * Customizable piece of the default paging text (defaults to "Refresh")
25034      * @type String
25035      */
25036     refreshText : "Refresh",
25037
25038     buttons : false,
25039     // private
25040     onRender : function(ct, position) 
25041     {
25042         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
25043         this.navgroup.parentId = this.id;
25044         this.navgroup.onRender(this.el, null);
25045         // add the buttons to the navgroup
25046         
25047         if(this.displayInfo){
25048             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
25049             this.displayEl = this.el.select('.x-paging-info', true).first();
25050 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
25051 //            this.displayEl = navel.el.select('span',true).first();
25052         }
25053         
25054         var _this = this;
25055         
25056         if(this.buttons){
25057             Roo.each(_this.buttons, function(e){ // this might need to use render????
25058                Roo.factory(e).render(_this.el);
25059             });
25060         }
25061             
25062         Roo.each(_this.toolbarItems, function(e) {
25063             _this.navgroup.addItem(e);
25064         });
25065         
25066         
25067         this.first = this.navgroup.addItem({
25068             tooltip: this.firstText,
25069             cls: "prev btn-outline-secondary",
25070             html : ' <i class="fa fa-step-backward"></i>',
25071             disabled: true,
25072             preventDefault: true,
25073             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
25074         });
25075         
25076         this.prev =  this.navgroup.addItem({
25077             tooltip: this.prevText,
25078             cls: "prev btn-outline-secondary",
25079             html : ' <i class="fa fa-backward"></i>',
25080             disabled: true,
25081             preventDefault: true,
25082             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
25083         });
25084     //this.addSeparator();
25085         
25086         
25087         var field = this.navgroup.addItem( {
25088             tagtype : 'span',
25089             cls : 'x-paging-position  btn-outline-secondary',
25090              disabled: true,
25091             html : this.beforePageText  +
25092                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
25093                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
25094          } ); //?? escaped?
25095         
25096         this.field = field.el.select('input', true).first();
25097         this.field.on("keydown", this.onPagingKeydown, this);
25098         this.field.on("focus", function(){this.dom.select();});
25099     
25100     
25101         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
25102         //this.field.setHeight(18);
25103         //this.addSeparator();
25104         this.next = this.navgroup.addItem({
25105             tooltip: this.nextText,
25106             cls: "next btn-outline-secondary",
25107             html : ' <i class="fa fa-forward"></i>',
25108             disabled: true,
25109             preventDefault: true,
25110             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
25111         });
25112         this.last = this.navgroup.addItem({
25113             tooltip: this.lastText,
25114             html : ' <i class="fa fa-step-forward"></i>',
25115             cls: "next btn-outline-secondary",
25116             disabled: true,
25117             preventDefault: true,
25118             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
25119         });
25120     //this.addSeparator();
25121         this.loading = this.navgroup.addItem({
25122             tooltip: this.refreshText,
25123             cls: "btn-outline-secondary",
25124             html : ' <i class="fa fa-refresh"></i>',
25125             preventDefault: true,
25126             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
25127         });
25128         
25129     },
25130
25131     // private
25132     updateInfo : function(){
25133         if(this.displayEl){
25134             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
25135             var msg = count == 0 ?
25136                 this.emptyMsg :
25137                 String.format(
25138                     this.displayMsg,
25139                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
25140                 );
25141             this.displayEl.update(msg);
25142         }
25143     },
25144
25145     // private
25146     onLoad : function(ds, r, o)
25147     {
25148         this.cursor = o.params.start ? o.params.start : 0;
25149         
25150         var d = this.getPageData(),
25151             ap = d.activePage,
25152             ps = d.pages;
25153         
25154         
25155         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
25156         this.field.dom.value = ap;
25157         this.first.setDisabled(ap == 1);
25158         this.prev.setDisabled(ap == 1);
25159         this.next.setDisabled(ap == ps);
25160         this.last.setDisabled(ap == ps);
25161         this.loading.enable();
25162         this.updateInfo();
25163     },
25164
25165     // private
25166     getPageData : function(){
25167         var total = this.ds.getTotalCount();
25168         return {
25169             total : total,
25170             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
25171             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
25172         };
25173     },
25174
25175     // private
25176     onLoadError : function(){
25177         this.loading.enable();
25178     },
25179
25180     // private
25181     onPagingKeydown : function(e){
25182         var k = e.getKey();
25183         var d = this.getPageData();
25184         if(k == e.RETURN){
25185             var v = this.field.dom.value, pageNum;
25186             if(!v || isNaN(pageNum = parseInt(v, 10))){
25187                 this.field.dom.value = d.activePage;
25188                 return;
25189             }
25190             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
25191             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25192             e.stopEvent();
25193         }
25194         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))
25195         {
25196           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
25197           this.field.dom.value = pageNum;
25198           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
25199           e.stopEvent();
25200         }
25201         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
25202         {
25203           var v = this.field.dom.value, pageNum; 
25204           var increment = (e.shiftKey) ? 10 : 1;
25205           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
25206                 increment *= -1;
25207           }
25208           if(!v || isNaN(pageNum = parseInt(v, 10))) {
25209             this.field.dom.value = d.activePage;
25210             return;
25211           }
25212           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25213           {
25214             this.field.dom.value = parseInt(v, 10) + increment;
25215             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25216             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25217           }
25218           e.stopEvent();
25219         }
25220     },
25221
25222     // private
25223     beforeLoad : function(){
25224         if(this.loading){
25225             this.loading.disable();
25226         }
25227     },
25228
25229     // private
25230     onClick : function(which){
25231         
25232         var ds = this.ds;
25233         if (!ds) {
25234             return;
25235         }
25236         
25237         switch(which){
25238             case "first":
25239                 ds.load({params:{start: 0, limit: this.pageSize}});
25240             break;
25241             case "prev":
25242                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25243             break;
25244             case "next":
25245                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25246             break;
25247             case "last":
25248                 var total = ds.getTotalCount();
25249                 var extra = total % this.pageSize;
25250                 var lastStart = extra ? (total - extra) : total-this.pageSize;
25251                 ds.load({params:{start: lastStart, limit: this.pageSize}});
25252             break;
25253             case "refresh":
25254                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25255             break;
25256         }
25257     },
25258
25259     /**
25260      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25261      * @param {Roo.data.Store} store The data store to unbind
25262      */
25263     unbind : function(ds){
25264         ds.un("beforeload", this.beforeLoad, this);
25265         ds.un("load", this.onLoad, this);
25266         ds.un("loadexception", this.onLoadError, this);
25267         ds.un("remove", this.updateInfo, this);
25268         ds.un("add", this.updateInfo, this);
25269         this.ds = undefined;
25270     },
25271
25272     /**
25273      * Binds the paging toolbar to the specified {@link Roo.data.Store}
25274      * @param {Roo.data.Store} store The data store to bind
25275      */
25276     bind : function(ds){
25277         ds.on("beforeload", this.beforeLoad, this);
25278         ds.on("load", this.onLoad, this);
25279         ds.on("loadexception", this.onLoadError, this);
25280         ds.on("remove", this.updateInfo, this);
25281         ds.on("add", this.updateInfo, this);
25282         this.ds = ds;
25283     }
25284 });/*
25285  * - LGPL
25286  *
25287  * element
25288  * 
25289  */
25290
25291 /**
25292  * @class Roo.bootstrap.MessageBar
25293  * @extends Roo.bootstrap.Component
25294  * Bootstrap MessageBar class
25295  * @cfg {String} html contents of the MessageBar
25296  * @cfg {String} weight (info | success | warning | danger) default info
25297  * @cfg {String} beforeClass insert the bar before the given class
25298  * @cfg {Boolean} closable (true | false) default false
25299  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25300  * 
25301  * @constructor
25302  * Create a new Element
25303  * @param {Object} config The config object
25304  */
25305
25306 Roo.bootstrap.MessageBar = function(config){
25307     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25308 };
25309
25310 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25311     
25312     html: '',
25313     weight: 'info',
25314     closable: false,
25315     fixed: false,
25316     beforeClass: 'bootstrap-sticky-wrap',
25317     
25318     getAutoCreate : function(){
25319         
25320         var cfg = {
25321             tag: 'div',
25322             cls: 'alert alert-dismissable alert-' + this.weight,
25323             cn: [
25324                 {
25325                     tag: 'span',
25326                     cls: 'message',
25327                     html: this.html || ''
25328                 }
25329             ]
25330         };
25331         
25332         if(this.fixed){
25333             cfg.cls += ' alert-messages-fixed';
25334         }
25335         
25336         if(this.closable){
25337             cfg.cn.push({
25338                 tag: 'button',
25339                 cls: 'close',
25340                 html: 'x'
25341             });
25342         }
25343         
25344         return cfg;
25345     },
25346     
25347     onRender : function(ct, position)
25348     {
25349         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25350         
25351         if(!this.el){
25352             var cfg = Roo.apply({},  this.getAutoCreate());
25353             cfg.id = Roo.id();
25354             
25355             if (this.cls) {
25356                 cfg.cls += ' ' + this.cls;
25357             }
25358             if (this.style) {
25359                 cfg.style = this.style;
25360             }
25361             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25362             
25363             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25364         }
25365         
25366         this.el.select('>button.close').on('click', this.hide, this);
25367         
25368     },
25369     
25370     show : function()
25371     {
25372         if (!this.rendered) {
25373             this.render();
25374         }
25375         
25376         this.el.show();
25377         
25378         this.fireEvent('show', this);
25379         
25380     },
25381     
25382     hide : function()
25383     {
25384         if (!this.rendered) {
25385             this.render();
25386         }
25387         
25388         this.el.hide();
25389         
25390         this.fireEvent('hide', this);
25391     },
25392     
25393     update : function()
25394     {
25395 //        var e = this.el.dom.firstChild;
25396 //        
25397 //        if(this.closable){
25398 //            e = e.nextSibling;
25399 //        }
25400 //        
25401 //        e.data = this.html || '';
25402
25403         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25404     }
25405    
25406 });
25407
25408  
25409
25410      /*
25411  * - LGPL
25412  *
25413  * Graph
25414  * 
25415  */
25416
25417
25418 /**
25419  * @class Roo.bootstrap.Graph
25420  * @extends Roo.bootstrap.Component
25421  * Bootstrap Graph class
25422 > Prameters
25423  -sm {number} sm 4
25424  -md {number} md 5
25425  @cfg {String} graphtype  bar | vbar | pie
25426  @cfg {number} g_x coodinator | centre x (pie)
25427  @cfg {number} g_y coodinator | centre y (pie)
25428  @cfg {number} g_r radius (pie)
25429  @cfg {number} g_height height of the chart (respected by all elements in the set)
25430  @cfg {number} g_width width of the chart (respected by all elements in the set)
25431  @cfg {Object} title The title of the chart
25432     
25433  -{Array}  values
25434  -opts (object) options for the chart 
25435      o {
25436      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25437      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25438      o vgutter (number)
25439      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.
25440      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25441      o to
25442      o stretch (boolean)
25443      o }
25444  -opts (object) options for the pie
25445      o{
25446      o cut
25447      o startAngle (number)
25448      o endAngle (number)
25449      } 
25450  *
25451  * @constructor
25452  * Create a new Input
25453  * @param {Object} config The config object
25454  */
25455
25456 Roo.bootstrap.Graph = function(config){
25457     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25458     
25459     this.addEvents({
25460         // img events
25461         /**
25462          * @event click
25463          * The img click event for the img.
25464          * @param {Roo.EventObject} e
25465          */
25466         "click" : true
25467     });
25468 };
25469
25470 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25471     
25472     sm: 4,
25473     md: 5,
25474     graphtype: 'bar',
25475     g_height: 250,
25476     g_width: 400,
25477     g_x: 50,
25478     g_y: 50,
25479     g_r: 30,
25480     opts:{
25481         //g_colors: this.colors,
25482         g_type: 'soft',
25483         g_gutter: '20%'
25484
25485     },
25486     title : false,
25487
25488     getAutoCreate : function(){
25489         
25490         var cfg = {
25491             tag: 'div',
25492             html : null
25493         };
25494         
25495         
25496         return  cfg;
25497     },
25498
25499     onRender : function(ct,position){
25500         
25501         
25502         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25503         
25504         if (typeof(Raphael) == 'undefined') {
25505             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25506             return;
25507         }
25508         
25509         this.raphael = Raphael(this.el.dom);
25510         
25511                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25512                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25513                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25514                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25515                 /*
25516                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25517                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25518                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25519                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25520                 
25521                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25522                 r.barchart(330, 10, 300, 220, data1);
25523                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25524                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25525                 */
25526                 
25527                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25528                 // r.barchart(30, 30, 560, 250,  xdata, {
25529                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25530                 //     axis : "0 0 1 1",
25531                 //     axisxlabels :  xdata
25532                 //     //yvalues : cols,
25533                    
25534                 // });
25535 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25536 //        
25537 //        this.load(null,xdata,{
25538 //                axis : "0 0 1 1",
25539 //                axisxlabels :  xdata
25540 //                });
25541
25542     },
25543
25544     load : function(graphtype,xdata,opts)
25545     {
25546         this.raphael.clear();
25547         if(!graphtype) {
25548             graphtype = this.graphtype;
25549         }
25550         if(!opts){
25551             opts = this.opts;
25552         }
25553         var r = this.raphael,
25554             fin = function () {
25555                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25556             },
25557             fout = function () {
25558                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25559             },
25560             pfin = function() {
25561                 this.sector.stop();
25562                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25563
25564                 if (this.label) {
25565                     this.label[0].stop();
25566                     this.label[0].attr({ r: 7.5 });
25567                     this.label[1].attr({ "font-weight": 800 });
25568                 }
25569             },
25570             pfout = function() {
25571                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25572
25573                 if (this.label) {
25574                     this.label[0].animate({ r: 5 }, 500, "bounce");
25575                     this.label[1].attr({ "font-weight": 400 });
25576                 }
25577             };
25578
25579         switch(graphtype){
25580             case 'bar':
25581                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25582                 break;
25583             case 'hbar':
25584                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25585                 break;
25586             case 'pie':
25587 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25588 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25589 //            
25590                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25591                 
25592                 break;
25593
25594         }
25595         
25596         if(this.title){
25597             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25598         }
25599         
25600     },
25601     
25602     setTitle: function(o)
25603     {
25604         this.title = o;
25605     },
25606     
25607     initEvents: function() {
25608         
25609         if(!this.href){
25610             this.el.on('click', this.onClick, this);
25611         }
25612     },
25613     
25614     onClick : function(e)
25615     {
25616         Roo.log('img onclick');
25617         this.fireEvent('click', this, e);
25618     }
25619    
25620 });
25621
25622  
25623 /*
25624  * - LGPL
25625  *
25626  * numberBox
25627  * 
25628  */
25629 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25630
25631 /**
25632  * @class Roo.bootstrap.dash.NumberBox
25633  * @extends Roo.bootstrap.Component
25634  * Bootstrap NumberBox class
25635  * @cfg {String} headline Box headline
25636  * @cfg {String} content Box content
25637  * @cfg {String} icon Box icon
25638  * @cfg {String} footer Footer text
25639  * @cfg {String} fhref Footer href
25640  * 
25641  * @constructor
25642  * Create a new NumberBox
25643  * @param {Object} config The config object
25644  */
25645
25646
25647 Roo.bootstrap.dash.NumberBox = function(config){
25648     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25649     
25650 };
25651
25652 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25653     
25654     headline : '',
25655     content : '',
25656     icon : '',
25657     footer : '',
25658     fhref : '',
25659     ficon : '',
25660     
25661     getAutoCreate : function(){
25662         
25663         var cfg = {
25664             tag : 'div',
25665             cls : 'small-box ',
25666             cn : [
25667                 {
25668                     tag : 'div',
25669                     cls : 'inner',
25670                     cn :[
25671                         {
25672                             tag : 'h3',
25673                             cls : 'roo-headline',
25674                             html : this.headline
25675                         },
25676                         {
25677                             tag : 'p',
25678                             cls : 'roo-content',
25679                             html : this.content
25680                         }
25681                     ]
25682                 }
25683             ]
25684         };
25685         
25686         if(this.icon){
25687             cfg.cn.push({
25688                 tag : 'div',
25689                 cls : 'icon',
25690                 cn :[
25691                     {
25692                         tag : 'i',
25693                         cls : 'ion ' + this.icon
25694                     }
25695                 ]
25696             });
25697         }
25698         
25699         if(this.footer){
25700             var footer = {
25701                 tag : 'a',
25702                 cls : 'small-box-footer',
25703                 href : this.fhref || '#',
25704                 html : this.footer
25705             };
25706             
25707             cfg.cn.push(footer);
25708             
25709         }
25710         
25711         return  cfg;
25712     },
25713
25714     onRender : function(ct,position){
25715         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25716
25717
25718        
25719                 
25720     },
25721
25722     setHeadline: function (value)
25723     {
25724         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25725     },
25726     
25727     setFooter: function (value, href)
25728     {
25729         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25730         
25731         if(href){
25732             this.el.select('a.small-box-footer',true).first().attr('href', href);
25733         }
25734         
25735     },
25736
25737     setContent: function (value)
25738     {
25739         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25740     },
25741
25742     initEvents: function() 
25743     {   
25744         
25745     }
25746     
25747 });
25748
25749  
25750 /*
25751  * - LGPL
25752  *
25753  * TabBox
25754  * 
25755  */
25756 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25757
25758 /**
25759  * @class Roo.bootstrap.dash.TabBox
25760  * @extends Roo.bootstrap.Component
25761  * Bootstrap TabBox class
25762  * @cfg {String} title Title of the TabBox
25763  * @cfg {String} icon Icon of the TabBox
25764  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25765  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25766  * 
25767  * @constructor
25768  * Create a new TabBox
25769  * @param {Object} config The config object
25770  */
25771
25772
25773 Roo.bootstrap.dash.TabBox = function(config){
25774     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25775     this.addEvents({
25776         // raw events
25777         /**
25778          * @event addpane
25779          * When a pane is added
25780          * @param {Roo.bootstrap.dash.TabPane} pane
25781          */
25782         "addpane" : true,
25783         /**
25784          * @event activatepane
25785          * When a pane is activated
25786          * @param {Roo.bootstrap.dash.TabPane} pane
25787          */
25788         "activatepane" : true
25789         
25790          
25791     });
25792     
25793     this.panes = [];
25794 };
25795
25796 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25797
25798     title : '',
25799     icon : false,
25800     showtabs : true,
25801     tabScrollable : false,
25802     
25803     getChildContainer : function()
25804     {
25805         return this.el.select('.tab-content', true).first();
25806     },
25807     
25808     getAutoCreate : function(){
25809         
25810         var header = {
25811             tag: 'li',
25812             cls: 'pull-left header',
25813             html: this.title,
25814             cn : []
25815         };
25816         
25817         if(this.icon){
25818             header.cn.push({
25819                 tag: 'i',
25820                 cls: 'fa ' + this.icon
25821             });
25822         }
25823         
25824         var h = {
25825             tag: 'ul',
25826             cls: 'nav nav-tabs pull-right',
25827             cn: [
25828                 header
25829             ]
25830         };
25831         
25832         if(this.tabScrollable){
25833             h = {
25834                 tag: 'div',
25835                 cls: 'tab-header',
25836                 cn: [
25837                     {
25838                         tag: 'ul',
25839                         cls: 'nav nav-tabs pull-right',
25840                         cn: [
25841                             header
25842                         ]
25843                     }
25844                 ]
25845             };
25846         }
25847         
25848         var cfg = {
25849             tag: 'div',
25850             cls: 'nav-tabs-custom',
25851             cn: [
25852                 h,
25853                 {
25854                     tag: 'div',
25855                     cls: 'tab-content no-padding',
25856                     cn: []
25857                 }
25858             ]
25859         };
25860
25861         return  cfg;
25862     },
25863     initEvents : function()
25864     {
25865         //Roo.log('add add pane handler');
25866         this.on('addpane', this.onAddPane, this);
25867     },
25868      /**
25869      * Updates the box title
25870      * @param {String} html to set the title to.
25871      */
25872     setTitle : function(value)
25873     {
25874         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25875     },
25876     onAddPane : function(pane)
25877     {
25878         this.panes.push(pane);
25879         //Roo.log('addpane');
25880         //Roo.log(pane);
25881         // tabs are rendere left to right..
25882         if(!this.showtabs){
25883             return;
25884         }
25885         
25886         var ctr = this.el.select('.nav-tabs', true).first();
25887          
25888          
25889         var existing = ctr.select('.nav-tab',true);
25890         var qty = existing.getCount();;
25891         
25892         
25893         var tab = ctr.createChild({
25894             tag : 'li',
25895             cls : 'nav-tab' + (qty ? '' : ' active'),
25896             cn : [
25897                 {
25898                     tag : 'a',
25899                     href:'#',
25900                     html : pane.title
25901                 }
25902             ]
25903         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25904         pane.tab = tab;
25905         
25906         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25907         if (!qty) {
25908             pane.el.addClass('active');
25909         }
25910         
25911                 
25912     },
25913     onTabClick : function(ev,un,ob,pane)
25914     {
25915         //Roo.log('tab - prev default');
25916         ev.preventDefault();
25917         
25918         
25919         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25920         pane.tab.addClass('active');
25921         //Roo.log(pane.title);
25922         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25923         // technically we should have a deactivate event.. but maybe add later.
25924         // and it should not de-activate the selected tab...
25925         this.fireEvent('activatepane', pane);
25926         pane.el.addClass('active');
25927         pane.fireEvent('activate');
25928         
25929         
25930     },
25931     
25932     getActivePane : function()
25933     {
25934         var r = false;
25935         Roo.each(this.panes, function(p) {
25936             if(p.el.hasClass('active')){
25937                 r = p;
25938                 return false;
25939             }
25940             
25941             return;
25942         });
25943         
25944         return r;
25945     }
25946     
25947     
25948 });
25949
25950  
25951 /*
25952  * - LGPL
25953  *
25954  * Tab pane
25955  * 
25956  */
25957 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25958 /**
25959  * @class Roo.bootstrap.TabPane
25960  * @extends Roo.bootstrap.Component
25961  * Bootstrap TabPane class
25962  * @cfg {Boolean} active (false | true) Default false
25963  * @cfg {String} title title of panel
25964
25965  * 
25966  * @constructor
25967  * Create a new TabPane
25968  * @param {Object} config The config object
25969  */
25970
25971 Roo.bootstrap.dash.TabPane = function(config){
25972     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25973     
25974     this.addEvents({
25975         // raw events
25976         /**
25977          * @event activate
25978          * When a pane is activated
25979          * @param {Roo.bootstrap.dash.TabPane} pane
25980          */
25981         "activate" : true
25982          
25983     });
25984 };
25985
25986 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25987     
25988     active : false,
25989     title : '',
25990     
25991     // the tabBox that this is attached to.
25992     tab : false,
25993      
25994     getAutoCreate : function() 
25995     {
25996         var cfg = {
25997             tag: 'div',
25998             cls: 'tab-pane'
25999         };
26000         
26001         if(this.active){
26002             cfg.cls += ' active';
26003         }
26004         
26005         return cfg;
26006     },
26007     initEvents  : function()
26008     {
26009         //Roo.log('trigger add pane handler');
26010         this.parent().fireEvent('addpane', this)
26011     },
26012     
26013      /**
26014      * Updates the tab title 
26015      * @param {String} html to set the title to.
26016      */
26017     setTitle: function(str)
26018     {
26019         if (!this.tab) {
26020             return;
26021         }
26022         this.title = str;
26023         this.tab.select('a', true).first().dom.innerHTML = str;
26024         
26025     }
26026     
26027     
26028     
26029 });
26030
26031  
26032
26033
26034  /*
26035  * - LGPL
26036  *
26037  * menu
26038  * 
26039  */
26040 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26041
26042 /**
26043  * @class Roo.bootstrap.menu.Menu
26044  * @extends Roo.bootstrap.Component
26045  * Bootstrap Menu class - container for Menu
26046  * @cfg {String} html Text of the menu
26047  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
26048  * @cfg {String} icon Font awesome icon
26049  * @cfg {String} pos Menu align to (top | bottom) default bottom
26050  * 
26051  * 
26052  * @constructor
26053  * Create a new Menu
26054  * @param {Object} config The config object
26055  */
26056
26057
26058 Roo.bootstrap.menu.Menu = function(config){
26059     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
26060     
26061     this.addEvents({
26062         /**
26063          * @event beforeshow
26064          * Fires before this menu is displayed
26065          * @param {Roo.bootstrap.menu.Menu} this
26066          */
26067         beforeshow : true,
26068         /**
26069          * @event beforehide
26070          * Fires before this menu is hidden
26071          * @param {Roo.bootstrap.menu.Menu} this
26072          */
26073         beforehide : true,
26074         /**
26075          * @event show
26076          * Fires after this menu is displayed
26077          * @param {Roo.bootstrap.menu.Menu} this
26078          */
26079         show : true,
26080         /**
26081          * @event hide
26082          * Fires after this menu is hidden
26083          * @param {Roo.bootstrap.menu.Menu} this
26084          */
26085         hide : true,
26086         /**
26087          * @event click
26088          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
26089          * @param {Roo.bootstrap.menu.Menu} this
26090          * @param {Roo.EventObject} e
26091          */
26092         click : true
26093     });
26094     
26095 };
26096
26097 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
26098     
26099     submenu : false,
26100     html : '',
26101     weight : 'default',
26102     icon : false,
26103     pos : 'bottom',
26104     
26105     
26106     getChildContainer : function() {
26107         if(this.isSubMenu){
26108             return this.el;
26109         }
26110         
26111         return this.el.select('ul.dropdown-menu', true).first();  
26112     },
26113     
26114     getAutoCreate : function()
26115     {
26116         var text = [
26117             {
26118                 tag : 'span',
26119                 cls : 'roo-menu-text',
26120                 html : this.html
26121             }
26122         ];
26123         
26124         if(this.icon){
26125             text.unshift({
26126                 tag : 'i',
26127                 cls : 'fa ' + this.icon
26128             })
26129         }
26130         
26131         
26132         var cfg = {
26133             tag : 'div',
26134             cls : 'btn-group',
26135             cn : [
26136                 {
26137                     tag : 'button',
26138                     cls : 'dropdown-button btn btn-' + this.weight,
26139                     cn : text
26140                 },
26141                 {
26142                     tag : 'button',
26143                     cls : 'dropdown-toggle btn btn-' + this.weight,
26144                     cn : [
26145                         {
26146                             tag : 'span',
26147                             cls : 'caret'
26148                         }
26149                     ]
26150                 },
26151                 {
26152                     tag : 'ul',
26153                     cls : 'dropdown-menu'
26154                 }
26155             ]
26156             
26157         };
26158         
26159         if(this.pos == 'top'){
26160             cfg.cls += ' dropup';
26161         }
26162         
26163         if(this.isSubMenu){
26164             cfg = {
26165                 tag : 'ul',
26166                 cls : 'dropdown-menu'
26167             }
26168         }
26169         
26170         return cfg;
26171     },
26172     
26173     onRender : function(ct, position)
26174     {
26175         this.isSubMenu = ct.hasClass('dropdown-submenu');
26176         
26177         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
26178     },
26179     
26180     initEvents : function() 
26181     {
26182         if(this.isSubMenu){
26183             return;
26184         }
26185         
26186         this.hidden = true;
26187         
26188         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
26189         this.triggerEl.on('click', this.onTriggerPress, this);
26190         
26191         this.buttonEl = this.el.select('button.dropdown-button', true).first();
26192         this.buttonEl.on('click', this.onClick, this);
26193         
26194     },
26195     
26196     list : function()
26197     {
26198         if(this.isSubMenu){
26199             return this.el;
26200         }
26201         
26202         return this.el.select('ul.dropdown-menu', true).first();
26203     },
26204     
26205     onClick : function(e)
26206     {
26207         this.fireEvent("click", this, e);
26208     },
26209     
26210     onTriggerPress  : function(e)
26211     {   
26212         if (this.isVisible()) {
26213             this.hide();
26214         } else {
26215             this.show();
26216         }
26217     },
26218     
26219     isVisible : function(){
26220         return !this.hidden;
26221     },
26222     
26223     show : function()
26224     {
26225         this.fireEvent("beforeshow", this);
26226         
26227         this.hidden = false;
26228         this.el.addClass('open');
26229         
26230         Roo.get(document).on("mouseup", this.onMouseUp, this);
26231         
26232         this.fireEvent("show", this);
26233         
26234         
26235     },
26236     
26237     hide : function()
26238     {
26239         this.fireEvent("beforehide", this);
26240         
26241         this.hidden = true;
26242         this.el.removeClass('open');
26243         
26244         Roo.get(document).un("mouseup", this.onMouseUp);
26245         
26246         this.fireEvent("hide", this);
26247     },
26248     
26249     onMouseUp : function()
26250     {
26251         this.hide();
26252     }
26253     
26254 });
26255
26256  
26257  /*
26258  * - LGPL
26259  *
26260  * menu item
26261  * 
26262  */
26263 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26264
26265 /**
26266  * @class Roo.bootstrap.menu.Item
26267  * @extends Roo.bootstrap.Component
26268  * Bootstrap MenuItem class
26269  * @cfg {Boolean} submenu (true | false) default false
26270  * @cfg {String} html text of the item
26271  * @cfg {String} href the link
26272  * @cfg {Boolean} disable (true | false) default false
26273  * @cfg {Boolean} preventDefault (true | false) default true
26274  * @cfg {String} icon Font awesome icon
26275  * @cfg {String} pos Submenu align to (left | right) default right 
26276  * 
26277  * 
26278  * @constructor
26279  * Create a new Item
26280  * @param {Object} config The config object
26281  */
26282
26283
26284 Roo.bootstrap.menu.Item = function(config){
26285     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26286     this.addEvents({
26287         /**
26288          * @event mouseover
26289          * Fires when the mouse is hovering over this menu
26290          * @param {Roo.bootstrap.menu.Item} this
26291          * @param {Roo.EventObject} e
26292          */
26293         mouseover : true,
26294         /**
26295          * @event mouseout
26296          * Fires when the mouse exits this menu
26297          * @param {Roo.bootstrap.menu.Item} this
26298          * @param {Roo.EventObject} e
26299          */
26300         mouseout : true,
26301         // raw events
26302         /**
26303          * @event click
26304          * The raw click event for the entire grid.
26305          * @param {Roo.EventObject} e
26306          */
26307         click : true
26308     });
26309 };
26310
26311 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26312     
26313     submenu : false,
26314     href : '',
26315     html : '',
26316     preventDefault: true,
26317     disable : false,
26318     icon : false,
26319     pos : 'right',
26320     
26321     getAutoCreate : function()
26322     {
26323         var text = [
26324             {
26325                 tag : 'span',
26326                 cls : 'roo-menu-item-text',
26327                 html : this.html
26328             }
26329         ];
26330         
26331         if(this.icon){
26332             text.unshift({
26333                 tag : 'i',
26334                 cls : 'fa ' + this.icon
26335             })
26336         }
26337         
26338         var cfg = {
26339             tag : 'li',
26340             cn : [
26341                 {
26342                     tag : 'a',
26343                     href : this.href || '#',
26344                     cn : text
26345                 }
26346             ]
26347         };
26348         
26349         if(this.disable){
26350             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26351         }
26352         
26353         if(this.submenu){
26354             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26355             
26356             if(this.pos == 'left'){
26357                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26358             }
26359         }
26360         
26361         return cfg;
26362     },
26363     
26364     initEvents : function() 
26365     {
26366         this.el.on('mouseover', this.onMouseOver, this);
26367         this.el.on('mouseout', this.onMouseOut, this);
26368         
26369         this.el.select('a', true).first().on('click', this.onClick, this);
26370         
26371     },
26372     
26373     onClick : function(e)
26374     {
26375         if(this.preventDefault){
26376             e.preventDefault();
26377         }
26378         
26379         this.fireEvent("click", this, e);
26380     },
26381     
26382     onMouseOver : function(e)
26383     {
26384         if(this.submenu && this.pos == 'left'){
26385             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26386         }
26387         
26388         this.fireEvent("mouseover", this, e);
26389     },
26390     
26391     onMouseOut : function(e)
26392     {
26393         this.fireEvent("mouseout", this, e);
26394     }
26395 });
26396
26397  
26398
26399  /*
26400  * - LGPL
26401  *
26402  * menu separator
26403  * 
26404  */
26405 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26406
26407 /**
26408  * @class Roo.bootstrap.menu.Separator
26409  * @extends Roo.bootstrap.Component
26410  * Bootstrap Separator class
26411  * 
26412  * @constructor
26413  * Create a new Separator
26414  * @param {Object} config The config object
26415  */
26416
26417
26418 Roo.bootstrap.menu.Separator = function(config){
26419     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26420 };
26421
26422 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26423     
26424     getAutoCreate : function(){
26425         var cfg = {
26426             tag : 'li',
26427             cls: 'divider'
26428         };
26429         
26430         return cfg;
26431     }
26432    
26433 });
26434
26435  
26436
26437  /*
26438  * - LGPL
26439  *
26440  * Tooltip
26441  * 
26442  */
26443
26444 /**
26445  * @class Roo.bootstrap.Tooltip
26446  * Bootstrap Tooltip class
26447  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26448  * to determine which dom element triggers the tooltip.
26449  * 
26450  * It needs to add support for additional attributes like tooltip-position
26451  * 
26452  * @constructor
26453  * Create a new Toolti
26454  * @param {Object} config The config object
26455  */
26456
26457 Roo.bootstrap.Tooltip = function(config){
26458     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26459     
26460     this.alignment = Roo.bootstrap.Tooltip.alignment;
26461     
26462     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26463         this.alignment = config.alignment;
26464     }
26465     
26466 };
26467
26468 Roo.apply(Roo.bootstrap.Tooltip, {
26469     /**
26470      * @function init initialize tooltip monitoring.
26471      * @static
26472      */
26473     currentEl : false,
26474     currentTip : false,
26475     currentRegion : false,
26476     
26477     //  init : delay?
26478     
26479     init : function()
26480     {
26481         Roo.get(document).on('mouseover', this.enter ,this);
26482         Roo.get(document).on('mouseout', this.leave, this);
26483          
26484         
26485         this.currentTip = new Roo.bootstrap.Tooltip();
26486     },
26487     
26488     enter : function(ev)
26489     {
26490         var dom = ev.getTarget();
26491         
26492         //Roo.log(['enter',dom]);
26493         var el = Roo.fly(dom);
26494         if (this.currentEl) {
26495             //Roo.log(dom);
26496             //Roo.log(this.currentEl);
26497             //Roo.log(this.currentEl.contains(dom));
26498             if (this.currentEl == el) {
26499                 return;
26500             }
26501             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26502                 return;
26503             }
26504
26505         }
26506         
26507         if (this.currentTip.el) {
26508             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26509         }    
26510         //Roo.log(ev);
26511         
26512         if(!el || el.dom == document){
26513             return;
26514         }
26515         
26516         var bindEl = el;
26517         
26518         // you can not look for children, as if el is the body.. then everythign is the child..
26519         if (!el.attr('tooltip')) { //
26520             if (!el.select("[tooltip]").elements.length) {
26521                 return;
26522             }
26523             // is the mouse over this child...?
26524             bindEl = el.select("[tooltip]").first();
26525             var xy = ev.getXY();
26526             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26527                 //Roo.log("not in region.");
26528                 return;
26529             }
26530             //Roo.log("child element over..");
26531             
26532         }
26533         this.currentEl = bindEl;
26534         this.currentTip.bind(bindEl);
26535         this.currentRegion = Roo.lib.Region.getRegion(dom);
26536         this.currentTip.enter();
26537         
26538     },
26539     leave : function(ev)
26540     {
26541         var dom = ev.getTarget();
26542         //Roo.log(['leave',dom]);
26543         if (!this.currentEl) {
26544             return;
26545         }
26546         
26547         
26548         if (dom != this.currentEl.dom) {
26549             return;
26550         }
26551         var xy = ev.getXY();
26552         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26553             return;
26554         }
26555         // only activate leave if mouse cursor is outside... bounding box..
26556         
26557         
26558         
26559         
26560         if (this.currentTip) {
26561             this.currentTip.leave();
26562         }
26563         //Roo.log('clear currentEl');
26564         this.currentEl = false;
26565         
26566         
26567     },
26568     alignment : {
26569         'left' : ['r-l', [-2,0], 'right'],
26570         'right' : ['l-r', [2,0], 'left'],
26571         'bottom' : ['t-b', [0,2], 'top'],
26572         'top' : [ 'b-t', [0,-2], 'bottom']
26573     }
26574     
26575 });
26576
26577
26578 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26579     
26580     
26581     bindEl : false,
26582     
26583     delay : null, // can be { show : 300 , hide: 500}
26584     
26585     timeout : null,
26586     
26587     hoverState : null, //???
26588     
26589     placement : 'bottom', 
26590     
26591     alignment : false,
26592     
26593     getAutoCreate : function(){
26594     
26595         var cfg = {
26596            cls : 'tooltip',
26597            role : 'tooltip',
26598            cn : [
26599                 {
26600                     cls : 'tooltip-arrow'
26601                 },
26602                 {
26603                     cls : 'tooltip-inner'
26604                 }
26605            ]
26606         };
26607         
26608         return cfg;
26609     },
26610     bind : function(el)
26611     {
26612         this.bindEl = el;
26613     },
26614       
26615     
26616     enter : function () {
26617        
26618         if (this.timeout != null) {
26619             clearTimeout(this.timeout);
26620         }
26621         
26622         this.hoverState = 'in';
26623          //Roo.log("enter - show");
26624         if (!this.delay || !this.delay.show) {
26625             this.show();
26626             return;
26627         }
26628         var _t = this;
26629         this.timeout = setTimeout(function () {
26630             if (_t.hoverState == 'in') {
26631                 _t.show();
26632             }
26633         }, this.delay.show);
26634     },
26635     leave : function()
26636     {
26637         clearTimeout(this.timeout);
26638     
26639         this.hoverState = 'out';
26640          if (!this.delay || !this.delay.hide) {
26641             this.hide();
26642             return;
26643         }
26644        
26645         var _t = this;
26646         this.timeout = setTimeout(function () {
26647             //Roo.log("leave - timeout");
26648             
26649             if (_t.hoverState == 'out') {
26650                 _t.hide();
26651                 Roo.bootstrap.Tooltip.currentEl = false;
26652             }
26653         }, delay);
26654     },
26655     
26656     show : function (msg)
26657     {
26658         if (!this.el) {
26659             this.render(document.body);
26660         }
26661         // set content.
26662         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26663         
26664         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26665         
26666         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26667         
26668         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26669         
26670         var placement = typeof this.placement == 'function' ?
26671             this.placement.call(this, this.el, on_el) :
26672             this.placement;
26673             
26674         var autoToken = /\s?auto?\s?/i;
26675         var autoPlace = autoToken.test(placement);
26676         if (autoPlace) {
26677             placement = placement.replace(autoToken, '') || 'top';
26678         }
26679         
26680         //this.el.detach()
26681         //this.el.setXY([0,0]);
26682         this.el.show();
26683         //this.el.dom.style.display='block';
26684         
26685         //this.el.appendTo(on_el);
26686         
26687         var p = this.getPosition();
26688         var box = this.el.getBox();
26689         
26690         if (autoPlace) {
26691             // fixme..
26692         }
26693         
26694         var align = this.alignment[placement];
26695         
26696         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26697         
26698         if(placement == 'top' || placement == 'bottom'){
26699             if(xy[0] < 0){
26700                 placement = 'right';
26701             }
26702             
26703             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26704                 placement = 'left';
26705             }
26706             
26707             var scroll = Roo.select('body', true).first().getScroll();
26708             
26709             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26710                 placement = 'top';
26711             }
26712             
26713             align = this.alignment[placement];
26714         }
26715         
26716         this.el.alignTo(this.bindEl, align[0],align[1]);
26717         //var arrow = this.el.select('.arrow',true).first();
26718         //arrow.set(align[2], 
26719         
26720         this.el.addClass(placement);
26721         
26722         this.el.addClass('in fade');
26723         
26724         this.hoverState = null;
26725         
26726         if (this.el.hasClass('fade')) {
26727             // fade it?
26728         }
26729         
26730     },
26731     hide : function()
26732     {
26733          
26734         if (!this.el) {
26735             return;
26736         }
26737         //this.el.setXY([0,0]);
26738         this.el.removeClass('in');
26739         //this.el.hide();
26740         
26741     }
26742     
26743 });
26744  
26745
26746  /*
26747  * - LGPL
26748  *
26749  * Location Picker
26750  * 
26751  */
26752
26753 /**
26754  * @class Roo.bootstrap.LocationPicker
26755  * @extends Roo.bootstrap.Component
26756  * Bootstrap LocationPicker class
26757  * @cfg {Number} latitude Position when init default 0
26758  * @cfg {Number} longitude Position when init default 0
26759  * @cfg {Number} zoom default 15
26760  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26761  * @cfg {Boolean} mapTypeControl default false
26762  * @cfg {Boolean} disableDoubleClickZoom default false
26763  * @cfg {Boolean} scrollwheel default true
26764  * @cfg {Boolean} streetViewControl default false
26765  * @cfg {Number} radius default 0
26766  * @cfg {String} locationName
26767  * @cfg {Boolean} draggable default true
26768  * @cfg {Boolean} enableAutocomplete default false
26769  * @cfg {Boolean} enableReverseGeocode default true
26770  * @cfg {String} markerTitle
26771  * 
26772  * @constructor
26773  * Create a new LocationPicker
26774  * @param {Object} config The config object
26775  */
26776
26777
26778 Roo.bootstrap.LocationPicker = function(config){
26779     
26780     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26781     
26782     this.addEvents({
26783         /**
26784          * @event initial
26785          * Fires when the picker initialized.
26786          * @param {Roo.bootstrap.LocationPicker} this
26787          * @param {Google Location} location
26788          */
26789         initial : true,
26790         /**
26791          * @event positionchanged
26792          * Fires when the picker position changed.
26793          * @param {Roo.bootstrap.LocationPicker} this
26794          * @param {Google Location} location
26795          */
26796         positionchanged : true,
26797         /**
26798          * @event resize
26799          * Fires when the map resize.
26800          * @param {Roo.bootstrap.LocationPicker} this
26801          */
26802         resize : true,
26803         /**
26804          * @event show
26805          * Fires when the map show.
26806          * @param {Roo.bootstrap.LocationPicker} this
26807          */
26808         show : true,
26809         /**
26810          * @event hide
26811          * Fires when the map hide.
26812          * @param {Roo.bootstrap.LocationPicker} this
26813          */
26814         hide : true,
26815         /**
26816          * @event mapClick
26817          * Fires when click the map.
26818          * @param {Roo.bootstrap.LocationPicker} this
26819          * @param {Map event} e
26820          */
26821         mapClick : true,
26822         /**
26823          * @event mapRightClick
26824          * Fires when right click the map.
26825          * @param {Roo.bootstrap.LocationPicker} this
26826          * @param {Map event} e
26827          */
26828         mapRightClick : true,
26829         /**
26830          * @event markerClick
26831          * Fires when click the marker.
26832          * @param {Roo.bootstrap.LocationPicker} this
26833          * @param {Map event} e
26834          */
26835         markerClick : true,
26836         /**
26837          * @event markerRightClick
26838          * Fires when right click the marker.
26839          * @param {Roo.bootstrap.LocationPicker} this
26840          * @param {Map event} e
26841          */
26842         markerRightClick : true,
26843         /**
26844          * @event OverlayViewDraw
26845          * Fires when OverlayView Draw
26846          * @param {Roo.bootstrap.LocationPicker} this
26847          */
26848         OverlayViewDraw : true,
26849         /**
26850          * @event OverlayViewOnAdd
26851          * Fires when OverlayView Draw
26852          * @param {Roo.bootstrap.LocationPicker} this
26853          */
26854         OverlayViewOnAdd : true,
26855         /**
26856          * @event OverlayViewOnRemove
26857          * Fires when OverlayView Draw
26858          * @param {Roo.bootstrap.LocationPicker} this
26859          */
26860         OverlayViewOnRemove : true,
26861         /**
26862          * @event OverlayViewShow
26863          * Fires when OverlayView Draw
26864          * @param {Roo.bootstrap.LocationPicker} this
26865          * @param {Pixel} cpx
26866          */
26867         OverlayViewShow : true,
26868         /**
26869          * @event OverlayViewHide
26870          * Fires when OverlayView Draw
26871          * @param {Roo.bootstrap.LocationPicker} this
26872          */
26873         OverlayViewHide : true,
26874         /**
26875          * @event loadexception
26876          * Fires when load google lib failed.
26877          * @param {Roo.bootstrap.LocationPicker} this
26878          */
26879         loadexception : true
26880     });
26881         
26882 };
26883
26884 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26885     
26886     gMapContext: false,
26887     
26888     latitude: 0,
26889     longitude: 0,
26890     zoom: 15,
26891     mapTypeId: false,
26892     mapTypeControl: false,
26893     disableDoubleClickZoom: false,
26894     scrollwheel: true,
26895     streetViewControl: false,
26896     radius: 0,
26897     locationName: '',
26898     draggable: true,
26899     enableAutocomplete: false,
26900     enableReverseGeocode: true,
26901     markerTitle: '',
26902     
26903     getAutoCreate: function()
26904     {
26905
26906         var cfg = {
26907             tag: 'div',
26908             cls: 'roo-location-picker'
26909         };
26910         
26911         return cfg
26912     },
26913     
26914     initEvents: function(ct, position)
26915     {       
26916         if(!this.el.getWidth() || this.isApplied()){
26917             return;
26918         }
26919         
26920         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26921         
26922         this.initial();
26923     },
26924     
26925     initial: function()
26926     {
26927         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26928             this.fireEvent('loadexception', this);
26929             return;
26930         }
26931         
26932         if(!this.mapTypeId){
26933             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26934         }
26935         
26936         this.gMapContext = this.GMapContext();
26937         
26938         this.initOverlayView();
26939         
26940         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26941         
26942         var _this = this;
26943                 
26944         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26945             _this.setPosition(_this.gMapContext.marker.position);
26946         });
26947         
26948         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26949             _this.fireEvent('mapClick', this, event);
26950             
26951         });
26952
26953         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26954             _this.fireEvent('mapRightClick', this, event);
26955             
26956         });
26957         
26958         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26959             _this.fireEvent('markerClick', this, event);
26960             
26961         });
26962
26963         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26964             _this.fireEvent('markerRightClick', this, event);
26965             
26966         });
26967         
26968         this.setPosition(this.gMapContext.location);
26969         
26970         this.fireEvent('initial', this, this.gMapContext.location);
26971     },
26972     
26973     initOverlayView: function()
26974     {
26975         var _this = this;
26976         
26977         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26978             
26979             draw: function()
26980             {
26981                 _this.fireEvent('OverlayViewDraw', _this);
26982             },
26983             
26984             onAdd: function()
26985             {
26986                 _this.fireEvent('OverlayViewOnAdd', _this);
26987             },
26988             
26989             onRemove: function()
26990             {
26991                 _this.fireEvent('OverlayViewOnRemove', _this);
26992             },
26993             
26994             show: function(cpx)
26995             {
26996                 _this.fireEvent('OverlayViewShow', _this, cpx);
26997             },
26998             
26999             hide: function()
27000             {
27001                 _this.fireEvent('OverlayViewHide', _this);
27002             }
27003             
27004         });
27005     },
27006     
27007     fromLatLngToContainerPixel: function(event)
27008     {
27009         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
27010     },
27011     
27012     isApplied: function() 
27013     {
27014         return this.getGmapContext() == false ? false : true;
27015     },
27016     
27017     getGmapContext: function() 
27018     {
27019         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
27020     },
27021     
27022     GMapContext: function() 
27023     {
27024         var position = new google.maps.LatLng(this.latitude, this.longitude);
27025         
27026         var _map = new google.maps.Map(this.el.dom, {
27027             center: position,
27028             zoom: this.zoom,
27029             mapTypeId: this.mapTypeId,
27030             mapTypeControl: this.mapTypeControl,
27031             disableDoubleClickZoom: this.disableDoubleClickZoom,
27032             scrollwheel: this.scrollwheel,
27033             streetViewControl: this.streetViewControl,
27034             locationName: this.locationName,
27035             draggable: this.draggable,
27036             enableAutocomplete: this.enableAutocomplete,
27037             enableReverseGeocode: this.enableReverseGeocode
27038         });
27039         
27040         var _marker = new google.maps.Marker({
27041             position: position,
27042             map: _map,
27043             title: this.markerTitle,
27044             draggable: this.draggable
27045         });
27046         
27047         return {
27048             map: _map,
27049             marker: _marker,
27050             circle: null,
27051             location: position,
27052             radius: this.radius,
27053             locationName: this.locationName,
27054             addressComponents: {
27055                 formatted_address: null,
27056                 addressLine1: null,
27057                 addressLine2: null,
27058                 streetName: null,
27059                 streetNumber: null,
27060                 city: null,
27061                 district: null,
27062                 state: null,
27063                 stateOrProvince: null
27064             },
27065             settings: this,
27066             domContainer: this.el.dom,
27067             geodecoder: new google.maps.Geocoder()
27068         };
27069     },
27070     
27071     drawCircle: function(center, radius, options) 
27072     {
27073         if (this.gMapContext.circle != null) {
27074             this.gMapContext.circle.setMap(null);
27075         }
27076         if (radius > 0) {
27077             radius *= 1;
27078             options = Roo.apply({}, options, {
27079                 strokeColor: "#0000FF",
27080                 strokeOpacity: .35,
27081                 strokeWeight: 2,
27082                 fillColor: "#0000FF",
27083                 fillOpacity: .2
27084             });
27085             
27086             options.map = this.gMapContext.map;
27087             options.radius = radius;
27088             options.center = center;
27089             this.gMapContext.circle = new google.maps.Circle(options);
27090             return this.gMapContext.circle;
27091         }
27092         
27093         return null;
27094     },
27095     
27096     setPosition: function(location) 
27097     {
27098         this.gMapContext.location = location;
27099         this.gMapContext.marker.setPosition(location);
27100         this.gMapContext.map.panTo(location);
27101         this.drawCircle(location, this.gMapContext.radius, {});
27102         
27103         var _this = this;
27104         
27105         if (this.gMapContext.settings.enableReverseGeocode) {
27106             this.gMapContext.geodecoder.geocode({
27107                 latLng: this.gMapContext.location
27108             }, function(results, status) {
27109                 
27110                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
27111                     _this.gMapContext.locationName = results[0].formatted_address;
27112                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
27113                     
27114                     _this.fireEvent('positionchanged', this, location);
27115                 }
27116             });
27117             
27118             return;
27119         }
27120         
27121         this.fireEvent('positionchanged', this, location);
27122     },
27123     
27124     resize: function()
27125     {
27126         google.maps.event.trigger(this.gMapContext.map, "resize");
27127         
27128         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
27129         
27130         this.fireEvent('resize', this);
27131     },
27132     
27133     setPositionByLatLng: function(latitude, longitude)
27134     {
27135         this.setPosition(new google.maps.LatLng(latitude, longitude));
27136     },
27137     
27138     getCurrentPosition: function() 
27139     {
27140         return {
27141             latitude: this.gMapContext.location.lat(),
27142             longitude: this.gMapContext.location.lng()
27143         };
27144     },
27145     
27146     getAddressName: function() 
27147     {
27148         return this.gMapContext.locationName;
27149     },
27150     
27151     getAddressComponents: function() 
27152     {
27153         return this.gMapContext.addressComponents;
27154     },
27155     
27156     address_component_from_google_geocode: function(address_components) 
27157     {
27158         var result = {};
27159         
27160         for (var i = 0; i < address_components.length; i++) {
27161             var component = address_components[i];
27162             if (component.types.indexOf("postal_code") >= 0) {
27163                 result.postalCode = component.short_name;
27164             } else if (component.types.indexOf("street_number") >= 0) {
27165                 result.streetNumber = component.short_name;
27166             } else if (component.types.indexOf("route") >= 0) {
27167                 result.streetName = component.short_name;
27168             } else if (component.types.indexOf("neighborhood") >= 0) {
27169                 result.city = component.short_name;
27170             } else if (component.types.indexOf("locality") >= 0) {
27171                 result.city = component.short_name;
27172             } else if (component.types.indexOf("sublocality") >= 0) {
27173                 result.district = component.short_name;
27174             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
27175                 result.stateOrProvince = component.short_name;
27176             } else if (component.types.indexOf("country") >= 0) {
27177                 result.country = component.short_name;
27178             }
27179         }
27180         
27181         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
27182         result.addressLine2 = "";
27183         return result;
27184     },
27185     
27186     setZoomLevel: function(zoom)
27187     {
27188         this.gMapContext.map.setZoom(zoom);
27189     },
27190     
27191     show: function()
27192     {
27193         if(!this.el){
27194             return;
27195         }
27196         
27197         this.el.show();
27198         
27199         this.resize();
27200         
27201         this.fireEvent('show', this);
27202     },
27203     
27204     hide: function()
27205     {
27206         if(!this.el){
27207             return;
27208         }
27209         
27210         this.el.hide();
27211         
27212         this.fireEvent('hide', this);
27213     }
27214     
27215 });
27216
27217 Roo.apply(Roo.bootstrap.LocationPicker, {
27218     
27219     OverlayView : function(map, options)
27220     {
27221         options = options || {};
27222         
27223         this.setMap(map);
27224     }
27225     
27226     
27227 });/**
27228  * @class Roo.bootstrap.Alert
27229  * @extends Roo.bootstrap.Component
27230  * Bootstrap Alert class - shows an alert area box
27231  * eg
27232  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
27233   Enter a valid email address
27234 </div>
27235  * @licence LGPL
27236  * @cfg {String} title The title of alert
27237  * @cfg {String} html The content of alert
27238  * @cfg {String} weight (  success | info | warning | danger )
27239  * @cfg {String} faicon font-awesomeicon
27240  * 
27241  * @constructor
27242  * Create a new alert
27243  * @param {Object} config The config object
27244  */
27245
27246
27247 Roo.bootstrap.Alert = function(config){
27248     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27249     
27250 };
27251
27252 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
27253     
27254     title: '',
27255     html: '',
27256     weight: false,
27257     faicon: false,
27258     
27259     getAutoCreate : function()
27260     {
27261         
27262         var cfg = {
27263             tag : 'div',
27264             cls : 'alert',
27265             cn : [
27266                 {
27267                     tag : 'i',
27268                     cls : 'roo-alert-icon'
27269                     
27270                 },
27271                 {
27272                     tag : 'b',
27273                     cls : 'roo-alert-title',
27274                     html : this.title
27275                 },
27276                 {
27277                     tag : 'span',
27278                     cls : 'roo-alert-text',
27279                     html : this.html
27280                 }
27281             ]
27282         };
27283         
27284         if(this.faicon){
27285             cfg.cn[0].cls += ' fa ' + this.faicon;
27286         }
27287         
27288         if(this.weight){
27289             cfg.cls += ' alert-' + this.weight;
27290         }
27291         
27292         return cfg;
27293     },
27294     
27295     initEvents: function() 
27296     {
27297         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27298     },
27299     
27300     setTitle : function(str)
27301     {
27302         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27303     },
27304     
27305     setText : function(str)
27306     {
27307         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27308     },
27309     
27310     setWeight : function(weight)
27311     {
27312         if(this.weight){
27313             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27314         }
27315         
27316         this.weight = weight;
27317         
27318         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27319     },
27320     
27321     setIcon : function(icon)
27322     {
27323         if(this.faicon){
27324             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27325         }
27326         
27327         this.faicon = icon;
27328         
27329         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27330     },
27331     
27332     hide: function() 
27333     {
27334         this.el.hide();   
27335     },
27336     
27337     show: function() 
27338     {  
27339         this.el.show();   
27340     }
27341     
27342 });
27343
27344  
27345 /*
27346 * Licence: LGPL
27347 */
27348
27349 /**
27350  * @class Roo.bootstrap.UploadCropbox
27351  * @extends Roo.bootstrap.Component
27352  * Bootstrap UploadCropbox class
27353  * @cfg {String} emptyText show when image has been loaded
27354  * @cfg {String} rotateNotify show when image too small to rotate
27355  * @cfg {Number} errorTimeout default 3000
27356  * @cfg {Number} minWidth default 300
27357  * @cfg {Number} minHeight default 300
27358  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27359  * @cfg {Boolean} isDocument (true|false) default false
27360  * @cfg {String} url action url
27361  * @cfg {String} paramName default 'imageUpload'
27362  * @cfg {String} method default POST
27363  * @cfg {Boolean} loadMask (true|false) default true
27364  * @cfg {Boolean} loadingText default 'Loading...'
27365  * 
27366  * @constructor
27367  * Create a new UploadCropbox
27368  * @param {Object} config The config object
27369  */
27370
27371 Roo.bootstrap.UploadCropbox = function(config){
27372     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27373     
27374     this.addEvents({
27375         /**
27376          * @event beforeselectfile
27377          * Fire before select file
27378          * @param {Roo.bootstrap.UploadCropbox} this
27379          */
27380         "beforeselectfile" : true,
27381         /**
27382          * @event initial
27383          * Fire after initEvent
27384          * @param {Roo.bootstrap.UploadCropbox} this
27385          */
27386         "initial" : true,
27387         /**
27388          * @event crop
27389          * Fire after initEvent
27390          * @param {Roo.bootstrap.UploadCropbox} this
27391          * @param {String} data
27392          */
27393         "crop" : true,
27394         /**
27395          * @event prepare
27396          * Fire when preparing the file data
27397          * @param {Roo.bootstrap.UploadCropbox} this
27398          * @param {Object} file
27399          */
27400         "prepare" : true,
27401         /**
27402          * @event exception
27403          * Fire when get exception
27404          * @param {Roo.bootstrap.UploadCropbox} this
27405          * @param {XMLHttpRequest} xhr
27406          */
27407         "exception" : true,
27408         /**
27409          * @event beforeloadcanvas
27410          * Fire before load the canvas
27411          * @param {Roo.bootstrap.UploadCropbox} this
27412          * @param {String} src
27413          */
27414         "beforeloadcanvas" : true,
27415         /**
27416          * @event trash
27417          * Fire when trash image
27418          * @param {Roo.bootstrap.UploadCropbox} this
27419          */
27420         "trash" : true,
27421         /**
27422          * @event download
27423          * Fire when download the image
27424          * @param {Roo.bootstrap.UploadCropbox} this
27425          */
27426         "download" : true,
27427         /**
27428          * @event footerbuttonclick
27429          * Fire when footerbuttonclick
27430          * @param {Roo.bootstrap.UploadCropbox} this
27431          * @param {String} type
27432          */
27433         "footerbuttonclick" : true,
27434         /**
27435          * @event resize
27436          * Fire when resize
27437          * @param {Roo.bootstrap.UploadCropbox} this
27438          */
27439         "resize" : true,
27440         /**
27441          * @event rotate
27442          * Fire when rotate the image
27443          * @param {Roo.bootstrap.UploadCropbox} this
27444          * @param {String} pos
27445          */
27446         "rotate" : true,
27447         /**
27448          * @event inspect
27449          * Fire when inspect the file
27450          * @param {Roo.bootstrap.UploadCropbox} this
27451          * @param {Object} file
27452          */
27453         "inspect" : true,
27454         /**
27455          * @event upload
27456          * Fire when xhr upload the file
27457          * @param {Roo.bootstrap.UploadCropbox} this
27458          * @param {Object} data
27459          */
27460         "upload" : true,
27461         /**
27462          * @event arrange
27463          * Fire when arrange the file data
27464          * @param {Roo.bootstrap.UploadCropbox} this
27465          * @param {Object} formData
27466          */
27467         "arrange" : true
27468     });
27469     
27470     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27471 };
27472
27473 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27474     
27475     emptyText : 'Click to upload image',
27476     rotateNotify : 'Image is too small to rotate',
27477     errorTimeout : 3000,
27478     scale : 0,
27479     baseScale : 1,
27480     rotate : 0,
27481     dragable : false,
27482     pinching : false,
27483     mouseX : 0,
27484     mouseY : 0,
27485     cropData : false,
27486     minWidth : 300,
27487     minHeight : 300,
27488     file : false,
27489     exif : {},
27490     baseRotate : 1,
27491     cropType : 'image/jpeg',
27492     buttons : false,
27493     canvasLoaded : false,
27494     isDocument : false,
27495     method : 'POST',
27496     paramName : 'imageUpload',
27497     loadMask : true,
27498     loadingText : 'Loading...',
27499     maskEl : false,
27500     
27501     getAutoCreate : function()
27502     {
27503         var cfg = {
27504             tag : 'div',
27505             cls : 'roo-upload-cropbox',
27506             cn : [
27507                 {
27508                     tag : 'input',
27509                     cls : 'roo-upload-cropbox-selector',
27510                     type : 'file'
27511                 },
27512                 {
27513                     tag : 'div',
27514                     cls : 'roo-upload-cropbox-body',
27515                     style : 'cursor:pointer',
27516                     cn : [
27517                         {
27518                             tag : 'div',
27519                             cls : 'roo-upload-cropbox-preview'
27520                         },
27521                         {
27522                             tag : 'div',
27523                             cls : 'roo-upload-cropbox-thumb'
27524                         },
27525                         {
27526                             tag : 'div',
27527                             cls : 'roo-upload-cropbox-empty-notify',
27528                             html : this.emptyText
27529                         },
27530                         {
27531                             tag : 'div',
27532                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27533                             html : this.rotateNotify
27534                         }
27535                     ]
27536                 },
27537                 {
27538                     tag : 'div',
27539                     cls : 'roo-upload-cropbox-footer',
27540                     cn : {
27541                         tag : 'div',
27542                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27543                         cn : []
27544                     }
27545                 }
27546             ]
27547         };
27548         
27549         return cfg;
27550     },
27551     
27552     onRender : function(ct, position)
27553     {
27554         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27555         
27556         if (this.buttons.length) {
27557             
27558             Roo.each(this.buttons, function(bb) {
27559                 
27560                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27561                 
27562                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27563                 
27564             }, this);
27565         }
27566         
27567         if(this.loadMask){
27568             this.maskEl = this.el;
27569         }
27570     },
27571     
27572     initEvents : function()
27573     {
27574         this.urlAPI = (window.createObjectURL && window) || 
27575                                 (window.URL && URL.revokeObjectURL && URL) || 
27576                                 (window.webkitURL && webkitURL);
27577                         
27578         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27579         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27580         
27581         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27582         this.selectorEl.hide();
27583         
27584         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27585         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27586         
27587         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27588         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27589         this.thumbEl.hide();
27590         
27591         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27592         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27593         
27594         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27595         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27596         this.errorEl.hide();
27597         
27598         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27599         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27600         this.footerEl.hide();
27601         
27602         this.setThumbBoxSize();
27603         
27604         this.bind();
27605         
27606         this.resize();
27607         
27608         this.fireEvent('initial', this);
27609     },
27610
27611     bind : function()
27612     {
27613         var _this = this;
27614         
27615         window.addEventListener("resize", function() { _this.resize(); } );
27616         
27617         this.bodyEl.on('click', this.beforeSelectFile, this);
27618         
27619         if(Roo.isTouch){
27620             this.bodyEl.on('touchstart', this.onTouchStart, this);
27621             this.bodyEl.on('touchmove', this.onTouchMove, this);
27622             this.bodyEl.on('touchend', this.onTouchEnd, this);
27623         }
27624         
27625         if(!Roo.isTouch){
27626             this.bodyEl.on('mousedown', this.onMouseDown, this);
27627             this.bodyEl.on('mousemove', this.onMouseMove, this);
27628             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27629             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27630             Roo.get(document).on('mouseup', this.onMouseUp, this);
27631         }
27632         
27633         this.selectorEl.on('change', this.onFileSelected, this);
27634     },
27635     
27636     reset : function()
27637     {    
27638         this.scale = 0;
27639         this.baseScale = 1;
27640         this.rotate = 0;
27641         this.baseRotate = 1;
27642         this.dragable = false;
27643         this.pinching = false;
27644         this.mouseX = 0;
27645         this.mouseY = 0;
27646         this.cropData = false;
27647         this.notifyEl.dom.innerHTML = this.emptyText;
27648         
27649         this.selectorEl.dom.value = '';
27650         
27651     },
27652     
27653     resize : function()
27654     {
27655         if(this.fireEvent('resize', this) != false){
27656             this.setThumbBoxPosition();
27657             this.setCanvasPosition();
27658         }
27659     },
27660     
27661     onFooterButtonClick : function(e, el, o, type)
27662     {
27663         switch (type) {
27664             case 'rotate-left' :
27665                 this.onRotateLeft(e);
27666                 break;
27667             case 'rotate-right' :
27668                 this.onRotateRight(e);
27669                 break;
27670             case 'picture' :
27671                 this.beforeSelectFile(e);
27672                 break;
27673             case 'trash' :
27674                 this.trash(e);
27675                 break;
27676             case 'crop' :
27677                 this.crop(e);
27678                 break;
27679             case 'download' :
27680                 this.download(e);
27681                 break;
27682             default :
27683                 break;
27684         }
27685         
27686         this.fireEvent('footerbuttonclick', this, type);
27687     },
27688     
27689     beforeSelectFile : function(e)
27690     {
27691         e.preventDefault();
27692         
27693         if(this.fireEvent('beforeselectfile', this) != false){
27694             this.selectorEl.dom.click();
27695         }
27696     },
27697     
27698     onFileSelected : function(e)
27699     {
27700         e.preventDefault();
27701         
27702         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27703             return;
27704         }
27705         
27706         var file = this.selectorEl.dom.files[0];
27707         
27708         if(this.fireEvent('inspect', this, file) != false){
27709             this.prepare(file);
27710         }
27711         
27712     },
27713     
27714     trash : function(e)
27715     {
27716         this.fireEvent('trash', this);
27717     },
27718     
27719     download : function(e)
27720     {
27721         this.fireEvent('download', this);
27722     },
27723     
27724     loadCanvas : function(src)
27725     {   
27726         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27727             
27728             this.reset();
27729             
27730             this.imageEl = document.createElement('img');
27731             
27732             var _this = this;
27733             
27734             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27735             
27736             this.imageEl.src = src;
27737         }
27738     },
27739     
27740     onLoadCanvas : function()
27741     {   
27742         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27743         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27744         
27745         this.bodyEl.un('click', this.beforeSelectFile, this);
27746         
27747         this.notifyEl.hide();
27748         this.thumbEl.show();
27749         this.footerEl.show();
27750         
27751         this.baseRotateLevel();
27752         
27753         if(this.isDocument){
27754             this.setThumbBoxSize();
27755         }
27756         
27757         this.setThumbBoxPosition();
27758         
27759         this.baseScaleLevel();
27760         
27761         this.draw();
27762         
27763         this.resize();
27764         
27765         this.canvasLoaded = true;
27766         
27767         if(this.loadMask){
27768             this.maskEl.unmask();
27769         }
27770         
27771     },
27772     
27773     setCanvasPosition : function()
27774     {   
27775         if(!this.canvasEl){
27776             return;
27777         }
27778         
27779         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27780         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27781         
27782         this.previewEl.setLeft(pw);
27783         this.previewEl.setTop(ph);
27784         
27785     },
27786     
27787     onMouseDown : function(e)
27788     {   
27789         e.stopEvent();
27790         
27791         this.dragable = true;
27792         this.pinching = false;
27793         
27794         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27795             this.dragable = false;
27796             return;
27797         }
27798         
27799         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27800         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27801         
27802     },
27803     
27804     onMouseMove : function(e)
27805     {   
27806         e.stopEvent();
27807         
27808         if(!this.canvasLoaded){
27809             return;
27810         }
27811         
27812         if (!this.dragable){
27813             return;
27814         }
27815         
27816         var minX = Math.ceil(this.thumbEl.getLeft(true));
27817         var minY = Math.ceil(this.thumbEl.getTop(true));
27818         
27819         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27820         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27821         
27822         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27823         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27824         
27825         x = x - this.mouseX;
27826         y = y - this.mouseY;
27827         
27828         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27829         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27830         
27831         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27832         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27833         
27834         this.previewEl.setLeft(bgX);
27835         this.previewEl.setTop(bgY);
27836         
27837         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27838         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27839     },
27840     
27841     onMouseUp : function(e)
27842     {   
27843         e.stopEvent();
27844         
27845         this.dragable = false;
27846     },
27847     
27848     onMouseWheel : function(e)
27849     {   
27850         e.stopEvent();
27851         
27852         this.startScale = this.scale;
27853         
27854         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27855         
27856         if(!this.zoomable()){
27857             this.scale = this.startScale;
27858             return;
27859         }
27860         
27861         this.draw();
27862         
27863         return;
27864     },
27865     
27866     zoomable : function()
27867     {
27868         var minScale = this.thumbEl.getWidth() / this.minWidth;
27869         
27870         if(this.minWidth < this.minHeight){
27871             minScale = this.thumbEl.getHeight() / this.minHeight;
27872         }
27873         
27874         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27875         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27876         
27877         if(
27878                 this.isDocument &&
27879                 (this.rotate == 0 || this.rotate == 180) && 
27880                 (
27881                     width > this.imageEl.OriginWidth || 
27882                     height > this.imageEl.OriginHeight ||
27883                     (width < this.minWidth && height < this.minHeight)
27884                 )
27885         ){
27886             return false;
27887         }
27888         
27889         if(
27890                 this.isDocument &&
27891                 (this.rotate == 90 || this.rotate == 270) && 
27892                 (
27893                     width > this.imageEl.OriginWidth || 
27894                     height > this.imageEl.OriginHeight ||
27895                     (width < this.minHeight && height < this.minWidth)
27896                 )
27897         ){
27898             return false;
27899         }
27900         
27901         if(
27902                 !this.isDocument &&
27903                 (this.rotate == 0 || this.rotate == 180) && 
27904                 (
27905                     width < this.minWidth || 
27906                     width > this.imageEl.OriginWidth || 
27907                     height < this.minHeight || 
27908                     height > this.imageEl.OriginHeight
27909                 )
27910         ){
27911             return false;
27912         }
27913         
27914         if(
27915                 !this.isDocument &&
27916                 (this.rotate == 90 || this.rotate == 270) && 
27917                 (
27918                     width < this.minHeight || 
27919                     width > this.imageEl.OriginWidth || 
27920                     height < this.minWidth || 
27921                     height > this.imageEl.OriginHeight
27922                 )
27923         ){
27924             return false;
27925         }
27926         
27927         return true;
27928         
27929     },
27930     
27931     onRotateLeft : function(e)
27932     {   
27933         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27934             
27935             var minScale = this.thumbEl.getWidth() / this.minWidth;
27936             
27937             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27938             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27939             
27940             this.startScale = this.scale;
27941             
27942             while (this.getScaleLevel() < minScale){
27943             
27944                 this.scale = this.scale + 1;
27945                 
27946                 if(!this.zoomable()){
27947                     break;
27948                 }
27949                 
27950                 if(
27951                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27952                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27953                 ){
27954                     continue;
27955                 }
27956                 
27957                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27958
27959                 this.draw();
27960                 
27961                 return;
27962             }
27963             
27964             this.scale = this.startScale;
27965             
27966             this.onRotateFail();
27967             
27968             return false;
27969         }
27970         
27971         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27972
27973         if(this.isDocument){
27974             this.setThumbBoxSize();
27975             this.setThumbBoxPosition();
27976             this.setCanvasPosition();
27977         }
27978         
27979         this.draw();
27980         
27981         this.fireEvent('rotate', this, 'left');
27982         
27983     },
27984     
27985     onRotateRight : function(e)
27986     {
27987         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27988             
27989             var minScale = this.thumbEl.getWidth() / this.minWidth;
27990         
27991             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27992             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27993             
27994             this.startScale = this.scale;
27995             
27996             while (this.getScaleLevel() < minScale){
27997             
27998                 this.scale = this.scale + 1;
27999                 
28000                 if(!this.zoomable()){
28001                     break;
28002                 }
28003                 
28004                 if(
28005                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28006                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28007                 ){
28008                     continue;
28009                 }
28010                 
28011                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28012
28013                 this.draw();
28014                 
28015                 return;
28016             }
28017             
28018             this.scale = this.startScale;
28019             
28020             this.onRotateFail();
28021             
28022             return false;
28023         }
28024         
28025         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28026
28027         if(this.isDocument){
28028             this.setThumbBoxSize();
28029             this.setThumbBoxPosition();
28030             this.setCanvasPosition();
28031         }
28032         
28033         this.draw();
28034         
28035         this.fireEvent('rotate', this, 'right');
28036     },
28037     
28038     onRotateFail : function()
28039     {
28040         this.errorEl.show(true);
28041         
28042         var _this = this;
28043         
28044         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
28045     },
28046     
28047     draw : function()
28048     {
28049         this.previewEl.dom.innerHTML = '';
28050         
28051         var canvasEl = document.createElement("canvas");
28052         
28053         var contextEl = canvasEl.getContext("2d");
28054         
28055         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28056         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28057         var center = this.imageEl.OriginWidth / 2;
28058         
28059         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
28060             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28061             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28062             center = this.imageEl.OriginHeight / 2;
28063         }
28064         
28065         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
28066         
28067         contextEl.translate(center, center);
28068         contextEl.rotate(this.rotate * Math.PI / 180);
28069
28070         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28071         
28072         this.canvasEl = document.createElement("canvas");
28073         
28074         this.contextEl = this.canvasEl.getContext("2d");
28075         
28076         switch (this.rotate) {
28077             case 0 :
28078                 
28079                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28080                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28081                 
28082                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28083                 
28084                 break;
28085             case 90 : 
28086                 
28087                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28088                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28089                 
28090                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28091                     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);
28092                     break;
28093                 }
28094                 
28095                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28096                 
28097                 break;
28098             case 180 :
28099                 
28100                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28101                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28102                 
28103                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28104                     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);
28105                     break;
28106                 }
28107                 
28108                 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);
28109                 
28110                 break;
28111             case 270 :
28112                 
28113                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28114                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28115         
28116                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28117                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28118                     break;
28119                 }
28120                 
28121                 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);
28122                 
28123                 break;
28124             default : 
28125                 break;
28126         }
28127         
28128         this.previewEl.appendChild(this.canvasEl);
28129         
28130         this.setCanvasPosition();
28131     },
28132     
28133     crop : function()
28134     {
28135         if(!this.canvasLoaded){
28136             return;
28137         }
28138         
28139         var imageCanvas = document.createElement("canvas");
28140         
28141         var imageContext = imageCanvas.getContext("2d");
28142         
28143         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28144         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28145         
28146         var center = imageCanvas.width / 2;
28147         
28148         imageContext.translate(center, center);
28149         
28150         imageContext.rotate(this.rotate * Math.PI / 180);
28151         
28152         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28153         
28154         var canvas = document.createElement("canvas");
28155         
28156         var context = canvas.getContext("2d");
28157                 
28158         canvas.width = this.minWidth;
28159         canvas.height = this.minHeight;
28160
28161         switch (this.rotate) {
28162             case 0 :
28163                 
28164                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28165                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28166                 
28167                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28168                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28169                 
28170                 var targetWidth = this.minWidth - 2 * x;
28171                 var targetHeight = this.minHeight - 2 * y;
28172                 
28173                 var scale = 1;
28174                 
28175                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28176                     scale = targetWidth / width;
28177                 }
28178                 
28179                 if(x > 0 && y == 0){
28180                     scale = targetHeight / height;
28181                 }
28182                 
28183                 if(x > 0 && y > 0){
28184                     scale = targetWidth / width;
28185                     
28186                     if(width < height){
28187                         scale = targetHeight / height;
28188                     }
28189                 }
28190                 
28191                 context.scale(scale, scale);
28192                 
28193                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28194                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28195
28196                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28197                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28198
28199                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28200                 
28201                 break;
28202             case 90 : 
28203                 
28204                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28205                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28206                 
28207                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28208                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28209                 
28210                 var targetWidth = this.minWidth - 2 * x;
28211                 var targetHeight = this.minHeight - 2 * y;
28212                 
28213                 var scale = 1;
28214                 
28215                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28216                     scale = targetWidth / width;
28217                 }
28218                 
28219                 if(x > 0 && y == 0){
28220                     scale = targetHeight / height;
28221                 }
28222                 
28223                 if(x > 0 && y > 0){
28224                     scale = targetWidth / width;
28225                     
28226                     if(width < height){
28227                         scale = targetHeight / height;
28228                     }
28229                 }
28230                 
28231                 context.scale(scale, scale);
28232                 
28233                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28234                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28235
28236                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28237                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28238                 
28239                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28240                 
28241                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28242                 
28243                 break;
28244             case 180 :
28245                 
28246                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28247                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28248                 
28249                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28250                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28251                 
28252                 var targetWidth = this.minWidth - 2 * x;
28253                 var targetHeight = this.minHeight - 2 * y;
28254                 
28255                 var scale = 1;
28256                 
28257                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28258                     scale = targetWidth / width;
28259                 }
28260                 
28261                 if(x > 0 && y == 0){
28262                     scale = targetHeight / height;
28263                 }
28264                 
28265                 if(x > 0 && y > 0){
28266                     scale = targetWidth / width;
28267                     
28268                     if(width < height){
28269                         scale = targetHeight / height;
28270                     }
28271                 }
28272                 
28273                 context.scale(scale, scale);
28274                 
28275                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28276                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28277
28278                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28279                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28280
28281                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28282                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28283                 
28284                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28285                 
28286                 break;
28287             case 270 :
28288                 
28289                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28290                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28291                 
28292                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28293                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28294                 
28295                 var targetWidth = this.minWidth - 2 * x;
28296                 var targetHeight = this.minHeight - 2 * y;
28297                 
28298                 var scale = 1;
28299                 
28300                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28301                     scale = targetWidth / width;
28302                 }
28303                 
28304                 if(x > 0 && y == 0){
28305                     scale = targetHeight / height;
28306                 }
28307                 
28308                 if(x > 0 && y > 0){
28309                     scale = targetWidth / width;
28310                     
28311                     if(width < height){
28312                         scale = targetHeight / height;
28313                     }
28314                 }
28315                 
28316                 context.scale(scale, scale);
28317                 
28318                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28319                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28320
28321                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28322                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28323                 
28324                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28325                 
28326                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28327                 
28328                 break;
28329             default : 
28330                 break;
28331         }
28332         
28333         this.cropData = canvas.toDataURL(this.cropType);
28334         
28335         if(this.fireEvent('crop', this, this.cropData) !== false){
28336             this.process(this.file, this.cropData);
28337         }
28338         
28339         return;
28340         
28341     },
28342     
28343     setThumbBoxSize : function()
28344     {
28345         var width, height;
28346         
28347         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28348             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28349             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28350             
28351             this.minWidth = width;
28352             this.minHeight = height;
28353             
28354             if(this.rotate == 90 || this.rotate == 270){
28355                 this.minWidth = height;
28356                 this.minHeight = width;
28357             }
28358         }
28359         
28360         height = 300;
28361         width = Math.ceil(this.minWidth * height / this.minHeight);
28362         
28363         if(this.minWidth > this.minHeight){
28364             width = 300;
28365             height = Math.ceil(this.minHeight * width / this.minWidth);
28366         }
28367         
28368         this.thumbEl.setStyle({
28369             width : width + 'px',
28370             height : height + 'px'
28371         });
28372
28373         return;
28374             
28375     },
28376     
28377     setThumbBoxPosition : function()
28378     {
28379         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28380         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28381         
28382         this.thumbEl.setLeft(x);
28383         this.thumbEl.setTop(y);
28384         
28385     },
28386     
28387     baseRotateLevel : function()
28388     {
28389         this.baseRotate = 1;
28390         
28391         if(
28392                 typeof(this.exif) != 'undefined' &&
28393                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28394                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28395         ){
28396             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28397         }
28398         
28399         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28400         
28401     },
28402     
28403     baseScaleLevel : function()
28404     {
28405         var width, height;
28406         
28407         if(this.isDocument){
28408             
28409             if(this.baseRotate == 6 || this.baseRotate == 8){
28410             
28411                 height = this.thumbEl.getHeight();
28412                 this.baseScale = height / this.imageEl.OriginWidth;
28413
28414                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28415                     width = this.thumbEl.getWidth();
28416                     this.baseScale = width / this.imageEl.OriginHeight;
28417                 }
28418
28419                 return;
28420             }
28421
28422             height = this.thumbEl.getHeight();
28423             this.baseScale = height / this.imageEl.OriginHeight;
28424
28425             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28426                 width = this.thumbEl.getWidth();
28427                 this.baseScale = width / this.imageEl.OriginWidth;
28428             }
28429
28430             return;
28431         }
28432         
28433         if(this.baseRotate == 6 || this.baseRotate == 8){
28434             
28435             width = this.thumbEl.getHeight();
28436             this.baseScale = width / this.imageEl.OriginHeight;
28437             
28438             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28439                 height = this.thumbEl.getWidth();
28440                 this.baseScale = height / this.imageEl.OriginHeight;
28441             }
28442             
28443             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28444                 height = this.thumbEl.getWidth();
28445                 this.baseScale = height / this.imageEl.OriginHeight;
28446                 
28447                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28448                     width = this.thumbEl.getHeight();
28449                     this.baseScale = width / this.imageEl.OriginWidth;
28450                 }
28451             }
28452             
28453             return;
28454         }
28455         
28456         width = this.thumbEl.getWidth();
28457         this.baseScale = width / this.imageEl.OriginWidth;
28458         
28459         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28460             height = this.thumbEl.getHeight();
28461             this.baseScale = height / this.imageEl.OriginHeight;
28462         }
28463         
28464         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28465             
28466             height = this.thumbEl.getHeight();
28467             this.baseScale = height / this.imageEl.OriginHeight;
28468             
28469             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28470                 width = this.thumbEl.getWidth();
28471                 this.baseScale = width / this.imageEl.OriginWidth;
28472             }
28473             
28474         }
28475         
28476         return;
28477     },
28478     
28479     getScaleLevel : function()
28480     {
28481         return this.baseScale * Math.pow(1.1, this.scale);
28482     },
28483     
28484     onTouchStart : function(e)
28485     {
28486         if(!this.canvasLoaded){
28487             this.beforeSelectFile(e);
28488             return;
28489         }
28490         
28491         var touches = e.browserEvent.touches;
28492         
28493         if(!touches){
28494             return;
28495         }
28496         
28497         if(touches.length == 1){
28498             this.onMouseDown(e);
28499             return;
28500         }
28501         
28502         if(touches.length != 2){
28503             return;
28504         }
28505         
28506         var coords = [];
28507         
28508         for(var i = 0, finger; finger = touches[i]; i++){
28509             coords.push(finger.pageX, finger.pageY);
28510         }
28511         
28512         var x = Math.pow(coords[0] - coords[2], 2);
28513         var y = Math.pow(coords[1] - coords[3], 2);
28514         
28515         this.startDistance = Math.sqrt(x + y);
28516         
28517         this.startScale = this.scale;
28518         
28519         this.pinching = true;
28520         this.dragable = false;
28521         
28522     },
28523     
28524     onTouchMove : function(e)
28525     {
28526         if(!this.pinching && !this.dragable){
28527             return;
28528         }
28529         
28530         var touches = e.browserEvent.touches;
28531         
28532         if(!touches){
28533             return;
28534         }
28535         
28536         if(this.dragable){
28537             this.onMouseMove(e);
28538             return;
28539         }
28540         
28541         var coords = [];
28542         
28543         for(var i = 0, finger; finger = touches[i]; i++){
28544             coords.push(finger.pageX, finger.pageY);
28545         }
28546         
28547         var x = Math.pow(coords[0] - coords[2], 2);
28548         var y = Math.pow(coords[1] - coords[3], 2);
28549         
28550         this.endDistance = Math.sqrt(x + y);
28551         
28552         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28553         
28554         if(!this.zoomable()){
28555             this.scale = this.startScale;
28556             return;
28557         }
28558         
28559         this.draw();
28560         
28561     },
28562     
28563     onTouchEnd : function(e)
28564     {
28565         this.pinching = false;
28566         this.dragable = false;
28567         
28568     },
28569     
28570     process : function(file, crop)
28571     {
28572         if(this.loadMask){
28573             this.maskEl.mask(this.loadingText);
28574         }
28575         
28576         this.xhr = new XMLHttpRequest();
28577         
28578         file.xhr = this.xhr;
28579
28580         this.xhr.open(this.method, this.url, true);
28581         
28582         var headers = {
28583             "Accept": "application/json",
28584             "Cache-Control": "no-cache",
28585             "X-Requested-With": "XMLHttpRequest"
28586         };
28587         
28588         for (var headerName in headers) {
28589             var headerValue = headers[headerName];
28590             if (headerValue) {
28591                 this.xhr.setRequestHeader(headerName, headerValue);
28592             }
28593         }
28594         
28595         var _this = this;
28596         
28597         this.xhr.onload = function()
28598         {
28599             _this.xhrOnLoad(_this.xhr);
28600         }
28601         
28602         this.xhr.onerror = function()
28603         {
28604             _this.xhrOnError(_this.xhr);
28605         }
28606         
28607         var formData = new FormData();
28608
28609         formData.append('returnHTML', 'NO');
28610         
28611         if(crop){
28612             formData.append('crop', crop);
28613         }
28614         
28615         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28616             formData.append(this.paramName, file, file.name);
28617         }
28618         
28619         if(typeof(file.filename) != 'undefined'){
28620             formData.append('filename', file.filename);
28621         }
28622         
28623         if(typeof(file.mimetype) != 'undefined'){
28624             formData.append('mimetype', file.mimetype);
28625         }
28626         
28627         if(this.fireEvent('arrange', this, formData) != false){
28628             this.xhr.send(formData);
28629         };
28630     },
28631     
28632     xhrOnLoad : function(xhr)
28633     {
28634         if(this.loadMask){
28635             this.maskEl.unmask();
28636         }
28637         
28638         if (xhr.readyState !== 4) {
28639             this.fireEvent('exception', this, xhr);
28640             return;
28641         }
28642
28643         var response = Roo.decode(xhr.responseText);
28644         
28645         if(!response.success){
28646             this.fireEvent('exception', this, xhr);
28647             return;
28648         }
28649         
28650         var response = Roo.decode(xhr.responseText);
28651         
28652         this.fireEvent('upload', this, response);
28653         
28654     },
28655     
28656     xhrOnError : function()
28657     {
28658         if(this.loadMask){
28659             this.maskEl.unmask();
28660         }
28661         
28662         Roo.log('xhr on error');
28663         
28664         var response = Roo.decode(xhr.responseText);
28665           
28666         Roo.log(response);
28667         
28668     },
28669     
28670     prepare : function(file)
28671     {   
28672         if(this.loadMask){
28673             this.maskEl.mask(this.loadingText);
28674         }
28675         
28676         this.file = false;
28677         this.exif = {};
28678         
28679         if(typeof(file) === 'string'){
28680             this.loadCanvas(file);
28681             return;
28682         }
28683         
28684         if(!file || !this.urlAPI){
28685             return;
28686         }
28687         
28688         this.file = file;
28689         this.cropType = file.type;
28690         
28691         var _this = this;
28692         
28693         if(this.fireEvent('prepare', this, this.file) != false){
28694             
28695             var reader = new FileReader();
28696             
28697             reader.onload = function (e) {
28698                 if (e.target.error) {
28699                     Roo.log(e.target.error);
28700                     return;
28701                 }
28702                 
28703                 var buffer = e.target.result,
28704                     dataView = new DataView(buffer),
28705                     offset = 2,
28706                     maxOffset = dataView.byteLength - 4,
28707                     markerBytes,
28708                     markerLength;
28709                 
28710                 if (dataView.getUint16(0) === 0xffd8) {
28711                     while (offset < maxOffset) {
28712                         markerBytes = dataView.getUint16(offset);
28713                         
28714                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28715                             markerLength = dataView.getUint16(offset + 2) + 2;
28716                             if (offset + markerLength > dataView.byteLength) {
28717                                 Roo.log('Invalid meta data: Invalid segment size.');
28718                                 break;
28719                             }
28720                             
28721                             if(markerBytes == 0xffe1){
28722                                 _this.parseExifData(
28723                                     dataView,
28724                                     offset,
28725                                     markerLength
28726                                 );
28727                             }
28728                             
28729                             offset += markerLength;
28730                             
28731                             continue;
28732                         }
28733                         
28734                         break;
28735                     }
28736                     
28737                 }
28738                 
28739                 var url = _this.urlAPI.createObjectURL(_this.file);
28740                 
28741                 _this.loadCanvas(url);
28742                 
28743                 return;
28744             }
28745             
28746             reader.readAsArrayBuffer(this.file);
28747             
28748         }
28749         
28750     },
28751     
28752     parseExifData : function(dataView, offset, length)
28753     {
28754         var tiffOffset = offset + 10,
28755             littleEndian,
28756             dirOffset;
28757     
28758         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28759             // No Exif data, might be XMP data instead
28760             return;
28761         }
28762         
28763         // Check for the ASCII code for "Exif" (0x45786966):
28764         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28765             // No Exif data, might be XMP data instead
28766             return;
28767         }
28768         if (tiffOffset + 8 > dataView.byteLength) {
28769             Roo.log('Invalid Exif data: Invalid segment size.');
28770             return;
28771         }
28772         // Check for the two null bytes:
28773         if (dataView.getUint16(offset + 8) !== 0x0000) {
28774             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28775             return;
28776         }
28777         // Check the byte alignment:
28778         switch (dataView.getUint16(tiffOffset)) {
28779         case 0x4949:
28780             littleEndian = true;
28781             break;
28782         case 0x4D4D:
28783             littleEndian = false;
28784             break;
28785         default:
28786             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28787             return;
28788         }
28789         // Check for the TIFF tag marker (0x002A):
28790         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28791             Roo.log('Invalid Exif data: Missing TIFF marker.');
28792             return;
28793         }
28794         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28795         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28796         
28797         this.parseExifTags(
28798             dataView,
28799             tiffOffset,
28800             tiffOffset + dirOffset,
28801             littleEndian
28802         );
28803     },
28804     
28805     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28806     {
28807         var tagsNumber,
28808             dirEndOffset,
28809             i;
28810         if (dirOffset + 6 > dataView.byteLength) {
28811             Roo.log('Invalid Exif data: Invalid directory offset.');
28812             return;
28813         }
28814         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28815         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28816         if (dirEndOffset + 4 > dataView.byteLength) {
28817             Roo.log('Invalid Exif data: Invalid directory size.');
28818             return;
28819         }
28820         for (i = 0; i < tagsNumber; i += 1) {
28821             this.parseExifTag(
28822                 dataView,
28823                 tiffOffset,
28824                 dirOffset + 2 + 12 * i, // tag offset
28825                 littleEndian
28826             );
28827         }
28828         // Return the offset to the next directory:
28829         return dataView.getUint32(dirEndOffset, littleEndian);
28830     },
28831     
28832     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28833     {
28834         var tag = dataView.getUint16(offset, littleEndian);
28835         
28836         this.exif[tag] = this.getExifValue(
28837             dataView,
28838             tiffOffset,
28839             offset,
28840             dataView.getUint16(offset + 2, littleEndian), // tag type
28841             dataView.getUint32(offset + 4, littleEndian), // tag length
28842             littleEndian
28843         );
28844     },
28845     
28846     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28847     {
28848         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28849             tagSize,
28850             dataOffset,
28851             values,
28852             i,
28853             str,
28854             c;
28855     
28856         if (!tagType) {
28857             Roo.log('Invalid Exif data: Invalid tag type.');
28858             return;
28859         }
28860         
28861         tagSize = tagType.size * length;
28862         // Determine if the value is contained in the dataOffset bytes,
28863         // or if the value at the dataOffset is a pointer to the actual data:
28864         dataOffset = tagSize > 4 ?
28865                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28866         if (dataOffset + tagSize > dataView.byteLength) {
28867             Roo.log('Invalid Exif data: Invalid data offset.');
28868             return;
28869         }
28870         if (length === 1) {
28871             return tagType.getValue(dataView, dataOffset, littleEndian);
28872         }
28873         values = [];
28874         for (i = 0; i < length; i += 1) {
28875             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28876         }
28877         
28878         if (tagType.ascii) {
28879             str = '';
28880             // Concatenate the chars:
28881             for (i = 0; i < values.length; i += 1) {
28882                 c = values[i];
28883                 // Ignore the terminating NULL byte(s):
28884                 if (c === '\u0000') {
28885                     break;
28886                 }
28887                 str += c;
28888             }
28889             return str;
28890         }
28891         return values;
28892     }
28893     
28894 });
28895
28896 Roo.apply(Roo.bootstrap.UploadCropbox, {
28897     tags : {
28898         'Orientation': 0x0112
28899     },
28900     
28901     Orientation: {
28902             1: 0, //'top-left',
28903 //            2: 'top-right',
28904             3: 180, //'bottom-right',
28905 //            4: 'bottom-left',
28906 //            5: 'left-top',
28907             6: 90, //'right-top',
28908 //            7: 'right-bottom',
28909             8: 270 //'left-bottom'
28910     },
28911     
28912     exifTagTypes : {
28913         // byte, 8-bit unsigned int:
28914         1: {
28915             getValue: function (dataView, dataOffset) {
28916                 return dataView.getUint8(dataOffset);
28917             },
28918             size: 1
28919         },
28920         // ascii, 8-bit byte:
28921         2: {
28922             getValue: function (dataView, dataOffset) {
28923                 return String.fromCharCode(dataView.getUint8(dataOffset));
28924             },
28925             size: 1,
28926             ascii: true
28927         },
28928         // short, 16 bit int:
28929         3: {
28930             getValue: function (dataView, dataOffset, littleEndian) {
28931                 return dataView.getUint16(dataOffset, littleEndian);
28932             },
28933             size: 2
28934         },
28935         // long, 32 bit int:
28936         4: {
28937             getValue: function (dataView, dataOffset, littleEndian) {
28938                 return dataView.getUint32(dataOffset, littleEndian);
28939             },
28940             size: 4
28941         },
28942         // rational = two long values, first is numerator, second is denominator:
28943         5: {
28944             getValue: function (dataView, dataOffset, littleEndian) {
28945                 return dataView.getUint32(dataOffset, littleEndian) /
28946                     dataView.getUint32(dataOffset + 4, littleEndian);
28947             },
28948             size: 8
28949         },
28950         // slong, 32 bit signed int:
28951         9: {
28952             getValue: function (dataView, dataOffset, littleEndian) {
28953                 return dataView.getInt32(dataOffset, littleEndian);
28954             },
28955             size: 4
28956         },
28957         // srational, two slongs, first is numerator, second is denominator:
28958         10: {
28959             getValue: function (dataView, dataOffset, littleEndian) {
28960                 return dataView.getInt32(dataOffset, littleEndian) /
28961                     dataView.getInt32(dataOffset + 4, littleEndian);
28962             },
28963             size: 8
28964         }
28965     },
28966     
28967     footer : {
28968         STANDARD : [
28969             {
28970                 tag : 'div',
28971                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28972                 action : 'rotate-left',
28973                 cn : [
28974                     {
28975                         tag : 'button',
28976                         cls : 'btn btn-default',
28977                         html : '<i class="fa fa-undo"></i>'
28978                     }
28979                 ]
28980             },
28981             {
28982                 tag : 'div',
28983                 cls : 'btn-group roo-upload-cropbox-picture',
28984                 action : 'picture',
28985                 cn : [
28986                     {
28987                         tag : 'button',
28988                         cls : 'btn btn-default',
28989                         html : '<i class="fa fa-picture-o"></i>'
28990                     }
28991                 ]
28992             },
28993             {
28994                 tag : 'div',
28995                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28996                 action : 'rotate-right',
28997                 cn : [
28998                     {
28999                         tag : 'button',
29000                         cls : 'btn btn-default',
29001                         html : '<i class="fa fa-repeat"></i>'
29002                     }
29003                 ]
29004             }
29005         ],
29006         DOCUMENT : [
29007             {
29008                 tag : 'div',
29009                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29010                 action : 'rotate-left',
29011                 cn : [
29012                     {
29013                         tag : 'button',
29014                         cls : 'btn btn-default',
29015                         html : '<i class="fa fa-undo"></i>'
29016                     }
29017                 ]
29018             },
29019             {
29020                 tag : 'div',
29021                 cls : 'btn-group roo-upload-cropbox-download',
29022                 action : 'download',
29023                 cn : [
29024                     {
29025                         tag : 'button',
29026                         cls : 'btn btn-default',
29027                         html : '<i class="fa fa-download"></i>'
29028                     }
29029                 ]
29030             },
29031             {
29032                 tag : 'div',
29033                 cls : 'btn-group roo-upload-cropbox-crop',
29034                 action : 'crop',
29035                 cn : [
29036                     {
29037                         tag : 'button',
29038                         cls : 'btn btn-default',
29039                         html : '<i class="fa fa-crop"></i>'
29040                     }
29041                 ]
29042             },
29043             {
29044                 tag : 'div',
29045                 cls : 'btn-group roo-upload-cropbox-trash',
29046                 action : 'trash',
29047                 cn : [
29048                     {
29049                         tag : 'button',
29050                         cls : 'btn btn-default',
29051                         html : '<i class="fa fa-trash"></i>'
29052                     }
29053                 ]
29054             },
29055             {
29056                 tag : 'div',
29057                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29058                 action : 'rotate-right',
29059                 cn : [
29060                     {
29061                         tag : 'button',
29062                         cls : 'btn btn-default',
29063                         html : '<i class="fa fa-repeat"></i>'
29064                     }
29065                 ]
29066             }
29067         ],
29068         ROTATOR : [
29069             {
29070                 tag : 'div',
29071                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29072                 action : 'rotate-left',
29073                 cn : [
29074                     {
29075                         tag : 'button',
29076                         cls : 'btn btn-default',
29077                         html : '<i class="fa fa-undo"></i>'
29078                     }
29079                 ]
29080             },
29081             {
29082                 tag : 'div',
29083                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29084                 action : 'rotate-right',
29085                 cn : [
29086                     {
29087                         tag : 'button',
29088                         cls : 'btn btn-default',
29089                         html : '<i class="fa fa-repeat"></i>'
29090                     }
29091                 ]
29092             }
29093         ]
29094     }
29095 });
29096
29097 /*
29098 * Licence: LGPL
29099 */
29100
29101 /**
29102  * @class Roo.bootstrap.DocumentManager
29103  * @extends Roo.bootstrap.Component
29104  * Bootstrap DocumentManager class
29105  * @cfg {String} paramName default 'imageUpload'
29106  * @cfg {String} toolTipName default 'filename'
29107  * @cfg {String} method default POST
29108  * @cfg {String} url action url
29109  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
29110  * @cfg {Boolean} multiple multiple upload default true
29111  * @cfg {Number} thumbSize default 300
29112  * @cfg {String} fieldLabel
29113  * @cfg {Number} labelWidth default 4
29114  * @cfg {String} labelAlign (left|top) default left
29115  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
29116 * @cfg {Number} labellg set the width of label (1-12)
29117  * @cfg {Number} labelmd set the width of label (1-12)
29118  * @cfg {Number} labelsm set the width of label (1-12)
29119  * @cfg {Number} labelxs set the width of label (1-12)
29120  * 
29121  * @constructor
29122  * Create a new DocumentManager
29123  * @param {Object} config The config object
29124  */
29125
29126 Roo.bootstrap.DocumentManager = function(config){
29127     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
29128     
29129     this.files = [];
29130     this.delegates = [];
29131     
29132     this.addEvents({
29133         /**
29134          * @event initial
29135          * Fire when initial the DocumentManager
29136          * @param {Roo.bootstrap.DocumentManager} this
29137          */
29138         "initial" : true,
29139         /**
29140          * @event inspect
29141          * inspect selected file
29142          * @param {Roo.bootstrap.DocumentManager} this
29143          * @param {File} file
29144          */
29145         "inspect" : true,
29146         /**
29147          * @event exception
29148          * Fire when xhr load exception
29149          * @param {Roo.bootstrap.DocumentManager} this
29150          * @param {XMLHttpRequest} xhr
29151          */
29152         "exception" : true,
29153         /**
29154          * @event afterupload
29155          * Fire when xhr load exception
29156          * @param {Roo.bootstrap.DocumentManager} this
29157          * @param {XMLHttpRequest} xhr
29158          */
29159         "afterupload" : true,
29160         /**
29161          * @event prepare
29162          * prepare the form data
29163          * @param {Roo.bootstrap.DocumentManager} this
29164          * @param {Object} formData
29165          */
29166         "prepare" : true,
29167         /**
29168          * @event remove
29169          * Fire when remove the file
29170          * @param {Roo.bootstrap.DocumentManager} this
29171          * @param {Object} file
29172          */
29173         "remove" : true,
29174         /**
29175          * @event refresh
29176          * Fire after refresh the file
29177          * @param {Roo.bootstrap.DocumentManager} this
29178          */
29179         "refresh" : true,
29180         /**
29181          * @event click
29182          * Fire after click the image
29183          * @param {Roo.bootstrap.DocumentManager} this
29184          * @param {Object} file
29185          */
29186         "click" : true,
29187         /**
29188          * @event edit
29189          * Fire when upload a image and editable set to true
29190          * @param {Roo.bootstrap.DocumentManager} this
29191          * @param {Object} file
29192          */
29193         "edit" : true,
29194         /**
29195          * @event beforeselectfile
29196          * Fire before select file
29197          * @param {Roo.bootstrap.DocumentManager} this
29198          */
29199         "beforeselectfile" : true,
29200         /**
29201          * @event process
29202          * Fire before process file
29203          * @param {Roo.bootstrap.DocumentManager} this
29204          * @param {Object} file
29205          */
29206         "process" : true,
29207         /**
29208          * @event previewrendered
29209          * Fire when preview rendered
29210          * @param {Roo.bootstrap.DocumentManager} this
29211          * @param {Object} file
29212          */
29213         "previewrendered" : true,
29214         /**
29215          */
29216         "previewResize" : true
29217         
29218     });
29219 };
29220
29221 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
29222     
29223     boxes : 0,
29224     inputName : '',
29225     thumbSize : 300,
29226     multiple : true,
29227     files : false,
29228     method : 'POST',
29229     url : '',
29230     paramName : 'imageUpload',
29231     toolTipName : 'filename',
29232     fieldLabel : '',
29233     labelWidth : 4,
29234     labelAlign : 'left',
29235     editable : true,
29236     delegates : false,
29237     xhr : false, 
29238     
29239     labellg : 0,
29240     labelmd : 0,
29241     labelsm : 0,
29242     labelxs : 0,
29243     
29244     getAutoCreate : function()
29245     {   
29246         var managerWidget = {
29247             tag : 'div',
29248             cls : 'roo-document-manager',
29249             cn : [
29250                 {
29251                     tag : 'input',
29252                     cls : 'roo-document-manager-selector',
29253                     type : 'file'
29254                 },
29255                 {
29256                     tag : 'div',
29257                     cls : 'roo-document-manager-uploader',
29258                     cn : [
29259                         {
29260                             tag : 'div',
29261                             cls : 'roo-document-manager-upload-btn',
29262                             html : '<i class="fa fa-plus"></i>'
29263                         }
29264                     ]
29265                     
29266                 }
29267             ]
29268         };
29269         
29270         var content = [
29271             {
29272                 tag : 'div',
29273                 cls : 'column col-md-12',
29274                 cn : managerWidget
29275             }
29276         ];
29277         
29278         if(this.fieldLabel.length){
29279             
29280             content = [
29281                 {
29282                     tag : 'div',
29283                     cls : 'column col-md-12',
29284                     html : this.fieldLabel
29285                 },
29286                 {
29287                     tag : 'div',
29288                     cls : 'column col-md-12',
29289                     cn : managerWidget
29290                 }
29291             ];
29292
29293             if(this.labelAlign == 'left'){
29294                 content = [
29295                     {
29296                         tag : 'div',
29297                         cls : 'column',
29298                         html : this.fieldLabel
29299                     },
29300                     {
29301                         tag : 'div',
29302                         cls : 'column',
29303                         cn : managerWidget
29304                     }
29305                 ];
29306                 
29307                 if(this.labelWidth > 12){
29308                     content[0].style = "width: " + this.labelWidth + 'px';
29309                 }
29310
29311                 if(this.labelWidth < 13 && this.labelmd == 0){
29312                     this.labelmd = this.labelWidth;
29313                 }
29314
29315                 if(this.labellg > 0){
29316                     content[0].cls += ' col-lg-' + this.labellg;
29317                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29318                 }
29319
29320                 if(this.labelmd > 0){
29321                     content[0].cls += ' col-md-' + this.labelmd;
29322                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29323                 }
29324
29325                 if(this.labelsm > 0){
29326                     content[0].cls += ' col-sm-' + this.labelsm;
29327                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29328                 }
29329
29330                 if(this.labelxs > 0){
29331                     content[0].cls += ' col-xs-' + this.labelxs;
29332                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29333                 }
29334                 
29335             }
29336         }
29337         
29338         var cfg = {
29339             tag : 'div',
29340             cls : 'row clearfix',
29341             cn : content
29342         };
29343         
29344         return cfg;
29345         
29346     },
29347     
29348     initEvents : function()
29349     {
29350         this.managerEl = this.el.select('.roo-document-manager', true).first();
29351         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29352         
29353         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29354         this.selectorEl.hide();
29355         
29356         if(this.multiple){
29357             this.selectorEl.attr('multiple', 'multiple');
29358         }
29359         
29360         this.selectorEl.on('change', this.onFileSelected, this);
29361         
29362         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29363         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29364         
29365         this.uploader.on('click', this.onUploaderClick, this);
29366         
29367         this.renderProgressDialog();
29368         
29369         var _this = this;
29370         
29371         window.addEventListener("resize", function() { _this.refresh(); } );
29372         
29373         this.fireEvent('initial', this);
29374     },
29375     
29376     renderProgressDialog : function()
29377     {
29378         var _this = this;
29379         
29380         this.progressDialog = new Roo.bootstrap.Modal({
29381             cls : 'roo-document-manager-progress-dialog',
29382             allow_close : false,
29383             animate : false,
29384             title : '',
29385             buttons : [
29386                 {
29387                     name  :'cancel',
29388                     weight : 'danger',
29389                     html : 'Cancel'
29390                 }
29391             ], 
29392             listeners : { 
29393                 btnclick : function() {
29394                     _this.uploadCancel();
29395                     this.hide();
29396                 }
29397             }
29398         });
29399          
29400         this.progressDialog.render(Roo.get(document.body));
29401          
29402         this.progress = new Roo.bootstrap.Progress({
29403             cls : 'roo-document-manager-progress',
29404             active : true,
29405             striped : true
29406         });
29407         
29408         this.progress.render(this.progressDialog.getChildContainer());
29409         
29410         this.progressBar = new Roo.bootstrap.ProgressBar({
29411             cls : 'roo-document-manager-progress-bar',
29412             aria_valuenow : 0,
29413             aria_valuemin : 0,
29414             aria_valuemax : 12,
29415             panel : 'success'
29416         });
29417         
29418         this.progressBar.render(this.progress.getChildContainer());
29419     },
29420     
29421     onUploaderClick : function(e)
29422     {
29423         e.preventDefault();
29424      
29425         if(this.fireEvent('beforeselectfile', this) != false){
29426             this.selectorEl.dom.click();
29427         }
29428         
29429     },
29430     
29431     onFileSelected : function(e)
29432     {
29433         e.preventDefault();
29434         
29435         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29436             return;
29437         }
29438         
29439         Roo.each(this.selectorEl.dom.files, function(file){
29440             if(this.fireEvent('inspect', this, file) != false){
29441                 this.files.push(file);
29442             }
29443         }, this);
29444         
29445         this.queue();
29446         
29447     },
29448     
29449     queue : function()
29450     {
29451         this.selectorEl.dom.value = '';
29452         
29453         if(!this.files || !this.files.length){
29454             return;
29455         }
29456         
29457         if(this.boxes > 0 && this.files.length > this.boxes){
29458             this.files = this.files.slice(0, this.boxes);
29459         }
29460         
29461         this.uploader.show();
29462         
29463         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29464             this.uploader.hide();
29465         }
29466         
29467         var _this = this;
29468         
29469         var files = [];
29470         
29471         var docs = [];
29472         
29473         Roo.each(this.files, function(file){
29474             
29475             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29476                 var f = this.renderPreview(file);
29477                 files.push(f);
29478                 return;
29479             }
29480             
29481             if(file.type.indexOf('image') != -1){
29482                 this.delegates.push(
29483                     (function(){
29484                         _this.process(file);
29485                     }).createDelegate(this)
29486                 );
29487         
29488                 return;
29489             }
29490             
29491             docs.push(
29492                 (function(){
29493                     _this.process(file);
29494                 }).createDelegate(this)
29495             );
29496             
29497         }, this);
29498         
29499         this.files = files;
29500         
29501         this.delegates = this.delegates.concat(docs);
29502         
29503         if(!this.delegates.length){
29504             this.refresh();
29505             return;
29506         }
29507         
29508         this.progressBar.aria_valuemax = this.delegates.length;
29509         
29510         this.arrange();
29511         
29512         return;
29513     },
29514     
29515     arrange : function()
29516     {
29517         if(!this.delegates.length){
29518             this.progressDialog.hide();
29519             this.refresh();
29520             return;
29521         }
29522         
29523         var delegate = this.delegates.shift();
29524         
29525         this.progressDialog.show();
29526         
29527         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29528         
29529         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29530         
29531         delegate();
29532     },
29533     
29534     refresh : function()
29535     {
29536         this.uploader.show();
29537         
29538         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29539             this.uploader.hide();
29540         }
29541         
29542         Roo.isTouch ? this.closable(false) : this.closable(true);
29543         
29544         this.fireEvent('refresh', this);
29545     },
29546     
29547     onRemove : function(e, el, o)
29548     {
29549         e.preventDefault();
29550         
29551         this.fireEvent('remove', this, o);
29552         
29553     },
29554     
29555     remove : function(o)
29556     {
29557         var files = [];
29558         
29559         Roo.each(this.files, function(file){
29560             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29561                 files.push(file);
29562                 return;
29563             }
29564
29565             o.target.remove();
29566
29567         }, this);
29568         
29569         this.files = files;
29570         
29571         this.refresh();
29572     },
29573     
29574     clear : function()
29575     {
29576         Roo.each(this.files, function(file){
29577             if(!file.target){
29578                 return;
29579             }
29580             
29581             file.target.remove();
29582
29583         }, this);
29584         
29585         this.files = [];
29586         
29587         this.refresh();
29588     },
29589     
29590     onClick : function(e, el, o)
29591     {
29592         e.preventDefault();
29593         
29594         this.fireEvent('click', this, o);
29595         
29596     },
29597     
29598     closable : function(closable)
29599     {
29600         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29601             
29602             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29603             
29604             if(closable){
29605                 el.show();
29606                 return;
29607             }
29608             
29609             el.hide();
29610             
29611         }, this);
29612     },
29613     
29614     xhrOnLoad : function(xhr)
29615     {
29616         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29617             el.remove();
29618         }, this);
29619         
29620         if (xhr.readyState !== 4) {
29621             this.arrange();
29622             this.fireEvent('exception', this, xhr);
29623             return;
29624         }
29625
29626         var response = Roo.decode(xhr.responseText);
29627         
29628         if(!response.success){
29629             this.arrange();
29630             this.fireEvent('exception', this, xhr);
29631             return;
29632         }
29633         
29634         var file = this.renderPreview(response.data);
29635         
29636         this.files.push(file);
29637         
29638         this.arrange();
29639         
29640         this.fireEvent('afterupload', this, xhr);
29641         
29642     },
29643     
29644     xhrOnError : function(xhr)
29645     {
29646         Roo.log('xhr on error');
29647         
29648         var response = Roo.decode(xhr.responseText);
29649           
29650         Roo.log(response);
29651         
29652         this.arrange();
29653     },
29654     
29655     process : function(file)
29656     {
29657         if(this.fireEvent('process', this, file) !== false){
29658             if(this.editable && file.type.indexOf('image') != -1){
29659                 this.fireEvent('edit', this, file);
29660                 return;
29661             }
29662
29663             this.uploadStart(file, false);
29664
29665             return;
29666         }
29667         
29668     },
29669     
29670     uploadStart : function(file, crop)
29671     {
29672         this.xhr = new XMLHttpRequest();
29673         
29674         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29675             this.arrange();
29676             return;
29677         }
29678         
29679         file.xhr = this.xhr;
29680             
29681         this.managerEl.createChild({
29682             tag : 'div',
29683             cls : 'roo-document-manager-loading',
29684             cn : [
29685                 {
29686                     tag : 'div',
29687                     tooltip : file.name,
29688                     cls : 'roo-document-manager-thumb',
29689                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29690                 }
29691             ]
29692
29693         });
29694
29695         this.xhr.open(this.method, this.url, true);
29696         
29697         var headers = {
29698             "Accept": "application/json",
29699             "Cache-Control": "no-cache",
29700             "X-Requested-With": "XMLHttpRequest"
29701         };
29702         
29703         for (var headerName in headers) {
29704             var headerValue = headers[headerName];
29705             if (headerValue) {
29706                 this.xhr.setRequestHeader(headerName, headerValue);
29707             }
29708         }
29709         
29710         var _this = this;
29711         
29712         this.xhr.onload = function()
29713         {
29714             _this.xhrOnLoad(_this.xhr);
29715         }
29716         
29717         this.xhr.onerror = function()
29718         {
29719             _this.xhrOnError(_this.xhr);
29720         }
29721         
29722         var formData = new FormData();
29723
29724         formData.append('returnHTML', 'NO');
29725         
29726         if(crop){
29727             formData.append('crop', crop);
29728         }
29729         
29730         formData.append(this.paramName, file, file.name);
29731         
29732         var options = {
29733             file : file, 
29734             manually : false
29735         };
29736         
29737         if(this.fireEvent('prepare', this, formData, options) != false){
29738             
29739             if(options.manually){
29740                 return;
29741             }
29742             
29743             this.xhr.send(formData);
29744             return;
29745         };
29746         
29747         this.uploadCancel();
29748     },
29749     
29750     uploadCancel : function()
29751     {
29752         if (this.xhr) {
29753             this.xhr.abort();
29754         }
29755         
29756         this.delegates = [];
29757         
29758         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29759             el.remove();
29760         }, this);
29761         
29762         this.arrange();
29763     },
29764     
29765     renderPreview : function(file)
29766     {
29767         if(typeof(file.target) != 'undefined' && file.target){
29768             return file;
29769         }
29770         
29771         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29772         
29773         var previewEl = this.managerEl.createChild({
29774             tag : 'div',
29775             cls : 'roo-document-manager-preview',
29776             cn : [
29777                 {
29778                     tag : 'div',
29779                     tooltip : file[this.toolTipName],
29780                     cls : 'roo-document-manager-thumb',
29781                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29782                 },
29783                 {
29784                     tag : 'button',
29785                     cls : 'close',
29786                     html : '<i class="fa fa-times-circle"></i>'
29787                 }
29788             ]
29789         });
29790
29791         var close = previewEl.select('button.close', true).first();
29792
29793         close.on('click', this.onRemove, this, file);
29794
29795         file.target = previewEl;
29796
29797         var image = previewEl.select('img', true).first();
29798         
29799         var _this = this;
29800         
29801         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29802         
29803         image.on('click', this.onClick, this, file);
29804         
29805         this.fireEvent('previewrendered', this, file);
29806         
29807         return file;
29808         
29809     },
29810     
29811     onPreviewLoad : function(file, image)
29812     {
29813         if(typeof(file.target) == 'undefined' || !file.target){
29814             return;
29815         }
29816         
29817         var width = image.dom.naturalWidth || image.dom.width;
29818         var height = image.dom.naturalHeight || image.dom.height;
29819         
29820         if(!this.previewResize) {
29821             return;
29822         }
29823         
29824         if(width > height){
29825             file.target.addClass('wide');
29826             return;
29827         }
29828         
29829         file.target.addClass('tall');
29830         return;
29831         
29832     },
29833     
29834     uploadFromSource : function(file, crop)
29835     {
29836         this.xhr = new XMLHttpRequest();
29837         
29838         this.managerEl.createChild({
29839             tag : 'div',
29840             cls : 'roo-document-manager-loading',
29841             cn : [
29842                 {
29843                     tag : 'div',
29844                     tooltip : file.name,
29845                     cls : 'roo-document-manager-thumb',
29846                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29847                 }
29848             ]
29849
29850         });
29851
29852         this.xhr.open(this.method, this.url, true);
29853         
29854         var headers = {
29855             "Accept": "application/json",
29856             "Cache-Control": "no-cache",
29857             "X-Requested-With": "XMLHttpRequest"
29858         };
29859         
29860         for (var headerName in headers) {
29861             var headerValue = headers[headerName];
29862             if (headerValue) {
29863                 this.xhr.setRequestHeader(headerName, headerValue);
29864             }
29865         }
29866         
29867         var _this = this;
29868         
29869         this.xhr.onload = function()
29870         {
29871             _this.xhrOnLoad(_this.xhr);
29872         }
29873         
29874         this.xhr.onerror = function()
29875         {
29876             _this.xhrOnError(_this.xhr);
29877         }
29878         
29879         var formData = new FormData();
29880
29881         formData.append('returnHTML', 'NO');
29882         
29883         formData.append('crop', crop);
29884         
29885         if(typeof(file.filename) != 'undefined'){
29886             formData.append('filename', file.filename);
29887         }
29888         
29889         if(typeof(file.mimetype) != 'undefined'){
29890             formData.append('mimetype', file.mimetype);
29891         }
29892         
29893         Roo.log(formData);
29894         
29895         if(this.fireEvent('prepare', this, formData) != false){
29896             this.xhr.send(formData);
29897         };
29898     }
29899 });
29900
29901 /*
29902 * Licence: LGPL
29903 */
29904
29905 /**
29906  * @class Roo.bootstrap.DocumentViewer
29907  * @extends Roo.bootstrap.Component
29908  * Bootstrap DocumentViewer class
29909  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29910  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29911  * 
29912  * @constructor
29913  * Create a new DocumentViewer
29914  * @param {Object} config The config object
29915  */
29916
29917 Roo.bootstrap.DocumentViewer = function(config){
29918     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29919     
29920     this.addEvents({
29921         /**
29922          * @event initial
29923          * Fire after initEvent
29924          * @param {Roo.bootstrap.DocumentViewer} this
29925          */
29926         "initial" : true,
29927         /**
29928          * @event click
29929          * Fire after click
29930          * @param {Roo.bootstrap.DocumentViewer} this
29931          */
29932         "click" : true,
29933         /**
29934          * @event download
29935          * Fire after download button
29936          * @param {Roo.bootstrap.DocumentViewer} this
29937          */
29938         "download" : true,
29939         /**
29940          * @event trash
29941          * Fire after trash button
29942          * @param {Roo.bootstrap.DocumentViewer} this
29943          */
29944         "trash" : true
29945         
29946     });
29947 };
29948
29949 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29950     
29951     showDownload : true,
29952     
29953     showTrash : true,
29954     
29955     getAutoCreate : function()
29956     {
29957         var cfg = {
29958             tag : 'div',
29959             cls : 'roo-document-viewer',
29960             cn : [
29961                 {
29962                     tag : 'div',
29963                     cls : 'roo-document-viewer-body',
29964                     cn : [
29965                         {
29966                             tag : 'div',
29967                             cls : 'roo-document-viewer-thumb',
29968                             cn : [
29969                                 {
29970                                     tag : 'img',
29971                                     cls : 'roo-document-viewer-image'
29972                                 }
29973                             ]
29974                         }
29975                     ]
29976                 },
29977                 {
29978                     tag : 'div',
29979                     cls : 'roo-document-viewer-footer',
29980                     cn : {
29981                         tag : 'div',
29982                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29983                         cn : [
29984                             {
29985                                 tag : 'div',
29986                                 cls : 'btn-group roo-document-viewer-download',
29987                                 cn : [
29988                                     {
29989                                         tag : 'button',
29990                                         cls : 'btn btn-default',
29991                                         html : '<i class="fa fa-download"></i>'
29992                                     }
29993                                 ]
29994                             },
29995                             {
29996                                 tag : 'div',
29997                                 cls : 'btn-group roo-document-viewer-trash',
29998                                 cn : [
29999                                     {
30000                                         tag : 'button',
30001                                         cls : 'btn btn-default',
30002                                         html : '<i class="fa fa-trash"></i>'
30003                                     }
30004                                 ]
30005                             }
30006                         ]
30007                     }
30008                 }
30009             ]
30010         };
30011         
30012         return cfg;
30013     },
30014     
30015     initEvents : function()
30016     {
30017         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
30018         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
30019         
30020         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
30021         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
30022         
30023         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
30024         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
30025         
30026         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
30027         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
30028         
30029         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
30030         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
30031         
30032         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
30033         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
30034         
30035         this.bodyEl.on('click', this.onClick, this);
30036         this.downloadBtn.on('click', this.onDownload, this);
30037         this.trashBtn.on('click', this.onTrash, this);
30038         
30039         this.downloadBtn.hide();
30040         this.trashBtn.hide();
30041         
30042         if(this.showDownload){
30043             this.downloadBtn.show();
30044         }
30045         
30046         if(this.showTrash){
30047             this.trashBtn.show();
30048         }
30049         
30050         if(!this.showDownload && !this.showTrash) {
30051             this.footerEl.hide();
30052         }
30053         
30054     },
30055     
30056     initial : function()
30057     {
30058         this.fireEvent('initial', this);
30059         
30060     },
30061     
30062     onClick : function(e)
30063     {
30064         e.preventDefault();
30065         
30066         this.fireEvent('click', this);
30067     },
30068     
30069     onDownload : function(e)
30070     {
30071         e.preventDefault();
30072         
30073         this.fireEvent('download', this);
30074     },
30075     
30076     onTrash : function(e)
30077     {
30078         e.preventDefault();
30079         
30080         this.fireEvent('trash', this);
30081     }
30082     
30083 });
30084 /*
30085  * - LGPL
30086  *
30087  * nav progress bar
30088  * 
30089  */
30090
30091 /**
30092  * @class Roo.bootstrap.NavProgressBar
30093  * @extends Roo.bootstrap.Component
30094  * Bootstrap NavProgressBar class
30095  * 
30096  * @constructor
30097  * Create a new nav progress bar
30098  * @param {Object} config The config object
30099  */
30100
30101 Roo.bootstrap.NavProgressBar = function(config){
30102     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
30103
30104     this.bullets = this.bullets || [];
30105    
30106 //    Roo.bootstrap.NavProgressBar.register(this);
30107      this.addEvents({
30108         /**
30109              * @event changed
30110              * Fires when the active item changes
30111              * @param {Roo.bootstrap.NavProgressBar} this
30112              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
30113              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
30114          */
30115         'changed': true
30116      });
30117     
30118 };
30119
30120 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
30121     
30122     bullets : [],
30123     barItems : [],
30124     
30125     getAutoCreate : function()
30126     {
30127         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
30128         
30129         cfg = {
30130             tag : 'div',
30131             cls : 'roo-navigation-bar-group',
30132             cn : [
30133                 {
30134                     tag : 'div',
30135                     cls : 'roo-navigation-top-bar'
30136                 },
30137                 {
30138                     tag : 'div',
30139                     cls : 'roo-navigation-bullets-bar',
30140                     cn : [
30141                         {
30142                             tag : 'ul',
30143                             cls : 'roo-navigation-bar'
30144                         }
30145                     ]
30146                 },
30147                 
30148                 {
30149                     tag : 'div',
30150                     cls : 'roo-navigation-bottom-bar'
30151                 }
30152             ]
30153             
30154         };
30155         
30156         return cfg;
30157         
30158     },
30159     
30160     initEvents: function() 
30161     {
30162         
30163     },
30164     
30165     onRender : function(ct, position) 
30166     {
30167         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30168         
30169         if(this.bullets.length){
30170             Roo.each(this.bullets, function(b){
30171                this.addItem(b);
30172             }, this);
30173         }
30174         
30175         this.format();
30176         
30177     },
30178     
30179     addItem : function(cfg)
30180     {
30181         var item = new Roo.bootstrap.NavProgressItem(cfg);
30182         
30183         item.parentId = this.id;
30184         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
30185         
30186         if(cfg.html){
30187             var top = new Roo.bootstrap.Element({
30188                 tag : 'div',
30189                 cls : 'roo-navigation-bar-text'
30190             });
30191             
30192             var bottom = new Roo.bootstrap.Element({
30193                 tag : 'div',
30194                 cls : 'roo-navigation-bar-text'
30195             });
30196             
30197             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
30198             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
30199             
30200             var topText = new Roo.bootstrap.Element({
30201                 tag : 'span',
30202                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
30203             });
30204             
30205             var bottomText = new Roo.bootstrap.Element({
30206                 tag : 'span',
30207                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30208             });
30209             
30210             topText.onRender(top.el, null);
30211             bottomText.onRender(bottom.el, null);
30212             
30213             item.topEl = top;
30214             item.bottomEl = bottom;
30215         }
30216         
30217         this.barItems.push(item);
30218         
30219         return item;
30220     },
30221     
30222     getActive : function()
30223     {
30224         var active = false;
30225         
30226         Roo.each(this.barItems, function(v){
30227             
30228             if (!v.isActive()) {
30229                 return;
30230             }
30231             
30232             active = v;
30233             return false;
30234             
30235         });
30236         
30237         return active;
30238     },
30239     
30240     setActiveItem : function(item)
30241     {
30242         var prev = false;
30243         
30244         Roo.each(this.barItems, function(v){
30245             if (v.rid == item.rid) {
30246                 return ;
30247             }
30248             
30249             if (v.isActive()) {
30250                 v.setActive(false);
30251                 prev = v;
30252             }
30253         });
30254
30255         item.setActive(true);
30256         
30257         this.fireEvent('changed', this, item, prev);
30258     },
30259     
30260     getBarItem: function(rid)
30261     {
30262         var ret = false;
30263         
30264         Roo.each(this.barItems, function(e) {
30265             if (e.rid != rid) {
30266                 return;
30267             }
30268             
30269             ret =  e;
30270             return false;
30271         });
30272         
30273         return ret;
30274     },
30275     
30276     indexOfItem : function(item)
30277     {
30278         var index = false;
30279         
30280         Roo.each(this.barItems, function(v, i){
30281             
30282             if (v.rid != item.rid) {
30283                 return;
30284             }
30285             
30286             index = i;
30287             return false
30288         });
30289         
30290         return index;
30291     },
30292     
30293     setActiveNext : function()
30294     {
30295         var i = this.indexOfItem(this.getActive());
30296         
30297         if (i > this.barItems.length) {
30298             return;
30299         }
30300         
30301         this.setActiveItem(this.barItems[i+1]);
30302     },
30303     
30304     setActivePrev : function()
30305     {
30306         var i = this.indexOfItem(this.getActive());
30307         
30308         if (i  < 1) {
30309             return;
30310         }
30311         
30312         this.setActiveItem(this.barItems[i-1]);
30313     },
30314     
30315     format : function()
30316     {
30317         if(!this.barItems.length){
30318             return;
30319         }
30320      
30321         var width = 100 / this.barItems.length;
30322         
30323         Roo.each(this.barItems, function(i){
30324             i.el.setStyle('width', width + '%');
30325             i.topEl.el.setStyle('width', width + '%');
30326             i.bottomEl.el.setStyle('width', width + '%');
30327         }, this);
30328         
30329     }
30330     
30331 });
30332 /*
30333  * - LGPL
30334  *
30335  * Nav Progress Item
30336  * 
30337  */
30338
30339 /**
30340  * @class Roo.bootstrap.NavProgressItem
30341  * @extends Roo.bootstrap.Component
30342  * Bootstrap NavProgressItem class
30343  * @cfg {String} rid the reference id
30344  * @cfg {Boolean} active (true|false) Is item active default false
30345  * @cfg {Boolean} disabled (true|false) Is item active default false
30346  * @cfg {String} html
30347  * @cfg {String} position (top|bottom) text position default bottom
30348  * @cfg {String} icon show icon instead of number
30349  * 
30350  * @constructor
30351  * Create a new NavProgressItem
30352  * @param {Object} config The config object
30353  */
30354 Roo.bootstrap.NavProgressItem = function(config){
30355     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30356     this.addEvents({
30357         // raw events
30358         /**
30359          * @event click
30360          * The raw click event for the entire grid.
30361          * @param {Roo.bootstrap.NavProgressItem} this
30362          * @param {Roo.EventObject} e
30363          */
30364         "click" : true
30365     });
30366    
30367 };
30368
30369 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30370     
30371     rid : '',
30372     active : false,
30373     disabled : false,
30374     html : '',
30375     position : 'bottom',
30376     icon : false,
30377     
30378     getAutoCreate : function()
30379     {
30380         var iconCls = 'roo-navigation-bar-item-icon';
30381         
30382         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30383         
30384         var cfg = {
30385             tag: 'li',
30386             cls: 'roo-navigation-bar-item',
30387             cn : [
30388                 {
30389                     tag : 'i',
30390                     cls : iconCls
30391                 }
30392             ]
30393         };
30394         
30395         if(this.active){
30396             cfg.cls += ' active';
30397         }
30398         if(this.disabled){
30399             cfg.cls += ' disabled';
30400         }
30401         
30402         return cfg;
30403     },
30404     
30405     disable : function()
30406     {
30407         this.setDisabled(true);
30408     },
30409     
30410     enable : function()
30411     {
30412         this.setDisabled(false);
30413     },
30414     
30415     initEvents: function() 
30416     {
30417         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30418         
30419         this.iconEl.on('click', this.onClick, this);
30420     },
30421     
30422     onClick : function(e)
30423     {
30424         e.preventDefault();
30425         
30426         if(this.disabled){
30427             return;
30428         }
30429         
30430         if(this.fireEvent('click', this, e) === false){
30431             return;
30432         };
30433         
30434         this.parent().setActiveItem(this);
30435     },
30436     
30437     isActive: function () 
30438     {
30439         return this.active;
30440     },
30441     
30442     setActive : function(state)
30443     {
30444         if(this.active == state){
30445             return;
30446         }
30447         
30448         this.active = state;
30449         
30450         if (state) {
30451             this.el.addClass('active');
30452             return;
30453         }
30454         
30455         this.el.removeClass('active');
30456         
30457         return;
30458     },
30459     
30460     setDisabled : function(state)
30461     {
30462         if(this.disabled == state){
30463             return;
30464         }
30465         
30466         this.disabled = state;
30467         
30468         if (state) {
30469             this.el.addClass('disabled');
30470             return;
30471         }
30472         
30473         this.el.removeClass('disabled');
30474     },
30475     
30476     tooltipEl : function()
30477     {
30478         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30479     }
30480 });
30481  
30482
30483  /*
30484  * - LGPL
30485  *
30486  * FieldLabel
30487  * 
30488  */
30489
30490 /**
30491  * @class Roo.bootstrap.FieldLabel
30492  * @extends Roo.bootstrap.Component
30493  * Bootstrap FieldLabel class
30494  * @cfg {String} html contents of the element
30495  * @cfg {String} tag tag of the element default label
30496  * @cfg {String} cls class of the element
30497  * @cfg {String} target label target 
30498  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30499  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
30500  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
30501  * @cfg {String} iconTooltip default "This field is required"
30502  * @cfg {String} indicatorpos (left|right) default left
30503  * 
30504  * @constructor
30505  * Create a new FieldLabel
30506  * @param {Object} config The config object
30507  */
30508
30509 Roo.bootstrap.FieldLabel = function(config){
30510     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30511     
30512     this.addEvents({
30513             /**
30514              * @event invalid
30515              * Fires after the field has been marked as invalid.
30516              * @param {Roo.form.FieldLabel} this
30517              * @param {String} msg The validation message
30518              */
30519             invalid : true,
30520             /**
30521              * @event valid
30522              * Fires after the field has been validated with no errors.
30523              * @param {Roo.form.FieldLabel} this
30524              */
30525             valid : true
30526         });
30527 };
30528
30529 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30530     
30531     tag: 'label',
30532     cls: '',
30533     html: '',
30534     target: '',
30535     allowBlank : true,
30536     invalidClass : 'has-warning',
30537     validClass : 'has-success',
30538     iconTooltip : 'This field is required',
30539     indicatorpos : 'left',
30540     
30541     getAutoCreate : function(){
30542         
30543         var cls = "";
30544         if (!this.allowBlank) {
30545             cls  = "visible";
30546         }
30547         
30548         var cfg = {
30549             tag : this.tag,
30550             cls : 'roo-bootstrap-field-label ' + this.cls,
30551             for : this.target,
30552             cn : [
30553                 {
30554                     tag : 'i',
30555                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30556                     tooltip : this.iconTooltip
30557                 },
30558                 {
30559                     tag : 'span',
30560                     html : this.html
30561                 }
30562             ] 
30563         };
30564         
30565         if(this.indicatorpos == 'right'){
30566             var cfg = {
30567                 tag : this.tag,
30568                 cls : 'roo-bootstrap-field-label ' + this.cls,
30569                 for : this.target,
30570                 cn : [
30571                     {
30572                         tag : 'span',
30573                         html : this.html
30574                     },
30575                     {
30576                         tag : 'i',
30577                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30578                         tooltip : this.iconTooltip
30579                     }
30580                 ] 
30581             };
30582         }
30583         
30584         return cfg;
30585     },
30586     
30587     initEvents: function() 
30588     {
30589         Roo.bootstrap.Element.superclass.initEvents.call(this);
30590         
30591         this.indicator = this.indicatorEl();
30592         
30593         if(this.indicator){
30594             this.indicator.removeClass('visible');
30595             this.indicator.addClass('invisible');
30596         }
30597         
30598         Roo.bootstrap.FieldLabel.register(this);
30599     },
30600     
30601     indicatorEl : function()
30602     {
30603         var indicator = this.el.select('i.roo-required-indicator',true).first();
30604         
30605         if(!indicator){
30606             return false;
30607         }
30608         
30609         return indicator;
30610         
30611     },
30612     
30613     /**
30614      * Mark this field as valid
30615      */
30616     markValid : function()
30617     {
30618         if(this.indicator){
30619             this.indicator.removeClass('visible');
30620             this.indicator.addClass('invisible');
30621         }
30622         if (Roo.bootstrap.version == 3) {
30623             this.el.removeClass(this.invalidClass);
30624             this.el.addClass(this.validClass);
30625         } else {
30626             this.el.removeClass('is-invalid');
30627             this.el.addClass('is-valid');
30628         }
30629         
30630         
30631         this.fireEvent('valid', this);
30632     },
30633     
30634     /**
30635      * Mark this field as invalid
30636      * @param {String} msg The validation message
30637      */
30638     markInvalid : function(msg)
30639     {
30640         if(this.indicator){
30641             this.indicator.removeClass('invisible');
30642             this.indicator.addClass('visible');
30643         }
30644           if (Roo.bootstrap.version == 3) {
30645             this.el.removeClass(this.validClass);
30646             this.el.addClass(this.invalidClass);
30647         } else {
30648             this.el.removeClass('is-valid');
30649             this.el.addClass('is-invalid');
30650         }
30651         
30652         
30653         this.fireEvent('invalid', this, msg);
30654     }
30655     
30656    
30657 });
30658
30659 Roo.apply(Roo.bootstrap.FieldLabel, {
30660     
30661     groups: {},
30662     
30663      /**
30664     * register a FieldLabel Group
30665     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30666     */
30667     register : function(label)
30668     {
30669         if(this.groups.hasOwnProperty(label.target)){
30670             return;
30671         }
30672      
30673         this.groups[label.target] = label;
30674         
30675     },
30676     /**
30677     * fetch a FieldLabel Group based on the target
30678     * @param {string} target
30679     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30680     */
30681     get: function(target) {
30682         if (typeof(this.groups[target]) == 'undefined') {
30683             return false;
30684         }
30685         
30686         return this.groups[target] ;
30687     }
30688 });
30689
30690  
30691
30692  /*
30693  * - LGPL
30694  *
30695  * page DateSplitField.
30696  * 
30697  */
30698
30699
30700 /**
30701  * @class Roo.bootstrap.DateSplitField
30702  * @extends Roo.bootstrap.Component
30703  * Bootstrap DateSplitField class
30704  * @cfg {string} fieldLabel - the label associated
30705  * @cfg {Number} labelWidth set the width of label (0-12)
30706  * @cfg {String} labelAlign (top|left)
30707  * @cfg {Boolean} dayAllowBlank (true|false) default false
30708  * @cfg {Boolean} monthAllowBlank (true|false) default false
30709  * @cfg {Boolean} yearAllowBlank (true|false) default false
30710  * @cfg {string} dayPlaceholder 
30711  * @cfg {string} monthPlaceholder
30712  * @cfg {string} yearPlaceholder
30713  * @cfg {string} dayFormat default 'd'
30714  * @cfg {string} monthFormat default 'm'
30715  * @cfg {string} yearFormat default 'Y'
30716  * @cfg {Number} labellg set the width of label (1-12)
30717  * @cfg {Number} labelmd set the width of label (1-12)
30718  * @cfg {Number} labelsm set the width of label (1-12)
30719  * @cfg {Number} labelxs set the width of label (1-12)
30720
30721  *     
30722  * @constructor
30723  * Create a new DateSplitField
30724  * @param {Object} config The config object
30725  */
30726
30727 Roo.bootstrap.DateSplitField = function(config){
30728     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30729     
30730     this.addEvents({
30731         // raw events
30732          /**
30733          * @event years
30734          * getting the data of years
30735          * @param {Roo.bootstrap.DateSplitField} this
30736          * @param {Object} years
30737          */
30738         "years" : true,
30739         /**
30740          * @event days
30741          * getting the data of days
30742          * @param {Roo.bootstrap.DateSplitField} this
30743          * @param {Object} days
30744          */
30745         "days" : true,
30746         /**
30747          * @event invalid
30748          * Fires after the field has been marked as invalid.
30749          * @param {Roo.form.Field} this
30750          * @param {String} msg The validation message
30751          */
30752         invalid : true,
30753        /**
30754          * @event valid
30755          * Fires after the field has been validated with no errors.
30756          * @param {Roo.form.Field} this
30757          */
30758         valid : true
30759     });
30760 };
30761
30762 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30763     
30764     fieldLabel : '',
30765     labelAlign : 'top',
30766     labelWidth : 3,
30767     dayAllowBlank : false,
30768     monthAllowBlank : false,
30769     yearAllowBlank : false,
30770     dayPlaceholder : '',
30771     monthPlaceholder : '',
30772     yearPlaceholder : '',
30773     dayFormat : 'd',
30774     monthFormat : 'm',
30775     yearFormat : 'Y',
30776     isFormField : true,
30777     labellg : 0,
30778     labelmd : 0,
30779     labelsm : 0,
30780     labelxs : 0,
30781     
30782     getAutoCreate : function()
30783     {
30784         var cfg = {
30785             tag : 'div',
30786             cls : 'row roo-date-split-field-group',
30787             cn : [
30788                 {
30789                     tag : 'input',
30790                     type : 'hidden',
30791                     cls : 'form-hidden-field roo-date-split-field-group-value',
30792                     name : this.name
30793                 }
30794             ]
30795         };
30796         
30797         var labelCls = 'col-md-12';
30798         var contentCls = 'col-md-4';
30799         
30800         if(this.fieldLabel){
30801             
30802             var label = {
30803                 tag : 'div',
30804                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30805                 cn : [
30806                     {
30807                         tag : 'label',
30808                         html : this.fieldLabel
30809                     }
30810                 ]
30811             };
30812             
30813             if(this.labelAlign == 'left'){
30814             
30815                 if(this.labelWidth > 12){
30816                     label.style = "width: " + this.labelWidth + 'px';
30817                 }
30818
30819                 if(this.labelWidth < 13 && this.labelmd == 0){
30820                     this.labelmd = this.labelWidth;
30821                 }
30822
30823                 if(this.labellg > 0){
30824                     labelCls = ' col-lg-' + this.labellg;
30825                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30826                 }
30827
30828                 if(this.labelmd > 0){
30829                     labelCls = ' col-md-' + this.labelmd;
30830                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30831                 }
30832
30833                 if(this.labelsm > 0){
30834                     labelCls = ' col-sm-' + this.labelsm;
30835                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30836                 }
30837
30838                 if(this.labelxs > 0){
30839                     labelCls = ' col-xs-' + this.labelxs;
30840                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30841                 }
30842             }
30843             
30844             label.cls += ' ' + labelCls;
30845             
30846             cfg.cn.push(label);
30847         }
30848         
30849         Roo.each(['day', 'month', 'year'], function(t){
30850             cfg.cn.push({
30851                 tag : 'div',
30852                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30853             });
30854         }, this);
30855         
30856         return cfg;
30857     },
30858     
30859     inputEl: function ()
30860     {
30861         return this.el.select('.roo-date-split-field-group-value', true).first();
30862     },
30863     
30864     onRender : function(ct, position) 
30865     {
30866         var _this = this;
30867         
30868         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30869         
30870         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30871         
30872         this.dayField = new Roo.bootstrap.ComboBox({
30873             allowBlank : this.dayAllowBlank,
30874             alwaysQuery : true,
30875             displayField : 'value',
30876             editable : false,
30877             fieldLabel : '',
30878             forceSelection : true,
30879             mode : 'local',
30880             placeholder : this.dayPlaceholder,
30881             selectOnFocus : true,
30882             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30883             triggerAction : 'all',
30884             typeAhead : true,
30885             valueField : 'value',
30886             store : new Roo.data.SimpleStore({
30887                 data : (function() {    
30888                     var days = [];
30889                     _this.fireEvent('days', _this, days);
30890                     return days;
30891                 })(),
30892                 fields : [ 'value' ]
30893             }),
30894             listeners : {
30895                 select : function (_self, record, index)
30896                 {
30897                     _this.setValue(_this.getValue());
30898                 }
30899             }
30900         });
30901
30902         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30903         
30904         this.monthField = new Roo.bootstrap.MonthField({
30905             after : '<i class=\"fa fa-calendar\"></i>',
30906             allowBlank : this.monthAllowBlank,
30907             placeholder : this.monthPlaceholder,
30908             readOnly : true,
30909             listeners : {
30910                 render : function (_self)
30911                 {
30912                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30913                         e.preventDefault();
30914                         _self.focus();
30915                     });
30916                 },
30917                 select : function (_self, oldvalue, newvalue)
30918                 {
30919                     _this.setValue(_this.getValue());
30920                 }
30921             }
30922         });
30923         
30924         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30925         
30926         this.yearField = new Roo.bootstrap.ComboBox({
30927             allowBlank : this.yearAllowBlank,
30928             alwaysQuery : true,
30929             displayField : 'value',
30930             editable : false,
30931             fieldLabel : '',
30932             forceSelection : true,
30933             mode : 'local',
30934             placeholder : this.yearPlaceholder,
30935             selectOnFocus : true,
30936             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30937             triggerAction : 'all',
30938             typeAhead : true,
30939             valueField : 'value',
30940             store : new Roo.data.SimpleStore({
30941                 data : (function() {
30942                     var years = [];
30943                     _this.fireEvent('years', _this, years);
30944                     return years;
30945                 })(),
30946                 fields : [ 'value' ]
30947             }),
30948             listeners : {
30949                 select : function (_self, record, index)
30950                 {
30951                     _this.setValue(_this.getValue());
30952                 }
30953             }
30954         });
30955
30956         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30957     },
30958     
30959     setValue : function(v, format)
30960     {
30961         this.inputEl.dom.value = v;
30962         
30963         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30964         
30965         var d = Date.parseDate(v, f);
30966         
30967         if(!d){
30968             this.validate();
30969             return;
30970         }
30971         
30972         this.setDay(d.format(this.dayFormat));
30973         this.setMonth(d.format(this.monthFormat));
30974         this.setYear(d.format(this.yearFormat));
30975         
30976         this.validate();
30977         
30978         return;
30979     },
30980     
30981     setDay : function(v)
30982     {
30983         this.dayField.setValue(v);
30984         this.inputEl.dom.value = this.getValue();
30985         this.validate();
30986         return;
30987     },
30988     
30989     setMonth : function(v)
30990     {
30991         this.monthField.setValue(v, true);
30992         this.inputEl.dom.value = this.getValue();
30993         this.validate();
30994         return;
30995     },
30996     
30997     setYear : function(v)
30998     {
30999         this.yearField.setValue(v);
31000         this.inputEl.dom.value = this.getValue();
31001         this.validate();
31002         return;
31003     },
31004     
31005     getDay : function()
31006     {
31007         return this.dayField.getValue();
31008     },
31009     
31010     getMonth : function()
31011     {
31012         return this.monthField.getValue();
31013     },
31014     
31015     getYear : function()
31016     {
31017         return this.yearField.getValue();
31018     },
31019     
31020     getValue : function()
31021     {
31022         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
31023         
31024         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
31025         
31026         return date;
31027     },
31028     
31029     reset : function()
31030     {
31031         this.setDay('');
31032         this.setMonth('');
31033         this.setYear('');
31034         this.inputEl.dom.value = '';
31035         this.validate();
31036         return;
31037     },
31038     
31039     validate : function()
31040     {
31041         var d = this.dayField.validate();
31042         var m = this.monthField.validate();
31043         var y = this.yearField.validate();
31044         
31045         var valid = true;
31046         
31047         if(
31048                 (!this.dayAllowBlank && !d) ||
31049                 (!this.monthAllowBlank && !m) ||
31050                 (!this.yearAllowBlank && !y)
31051         ){
31052             valid = false;
31053         }
31054         
31055         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
31056             return valid;
31057         }
31058         
31059         if(valid){
31060             this.markValid();
31061             return valid;
31062         }
31063         
31064         this.markInvalid();
31065         
31066         return valid;
31067     },
31068     
31069     markValid : function()
31070     {
31071         
31072         var label = this.el.select('label', true).first();
31073         var icon = this.el.select('i.fa-star', true).first();
31074
31075         if(label && icon){
31076             icon.remove();
31077         }
31078         
31079         this.fireEvent('valid', this);
31080     },
31081     
31082      /**
31083      * Mark this field as invalid
31084      * @param {String} msg The validation message
31085      */
31086     markInvalid : function(msg)
31087     {
31088         
31089         var label = this.el.select('label', true).first();
31090         var icon = this.el.select('i.fa-star', true).first();
31091
31092         if(label && !icon){
31093             this.el.select('.roo-date-split-field-label', true).createChild({
31094                 tag : 'i',
31095                 cls : 'text-danger fa fa-lg fa-star',
31096                 tooltip : 'This field is required',
31097                 style : 'margin-right:5px;'
31098             }, label, true);
31099         }
31100         
31101         this.fireEvent('invalid', this, msg);
31102     },
31103     
31104     clearInvalid : function()
31105     {
31106         var label = this.el.select('label', true).first();
31107         var icon = this.el.select('i.fa-star', true).first();
31108
31109         if(label && icon){
31110             icon.remove();
31111         }
31112         
31113         this.fireEvent('valid', this);
31114     },
31115     
31116     getName: function()
31117     {
31118         return this.name;
31119     }
31120     
31121 });
31122
31123  /**
31124  *
31125  * This is based on 
31126  * http://masonry.desandro.com
31127  *
31128  * The idea is to render all the bricks based on vertical width...
31129  *
31130  * The original code extends 'outlayer' - we might need to use that....
31131  * 
31132  */
31133
31134
31135 /**
31136  * @class Roo.bootstrap.LayoutMasonry
31137  * @extends Roo.bootstrap.Component
31138  * Bootstrap Layout Masonry class
31139  * 
31140  * @constructor
31141  * Create a new Element
31142  * @param {Object} config The config object
31143  */
31144
31145 Roo.bootstrap.LayoutMasonry = function(config){
31146     
31147     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
31148     
31149     this.bricks = [];
31150     
31151     Roo.bootstrap.LayoutMasonry.register(this);
31152     
31153     this.addEvents({
31154         // raw events
31155         /**
31156          * @event layout
31157          * Fire after layout the items
31158          * @param {Roo.bootstrap.LayoutMasonry} this
31159          * @param {Roo.EventObject} e
31160          */
31161         "layout" : true
31162     });
31163     
31164 };
31165
31166 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
31167     
31168     /**
31169      * @cfg {Boolean} isLayoutInstant = no animation?
31170      */   
31171     isLayoutInstant : false, // needed?
31172    
31173     /**
31174      * @cfg {Number} boxWidth  width of the columns
31175      */   
31176     boxWidth : 450,
31177     
31178       /**
31179      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
31180      */   
31181     boxHeight : 0,
31182     
31183     /**
31184      * @cfg {Number} padWidth padding below box..
31185      */   
31186     padWidth : 10, 
31187     
31188     /**
31189      * @cfg {Number} gutter gutter width..
31190      */   
31191     gutter : 10,
31192     
31193      /**
31194      * @cfg {Number} maxCols maximum number of columns
31195      */   
31196     
31197     maxCols: 0,
31198     
31199     /**
31200      * @cfg {Boolean} isAutoInitial defalut true
31201      */   
31202     isAutoInitial : true, 
31203     
31204     containerWidth: 0,
31205     
31206     /**
31207      * @cfg {Boolean} isHorizontal defalut false
31208      */   
31209     isHorizontal : false, 
31210
31211     currentSize : null,
31212     
31213     tag: 'div',
31214     
31215     cls: '',
31216     
31217     bricks: null, //CompositeElement
31218     
31219     cols : 1,
31220     
31221     _isLayoutInited : false,
31222     
31223 //    isAlternative : false, // only use for vertical layout...
31224     
31225     /**
31226      * @cfg {Number} alternativePadWidth padding below box..
31227      */   
31228     alternativePadWidth : 50,
31229     
31230     selectedBrick : [],
31231     
31232     getAutoCreate : function(){
31233         
31234         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31235         
31236         var cfg = {
31237             tag: this.tag,
31238             cls: 'blog-masonary-wrapper ' + this.cls,
31239             cn : {
31240                 cls : 'mas-boxes masonary'
31241             }
31242         };
31243         
31244         return cfg;
31245     },
31246     
31247     getChildContainer: function( )
31248     {
31249         if (this.boxesEl) {
31250             return this.boxesEl;
31251         }
31252         
31253         this.boxesEl = this.el.select('.mas-boxes').first();
31254         
31255         return this.boxesEl;
31256     },
31257     
31258     
31259     initEvents : function()
31260     {
31261         var _this = this;
31262         
31263         if(this.isAutoInitial){
31264             Roo.log('hook children rendered');
31265             this.on('childrenrendered', function() {
31266                 Roo.log('children rendered');
31267                 _this.initial();
31268             } ,this);
31269         }
31270     },
31271     
31272     initial : function()
31273     {
31274         this.selectedBrick = [];
31275         
31276         this.currentSize = this.el.getBox(true);
31277         
31278         Roo.EventManager.onWindowResize(this.resize, this); 
31279
31280         if(!this.isAutoInitial){
31281             this.layout();
31282             return;
31283         }
31284         
31285         this.layout();
31286         
31287         return;
31288         //this.layout.defer(500,this);
31289         
31290     },
31291     
31292     resize : function()
31293     {
31294         var cs = this.el.getBox(true);
31295         
31296         if (
31297                 this.currentSize.width == cs.width && 
31298                 this.currentSize.x == cs.x && 
31299                 this.currentSize.height == cs.height && 
31300                 this.currentSize.y == cs.y 
31301         ) {
31302             Roo.log("no change in with or X or Y");
31303             return;
31304         }
31305         
31306         this.currentSize = cs;
31307         
31308         this.layout();
31309         
31310     },
31311     
31312     layout : function()
31313     {   
31314         this._resetLayout();
31315         
31316         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31317         
31318         this.layoutItems( isInstant );
31319       
31320         this._isLayoutInited = true;
31321         
31322         this.fireEvent('layout', this);
31323         
31324     },
31325     
31326     _resetLayout : function()
31327     {
31328         if(this.isHorizontal){
31329             this.horizontalMeasureColumns();
31330             return;
31331         }
31332         
31333         this.verticalMeasureColumns();
31334         
31335     },
31336     
31337     verticalMeasureColumns : function()
31338     {
31339         this.getContainerWidth();
31340         
31341 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31342 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31343 //            return;
31344 //        }
31345         
31346         var boxWidth = this.boxWidth + this.padWidth;
31347         
31348         if(this.containerWidth < this.boxWidth){
31349             boxWidth = this.containerWidth
31350         }
31351         
31352         var containerWidth = this.containerWidth;
31353         
31354         var cols = Math.floor(containerWidth / boxWidth);
31355         
31356         this.cols = Math.max( cols, 1 );
31357         
31358         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31359         
31360         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31361         
31362         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31363         
31364         this.colWidth = boxWidth + avail - this.padWidth;
31365         
31366         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31367         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31368     },
31369     
31370     horizontalMeasureColumns : function()
31371     {
31372         this.getContainerWidth();
31373         
31374         var boxWidth = this.boxWidth;
31375         
31376         if(this.containerWidth < boxWidth){
31377             boxWidth = this.containerWidth;
31378         }
31379         
31380         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31381         
31382         this.el.setHeight(boxWidth);
31383         
31384     },
31385     
31386     getContainerWidth : function()
31387     {
31388         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31389     },
31390     
31391     layoutItems : function( isInstant )
31392     {
31393         Roo.log(this.bricks);
31394         
31395         var items = Roo.apply([], this.bricks);
31396         
31397         if(this.isHorizontal){
31398             this._horizontalLayoutItems( items , isInstant );
31399             return;
31400         }
31401         
31402 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31403 //            this._verticalAlternativeLayoutItems( items , isInstant );
31404 //            return;
31405 //        }
31406         
31407         this._verticalLayoutItems( items , isInstant );
31408         
31409     },
31410     
31411     _verticalLayoutItems : function ( items , isInstant)
31412     {
31413         if ( !items || !items.length ) {
31414             return;
31415         }
31416         
31417         var standard = [
31418             ['xs', 'xs', 'xs', 'tall'],
31419             ['xs', 'xs', 'tall'],
31420             ['xs', 'xs', 'sm'],
31421             ['xs', 'xs', 'xs'],
31422             ['xs', 'tall'],
31423             ['xs', 'sm'],
31424             ['xs', 'xs'],
31425             ['xs'],
31426             
31427             ['sm', 'xs', 'xs'],
31428             ['sm', 'xs'],
31429             ['sm'],
31430             
31431             ['tall', 'xs', 'xs', 'xs'],
31432             ['tall', 'xs', 'xs'],
31433             ['tall', 'xs'],
31434             ['tall']
31435             
31436         ];
31437         
31438         var queue = [];
31439         
31440         var boxes = [];
31441         
31442         var box = [];
31443         
31444         Roo.each(items, function(item, k){
31445             
31446             switch (item.size) {
31447                 // these layouts take up a full box,
31448                 case 'md' :
31449                 case 'md-left' :
31450                 case 'md-right' :
31451                 case 'wide' :
31452                     
31453                     if(box.length){
31454                         boxes.push(box);
31455                         box = [];
31456                     }
31457                     
31458                     boxes.push([item]);
31459                     
31460                     break;
31461                     
31462                 case 'xs' :
31463                 case 'sm' :
31464                 case 'tall' :
31465                     
31466                     box.push(item);
31467                     
31468                     break;
31469                 default :
31470                     break;
31471                     
31472             }
31473             
31474         }, this);
31475         
31476         if(box.length){
31477             boxes.push(box);
31478             box = [];
31479         }
31480         
31481         var filterPattern = function(box, length)
31482         {
31483             if(!box.length){
31484                 return;
31485             }
31486             
31487             var match = false;
31488             
31489             var pattern = box.slice(0, length);
31490             
31491             var format = [];
31492             
31493             Roo.each(pattern, function(i){
31494                 format.push(i.size);
31495             }, this);
31496             
31497             Roo.each(standard, function(s){
31498                 
31499                 if(String(s) != String(format)){
31500                     return;
31501                 }
31502                 
31503                 match = true;
31504                 return false;
31505                 
31506             }, this);
31507             
31508             if(!match && length == 1){
31509                 return;
31510             }
31511             
31512             if(!match){
31513                 filterPattern(box, length - 1);
31514                 return;
31515             }
31516                 
31517             queue.push(pattern);
31518
31519             box = box.slice(length, box.length);
31520
31521             filterPattern(box, 4);
31522
31523             return;
31524             
31525         }
31526         
31527         Roo.each(boxes, function(box, k){
31528             
31529             if(!box.length){
31530                 return;
31531             }
31532             
31533             if(box.length == 1){
31534                 queue.push(box);
31535                 return;
31536             }
31537             
31538             filterPattern(box, 4);
31539             
31540         }, this);
31541         
31542         this._processVerticalLayoutQueue( queue, isInstant );
31543         
31544     },
31545     
31546 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31547 //    {
31548 //        if ( !items || !items.length ) {
31549 //            return;
31550 //        }
31551 //
31552 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31553 //        
31554 //    },
31555     
31556     _horizontalLayoutItems : function ( items , isInstant)
31557     {
31558         if ( !items || !items.length || items.length < 3) {
31559             return;
31560         }
31561         
31562         items.reverse();
31563         
31564         var eItems = items.slice(0, 3);
31565         
31566         items = items.slice(3, items.length);
31567         
31568         var standard = [
31569             ['xs', 'xs', 'xs', 'wide'],
31570             ['xs', 'xs', 'wide'],
31571             ['xs', 'xs', 'sm'],
31572             ['xs', 'xs', 'xs'],
31573             ['xs', 'wide'],
31574             ['xs', 'sm'],
31575             ['xs', 'xs'],
31576             ['xs'],
31577             
31578             ['sm', 'xs', 'xs'],
31579             ['sm', 'xs'],
31580             ['sm'],
31581             
31582             ['wide', 'xs', 'xs', 'xs'],
31583             ['wide', 'xs', 'xs'],
31584             ['wide', 'xs'],
31585             ['wide'],
31586             
31587             ['wide-thin']
31588         ];
31589         
31590         var queue = [];
31591         
31592         var boxes = [];
31593         
31594         var box = [];
31595         
31596         Roo.each(items, function(item, k){
31597             
31598             switch (item.size) {
31599                 case 'md' :
31600                 case 'md-left' :
31601                 case 'md-right' :
31602                 case 'tall' :
31603                     
31604                     if(box.length){
31605                         boxes.push(box);
31606                         box = [];
31607                     }
31608                     
31609                     boxes.push([item]);
31610                     
31611                     break;
31612                     
31613                 case 'xs' :
31614                 case 'sm' :
31615                 case 'wide' :
31616                 case 'wide-thin' :
31617                     
31618                     box.push(item);
31619                     
31620                     break;
31621                 default :
31622                     break;
31623                     
31624             }
31625             
31626         }, this);
31627         
31628         if(box.length){
31629             boxes.push(box);
31630             box = [];
31631         }
31632         
31633         var filterPattern = function(box, length)
31634         {
31635             if(!box.length){
31636                 return;
31637             }
31638             
31639             var match = false;
31640             
31641             var pattern = box.slice(0, length);
31642             
31643             var format = [];
31644             
31645             Roo.each(pattern, function(i){
31646                 format.push(i.size);
31647             }, this);
31648             
31649             Roo.each(standard, function(s){
31650                 
31651                 if(String(s) != String(format)){
31652                     return;
31653                 }
31654                 
31655                 match = true;
31656                 return false;
31657                 
31658             }, this);
31659             
31660             if(!match && length == 1){
31661                 return;
31662             }
31663             
31664             if(!match){
31665                 filterPattern(box, length - 1);
31666                 return;
31667             }
31668                 
31669             queue.push(pattern);
31670
31671             box = box.slice(length, box.length);
31672
31673             filterPattern(box, 4);
31674
31675             return;
31676             
31677         }
31678         
31679         Roo.each(boxes, function(box, k){
31680             
31681             if(!box.length){
31682                 return;
31683             }
31684             
31685             if(box.length == 1){
31686                 queue.push(box);
31687                 return;
31688             }
31689             
31690             filterPattern(box, 4);
31691             
31692         }, this);
31693         
31694         
31695         var prune = [];
31696         
31697         var pos = this.el.getBox(true);
31698         
31699         var minX = pos.x;
31700         
31701         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31702         
31703         var hit_end = false;
31704         
31705         Roo.each(queue, function(box){
31706             
31707             if(hit_end){
31708                 
31709                 Roo.each(box, function(b){
31710                 
31711                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31712                     b.el.hide();
31713
31714                 }, this);
31715
31716                 return;
31717             }
31718             
31719             var mx = 0;
31720             
31721             Roo.each(box, function(b){
31722                 
31723                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31724                 b.el.show();
31725
31726                 mx = Math.max(mx, b.x);
31727                 
31728             }, this);
31729             
31730             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31731             
31732             if(maxX < minX){
31733                 
31734                 Roo.each(box, function(b){
31735                 
31736                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31737                     b.el.hide();
31738                     
31739                 }, this);
31740                 
31741                 hit_end = true;
31742                 
31743                 return;
31744             }
31745             
31746             prune.push(box);
31747             
31748         }, this);
31749         
31750         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31751     },
31752     
31753     /** Sets position of item in DOM
31754     * @param {Element} item
31755     * @param {Number} x - horizontal position
31756     * @param {Number} y - vertical position
31757     * @param {Boolean} isInstant - disables transitions
31758     */
31759     _processVerticalLayoutQueue : function( queue, isInstant )
31760     {
31761         var pos = this.el.getBox(true);
31762         var x = pos.x;
31763         var y = pos.y;
31764         var maxY = [];
31765         
31766         for (var i = 0; i < this.cols; i++){
31767             maxY[i] = pos.y;
31768         }
31769         
31770         Roo.each(queue, function(box, k){
31771             
31772             var col = k % this.cols;
31773             
31774             Roo.each(box, function(b,kk){
31775                 
31776                 b.el.position('absolute');
31777                 
31778                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31779                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31780                 
31781                 if(b.size == 'md-left' || b.size == 'md-right'){
31782                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31783                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31784                 }
31785                 
31786                 b.el.setWidth(width);
31787                 b.el.setHeight(height);
31788                 // iframe?
31789                 b.el.select('iframe',true).setSize(width,height);
31790                 
31791             }, this);
31792             
31793             for (var i = 0; i < this.cols; i++){
31794                 
31795                 if(maxY[i] < maxY[col]){
31796                     col = i;
31797                     continue;
31798                 }
31799                 
31800                 col = Math.min(col, i);
31801                 
31802             }
31803             
31804             x = pos.x + col * (this.colWidth + this.padWidth);
31805             
31806             y = maxY[col];
31807             
31808             var positions = [];
31809             
31810             switch (box.length){
31811                 case 1 :
31812                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31813                     break;
31814                 case 2 :
31815                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31816                     break;
31817                 case 3 :
31818                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31819                     break;
31820                 case 4 :
31821                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31822                     break;
31823                 default :
31824                     break;
31825             }
31826             
31827             Roo.each(box, function(b,kk){
31828                 
31829                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31830                 
31831                 var sz = b.el.getSize();
31832                 
31833                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31834                 
31835             }, this);
31836             
31837         }, this);
31838         
31839         var mY = 0;
31840         
31841         for (var i = 0; i < this.cols; i++){
31842             mY = Math.max(mY, maxY[i]);
31843         }
31844         
31845         this.el.setHeight(mY - pos.y);
31846         
31847     },
31848     
31849 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31850 //    {
31851 //        var pos = this.el.getBox(true);
31852 //        var x = pos.x;
31853 //        var y = pos.y;
31854 //        var maxX = pos.right;
31855 //        
31856 //        var maxHeight = 0;
31857 //        
31858 //        Roo.each(items, function(item, k){
31859 //            
31860 //            var c = k % 2;
31861 //            
31862 //            item.el.position('absolute');
31863 //                
31864 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31865 //
31866 //            item.el.setWidth(width);
31867 //
31868 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31869 //
31870 //            item.el.setHeight(height);
31871 //            
31872 //            if(c == 0){
31873 //                item.el.setXY([x, y], isInstant ? false : true);
31874 //            } else {
31875 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31876 //            }
31877 //            
31878 //            y = y + height + this.alternativePadWidth;
31879 //            
31880 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31881 //            
31882 //        }, this);
31883 //        
31884 //        this.el.setHeight(maxHeight);
31885 //        
31886 //    },
31887     
31888     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31889     {
31890         var pos = this.el.getBox(true);
31891         
31892         var minX = pos.x;
31893         var minY = pos.y;
31894         
31895         var maxX = pos.right;
31896         
31897         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31898         
31899         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31900         
31901         Roo.each(queue, function(box, k){
31902             
31903             Roo.each(box, function(b, kk){
31904                 
31905                 b.el.position('absolute');
31906                 
31907                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31908                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31909                 
31910                 if(b.size == 'md-left' || b.size == 'md-right'){
31911                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31912                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31913                 }
31914                 
31915                 b.el.setWidth(width);
31916                 b.el.setHeight(height);
31917                 
31918             }, this);
31919             
31920             if(!box.length){
31921                 return;
31922             }
31923             
31924             var positions = [];
31925             
31926             switch (box.length){
31927                 case 1 :
31928                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31929                     break;
31930                 case 2 :
31931                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31932                     break;
31933                 case 3 :
31934                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31935                     break;
31936                 case 4 :
31937                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31938                     break;
31939                 default :
31940                     break;
31941             }
31942             
31943             Roo.each(box, function(b,kk){
31944                 
31945                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31946                 
31947                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31948                 
31949             }, this);
31950             
31951         }, this);
31952         
31953     },
31954     
31955     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31956     {
31957         Roo.each(eItems, function(b,k){
31958             
31959             b.size = (k == 0) ? 'sm' : 'xs';
31960             b.x = (k == 0) ? 2 : 1;
31961             b.y = (k == 0) ? 2 : 1;
31962             
31963             b.el.position('absolute');
31964             
31965             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31966                 
31967             b.el.setWidth(width);
31968             
31969             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31970             
31971             b.el.setHeight(height);
31972             
31973         }, this);
31974
31975         var positions = [];
31976         
31977         positions.push({
31978             x : maxX - this.unitWidth * 2 - this.gutter,
31979             y : minY
31980         });
31981         
31982         positions.push({
31983             x : maxX - this.unitWidth,
31984             y : minY + (this.unitWidth + this.gutter) * 2
31985         });
31986         
31987         positions.push({
31988             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31989             y : minY
31990         });
31991         
31992         Roo.each(eItems, function(b,k){
31993             
31994             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31995
31996         }, this);
31997         
31998     },
31999     
32000     getVerticalOneBoxColPositions : function(x, y, box)
32001     {
32002         var pos = [];
32003         
32004         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
32005         
32006         if(box[0].size == 'md-left'){
32007             rand = 0;
32008         }
32009         
32010         if(box[0].size == 'md-right'){
32011             rand = 1;
32012         }
32013         
32014         pos.push({
32015             x : x + (this.unitWidth + this.gutter) * rand,
32016             y : y
32017         });
32018         
32019         return pos;
32020     },
32021     
32022     getVerticalTwoBoxColPositions : function(x, y, box)
32023     {
32024         var pos = [];
32025         
32026         if(box[0].size == 'xs'){
32027             
32028             pos.push({
32029                 x : x,
32030                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
32031             });
32032
32033             pos.push({
32034                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
32035                 y : y
32036             });
32037             
32038             return pos;
32039             
32040         }
32041         
32042         pos.push({
32043             x : x,
32044             y : y
32045         });
32046
32047         pos.push({
32048             x : x + (this.unitWidth + this.gutter) * 2,
32049             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
32050         });
32051         
32052         return pos;
32053         
32054     },
32055     
32056     getVerticalThreeBoxColPositions : function(x, y, box)
32057     {
32058         var pos = [];
32059         
32060         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32061             
32062             pos.push({
32063                 x : x,
32064                 y : y
32065             });
32066
32067             pos.push({
32068                 x : x + (this.unitWidth + this.gutter) * 1,
32069                 y : y
32070             });
32071             
32072             pos.push({
32073                 x : x + (this.unitWidth + this.gutter) * 2,
32074                 y : y
32075             });
32076             
32077             return pos;
32078             
32079         }
32080         
32081         if(box[0].size == 'xs' && box[1].size == 'xs'){
32082             
32083             pos.push({
32084                 x : x,
32085                 y : y
32086             });
32087
32088             pos.push({
32089                 x : x,
32090                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
32091             });
32092             
32093             pos.push({
32094                 x : x + (this.unitWidth + this.gutter) * 1,
32095                 y : y
32096             });
32097             
32098             return pos;
32099             
32100         }
32101         
32102         pos.push({
32103             x : x,
32104             y : y
32105         });
32106
32107         pos.push({
32108             x : x + (this.unitWidth + this.gutter) * 2,
32109             y : y
32110         });
32111
32112         pos.push({
32113             x : x + (this.unitWidth + this.gutter) * 2,
32114             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
32115         });
32116             
32117         return pos;
32118         
32119     },
32120     
32121     getVerticalFourBoxColPositions : function(x, y, box)
32122     {
32123         var pos = [];
32124         
32125         if(box[0].size == 'xs'){
32126             
32127             pos.push({
32128                 x : x,
32129                 y : y
32130             });
32131
32132             pos.push({
32133                 x : x,
32134                 y : y + (this.unitHeight + this.gutter) * 1
32135             });
32136             
32137             pos.push({
32138                 x : x,
32139                 y : y + (this.unitHeight + this.gutter) * 2
32140             });
32141             
32142             pos.push({
32143                 x : x + (this.unitWidth + this.gutter) * 1,
32144                 y : y
32145             });
32146             
32147             return pos;
32148             
32149         }
32150         
32151         pos.push({
32152             x : x,
32153             y : y
32154         });
32155
32156         pos.push({
32157             x : x + (this.unitWidth + this.gutter) * 2,
32158             y : y
32159         });
32160
32161         pos.push({
32162             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
32163             y : y + (this.unitHeight + this.gutter) * 1
32164         });
32165
32166         pos.push({
32167             x : x + (this.unitWidth + this.gutter) * 2,
32168             y : y + (this.unitWidth + this.gutter) * 2
32169         });
32170
32171         return pos;
32172         
32173     },
32174     
32175     getHorizontalOneBoxColPositions : function(maxX, minY, box)
32176     {
32177         var pos = [];
32178         
32179         if(box[0].size == 'md-left'){
32180             pos.push({
32181                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32182                 y : minY
32183             });
32184             
32185             return pos;
32186         }
32187         
32188         if(box[0].size == 'md-right'){
32189             pos.push({
32190                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32191                 y : minY + (this.unitWidth + this.gutter) * 1
32192             });
32193             
32194             return pos;
32195         }
32196         
32197         var rand = Math.floor(Math.random() * (4 - box[0].y));
32198         
32199         pos.push({
32200             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32201             y : minY + (this.unitWidth + this.gutter) * rand
32202         });
32203         
32204         return pos;
32205         
32206     },
32207     
32208     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
32209     {
32210         var pos = [];
32211         
32212         if(box[0].size == 'xs'){
32213             
32214             pos.push({
32215                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32216                 y : minY
32217             });
32218
32219             pos.push({
32220                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32221                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32222             });
32223             
32224             return pos;
32225             
32226         }
32227         
32228         pos.push({
32229             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32230             y : minY
32231         });
32232
32233         pos.push({
32234             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32235             y : minY + (this.unitWidth + this.gutter) * 2
32236         });
32237         
32238         return pos;
32239         
32240     },
32241     
32242     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32243     {
32244         var pos = [];
32245         
32246         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32247             
32248             pos.push({
32249                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32250                 y : minY
32251             });
32252
32253             pos.push({
32254                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32255                 y : minY + (this.unitWidth + this.gutter) * 1
32256             });
32257             
32258             pos.push({
32259                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32260                 y : minY + (this.unitWidth + this.gutter) * 2
32261             });
32262             
32263             return pos;
32264             
32265         }
32266         
32267         if(box[0].size == 'xs' && box[1].size == 'xs'){
32268             
32269             pos.push({
32270                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32271                 y : minY
32272             });
32273
32274             pos.push({
32275                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32276                 y : minY
32277             });
32278             
32279             pos.push({
32280                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32281                 y : minY + (this.unitWidth + this.gutter) * 1
32282             });
32283             
32284             return pos;
32285             
32286         }
32287         
32288         pos.push({
32289             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32290             y : minY
32291         });
32292
32293         pos.push({
32294             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32295             y : minY + (this.unitWidth + this.gutter) * 2
32296         });
32297
32298         pos.push({
32299             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32300             y : minY + (this.unitWidth + this.gutter) * 2
32301         });
32302             
32303         return pos;
32304         
32305     },
32306     
32307     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32308     {
32309         var pos = [];
32310         
32311         if(box[0].size == 'xs'){
32312             
32313             pos.push({
32314                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32315                 y : minY
32316             });
32317
32318             pos.push({
32319                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32320                 y : minY
32321             });
32322             
32323             pos.push({
32324                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32325                 y : minY
32326             });
32327             
32328             pos.push({
32329                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32330                 y : minY + (this.unitWidth + this.gutter) * 1
32331             });
32332             
32333             return pos;
32334             
32335         }
32336         
32337         pos.push({
32338             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32339             y : minY
32340         });
32341         
32342         pos.push({
32343             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32344             y : minY + (this.unitWidth + this.gutter) * 2
32345         });
32346         
32347         pos.push({
32348             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32349             y : minY + (this.unitWidth + this.gutter) * 2
32350         });
32351         
32352         pos.push({
32353             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1) - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32354             y : minY + (this.unitWidth + this.gutter) * 2
32355         });
32356
32357         return pos;
32358         
32359     },
32360     
32361     /**
32362     * remove a Masonry Brick
32363     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32364     */
32365     removeBrick : function(brick_id)
32366     {
32367         if (!brick_id) {
32368             return;
32369         }
32370         
32371         for (var i = 0; i<this.bricks.length; i++) {
32372             if (this.bricks[i].id == brick_id) {
32373                 this.bricks.splice(i,1);
32374                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32375                 this.initial();
32376             }
32377         }
32378     },
32379     
32380     /**
32381     * adds a Masonry Brick
32382     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32383     */
32384     addBrick : function(cfg)
32385     {
32386         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32387         //this.register(cn);
32388         cn.parentId = this.id;
32389         cn.render(this.el);
32390         return cn;
32391     },
32392     
32393     /**
32394     * register a Masonry Brick
32395     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32396     */
32397     
32398     register : function(brick)
32399     {
32400         this.bricks.push(brick);
32401         brick.masonryId = this.id;
32402     },
32403     
32404     /**
32405     * clear all the Masonry Brick
32406     */
32407     clearAll : function()
32408     {
32409         this.bricks = [];
32410         //this.getChildContainer().dom.innerHTML = "";
32411         this.el.dom.innerHTML = '';
32412     },
32413     
32414     getSelected : function()
32415     {
32416         if (!this.selectedBrick) {
32417             return false;
32418         }
32419         
32420         return this.selectedBrick;
32421     }
32422 });
32423
32424 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32425     
32426     groups: {},
32427      /**
32428     * register a Masonry Layout
32429     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32430     */
32431     
32432     register : function(layout)
32433     {
32434         this.groups[layout.id] = layout;
32435     },
32436     /**
32437     * fetch a  Masonry Layout based on the masonry layout ID
32438     * @param {string} the masonry layout to add
32439     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32440     */
32441     
32442     get: function(layout_id) {
32443         if (typeof(this.groups[layout_id]) == 'undefined') {
32444             return false;
32445         }
32446         return this.groups[layout_id] ;
32447     }
32448     
32449     
32450     
32451 });
32452
32453  
32454
32455  /**
32456  *
32457  * This is based on 
32458  * http://masonry.desandro.com
32459  *
32460  * The idea is to render all the bricks based on vertical width...
32461  *
32462  * The original code extends 'outlayer' - we might need to use that....
32463  * 
32464  */
32465
32466
32467 /**
32468  * @class Roo.bootstrap.LayoutMasonryAuto
32469  * @extends Roo.bootstrap.Component
32470  * Bootstrap Layout Masonry class
32471  * 
32472  * @constructor
32473  * Create a new Element
32474  * @param {Object} config The config object
32475  */
32476
32477 Roo.bootstrap.LayoutMasonryAuto = function(config){
32478     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32479 };
32480
32481 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32482     
32483       /**
32484      * @cfg {Boolean} isFitWidth  - resize the width..
32485      */   
32486     isFitWidth : false,  // options..
32487     /**
32488      * @cfg {Boolean} isOriginLeft = left align?
32489      */   
32490     isOriginLeft : true,
32491     /**
32492      * @cfg {Boolean} isOriginTop = top align?
32493      */   
32494     isOriginTop : false,
32495     /**
32496      * @cfg {Boolean} isLayoutInstant = no animation?
32497      */   
32498     isLayoutInstant : false, // needed?
32499     /**
32500      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32501      */   
32502     isResizingContainer : true,
32503     /**
32504      * @cfg {Number} columnWidth  width of the columns 
32505      */   
32506     
32507     columnWidth : 0,
32508     
32509     /**
32510      * @cfg {Number} maxCols maximum number of columns
32511      */   
32512     
32513     maxCols: 0,
32514     /**
32515      * @cfg {Number} padHeight padding below box..
32516      */   
32517     
32518     padHeight : 10, 
32519     
32520     /**
32521      * @cfg {Boolean} isAutoInitial defalut true
32522      */   
32523     
32524     isAutoInitial : true, 
32525     
32526     // private?
32527     gutter : 0,
32528     
32529     containerWidth: 0,
32530     initialColumnWidth : 0,
32531     currentSize : null,
32532     
32533     colYs : null, // array.
32534     maxY : 0,
32535     padWidth: 10,
32536     
32537     
32538     tag: 'div',
32539     cls: '',
32540     bricks: null, //CompositeElement
32541     cols : 0, // array?
32542     // element : null, // wrapped now this.el
32543     _isLayoutInited : null, 
32544     
32545     
32546     getAutoCreate : function(){
32547         
32548         var cfg = {
32549             tag: this.tag,
32550             cls: 'blog-masonary-wrapper ' + this.cls,
32551             cn : {
32552                 cls : 'mas-boxes masonary'
32553             }
32554         };
32555         
32556         return cfg;
32557     },
32558     
32559     getChildContainer: function( )
32560     {
32561         if (this.boxesEl) {
32562             return this.boxesEl;
32563         }
32564         
32565         this.boxesEl = this.el.select('.mas-boxes').first();
32566         
32567         return this.boxesEl;
32568     },
32569     
32570     
32571     initEvents : function()
32572     {
32573         var _this = this;
32574         
32575         if(this.isAutoInitial){
32576             Roo.log('hook children rendered');
32577             this.on('childrenrendered', function() {
32578                 Roo.log('children rendered');
32579                 _this.initial();
32580             } ,this);
32581         }
32582         
32583     },
32584     
32585     initial : function()
32586     {
32587         this.reloadItems();
32588
32589         this.currentSize = this.el.getBox(true);
32590
32591         /// was window resize... - let's see if this works..
32592         Roo.EventManager.onWindowResize(this.resize, this); 
32593
32594         if(!this.isAutoInitial){
32595             this.layout();
32596             return;
32597         }
32598         
32599         this.layout.defer(500,this);
32600     },
32601     
32602     reloadItems: function()
32603     {
32604         this.bricks = this.el.select('.masonry-brick', true);
32605         
32606         this.bricks.each(function(b) {
32607             //Roo.log(b.getSize());
32608             if (!b.attr('originalwidth')) {
32609                 b.attr('originalwidth',  b.getSize().width);
32610             }
32611             
32612         });
32613         
32614         Roo.log(this.bricks.elements.length);
32615     },
32616     
32617     resize : function()
32618     {
32619         Roo.log('resize');
32620         var cs = this.el.getBox(true);
32621         
32622         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32623             Roo.log("no change in with or X");
32624             return;
32625         }
32626         this.currentSize = cs;
32627         this.layout();
32628     },
32629     
32630     layout : function()
32631     {
32632          Roo.log('layout');
32633         this._resetLayout();
32634         //this._manageStamps();
32635       
32636         // don't animate first layout
32637         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32638         this.layoutItems( isInstant );
32639       
32640         // flag for initalized
32641         this._isLayoutInited = true;
32642     },
32643     
32644     layoutItems : function( isInstant )
32645     {
32646         //var items = this._getItemsForLayout( this.items );
32647         // original code supports filtering layout items.. we just ignore it..
32648         
32649         this._layoutItems( this.bricks , isInstant );
32650       
32651         this._postLayout();
32652     },
32653     _layoutItems : function ( items , isInstant)
32654     {
32655        //this.fireEvent( 'layout', this, items );
32656     
32657
32658         if ( !items || !items.elements.length ) {
32659           // no items, emit event with empty array
32660             return;
32661         }
32662
32663         var queue = [];
32664         items.each(function(item) {
32665             Roo.log("layout item");
32666             Roo.log(item);
32667             // get x/y object from method
32668             var position = this._getItemLayoutPosition( item );
32669             // enqueue
32670             position.item = item;
32671             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32672             queue.push( position );
32673         }, this);
32674       
32675         this._processLayoutQueue( queue );
32676     },
32677     /** Sets position of item in DOM
32678     * @param {Element} item
32679     * @param {Number} x - horizontal position
32680     * @param {Number} y - vertical position
32681     * @param {Boolean} isInstant - disables transitions
32682     */
32683     _processLayoutQueue : function( queue )
32684     {
32685         for ( var i=0, len = queue.length; i < len; i++ ) {
32686             var obj = queue[i];
32687             obj.item.position('absolute');
32688             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32689         }
32690     },
32691       
32692     
32693     /**
32694     * Any logic you want to do after each layout,
32695     * i.e. size the container
32696     */
32697     _postLayout : function()
32698     {
32699         this.resizeContainer();
32700     },
32701     
32702     resizeContainer : function()
32703     {
32704         if ( !this.isResizingContainer ) {
32705             return;
32706         }
32707         var size = this._getContainerSize();
32708         if ( size ) {
32709             this.el.setSize(size.width,size.height);
32710             this.boxesEl.setSize(size.width,size.height);
32711         }
32712     },
32713     
32714     
32715     
32716     _resetLayout : function()
32717     {
32718         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32719         this.colWidth = this.el.getWidth();
32720         //this.gutter = this.el.getWidth(); 
32721         
32722         this.measureColumns();
32723
32724         // reset column Y
32725         var i = this.cols;
32726         this.colYs = [];
32727         while (i--) {
32728             this.colYs.push( 0 );
32729         }
32730     
32731         this.maxY = 0;
32732     },
32733
32734     measureColumns : function()
32735     {
32736         this.getContainerWidth();
32737       // if columnWidth is 0, default to outerWidth of first item
32738         if ( !this.columnWidth ) {
32739             var firstItem = this.bricks.first();
32740             Roo.log(firstItem);
32741             this.columnWidth  = this.containerWidth;
32742             if (firstItem && firstItem.attr('originalwidth') ) {
32743                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32744             }
32745             // columnWidth fall back to item of first element
32746             Roo.log("set column width?");
32747                         this.initialColumnWidth = this.columnWidth  ;
32748
32749             // if first elem has no width, default to size of container
32750             
32751         }
32752         
32753         
32754         if (this.initialColumnWidth) {
32755             this.columnWidth = this.initialColumnWidth;
32756         }
32757         
32758         
32759             
32760         // column width is fixed at the top - however if container width get's smaller we should
32761         // reduce it...
32762         
32763         // this bit calcs how man columns..
32764             
32765         var columnWidth = this.columnWidth += this.gutter;
32766       
32767         // calculate columns
32768         var containerWidth = this.containerWidth + this.gutter;
32769         
32770         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32771         // fix rounding errors, typically with gutters
32772         var excess = columnWidth - containerWidth % columnWidth;
32773         
32774         
32775         // if overshoot is less than a pixel, round up, otherwise floor it
32776         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32777         cols = Math[ mathMethod ]( cols );
32778         this.cols = Math.max( cols, 1 );
32779         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32780         
32781          // padding positioning..
32782         var totalColWidth = this.cols * this.columnWidth;
32783         var padavail = this.containerWidth - totalColWidth;
32784         // so for 2 columns - we need 3 'pads'
32785         
32786         var padNeeded = (1+this.cols) * this.padWidth;
32787         
32788         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32789         
32790         this.columnWidth += padExtra
32791         //this.padWidth = Math.floor(padavail /  ( this.cols));
32792         
32793         // adjust colum width so that padding is fixed??
32794         
32795         // we have 3 columns ... total = width * 3
32796         // we have X left over... that should be used by 
32797         
32798         //if (this.expandC) {
32799             
32800         //}
32801         
32802         
32803         
32804     },
32805     
32806     getContainerWidth : function()
32807     {
32808        /* // container is parent if fit width
32809         var container = this.isFitWidth ? this.element.parentNode : this.element;
32810         // check that this.size and size are there
32811         // IE8 triggers resize on body size change, so they might not be
32812         
32813         var size = getSize( container );  //FIXME
32814         this.containerWidth = size && size.innerWidth; //FIXME
32815         */
32816          
32817         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32818         
32819     },
32820     
32821     _getItemLayoutPosition : function( item )  // what is item?
32822     {
32823         // we resize the item to our columnWidth..
32824       
32825         item.setWidth(this.columnWidth);
32826         item.autoBoxAdjust  = false;
32827         
32828         var sz = item.getSize();
32829  
32830         // how many columns does this brick span
32831         var remainder = this.containerWidth % this.columnWidth;
32832         
32833         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32834         // round if off by 1 pixel, otherwise use ceil
32835         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32836         colSpan = Math.min( colSpan, this.cols );
32837         
32838         // normally this should be '1' as we dont' currently allow multi width columns..
32839         
32840         var colGroup = this._getColGroup( colSpan );
32841         // get the minimum Y value from the columns
32842         var minimumY = Math.min.apply( Math, colGroup );
32843         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32844         
32845         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32846          
32847         // position the brick
32848         var position = {
32849             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32850             y: this.currentSize.y + minimumY + this.padHeight
32851         };
32852         
32853         Roo.log(position);
32854         // apply setHeight to necessary columns
32855         var setHeight = minimumY + sz.height + this.padHeight;
32856         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32857         
32858         var setSpan = this.cols + 1 - colGroup.length;
32859         for ( var i = 0; i < setSpan; i++ ) {
32860           this.colYs[ shortColIndex + i ] = setHeight ;
32861         }
32862       
32863         return position;
32864     },
32865     
32866     /**
32867      * @param {Number} colSpan - number of columns the element spans
32868      * @returns {Array} colGroup
32869      */
32870     _getColGroup : function( colSpan )
32871     {
32872         if ( colSpan < 2 ) {
32873           // if brick spans only one column, use all the column Ys
32874           return this.colYs;
32875         }
32876       
32877         var colGroup = [];
32878         // how many different places could this brick fit horizontally
32879         var groupCount = this.cols + 1 - colSpan;
32880         // for each group potential horizontal position
32881         for ( var i = 0; i < groupCount; i++ ) {
32882           // make an array of colY values for that one group
32883           var groupColYs = this.colYs.slice( i, i + colSpan );
32884           // and get the max value of the array
32885           colGroup[i] = Math.max.apply( Math, groupColYs );
32886         }
32887         return colGroup;
32888     },
32889     /*
32890     _manageStamp : function( stamp )
32891     {
32892         var stampSize =  stamp.getSize();
32893         var offset = stamp.getBox();
32894         // get the columns that this stamp affects
32895         var firstX = this.isOriginLeft ? offset.x : offset.right;
32896         var lastX = firstX + stampSize.width;
32897         var firstCol = Math.floor( firstX / this.columnWidth );
32898         firstCol = Math.max( 0, firstCol );
32899         
32900         var lastCol = Math.floor( lastX / this.columnWidth );
32901         // lastCol should not go over if multiple of columnWidth #425
32902         lastCol -= lastX % this.columnWidth ? 0 : 1;
32903         lastCol = Math.min( this.cols - 1, lastCol );
32904         
32905         // set colYs to bottom of the stamp
32906         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32907             stampSize.height;
32908             
32909         for ( var i = firstCol; i <= lastCol; i++ ) {
32910           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32911         }
32912     },
32913     */
32914     
32915     _getContainerSize : function()
32916     {
32917         this.maxY = Math.max.apply( Math, this.colYs );
32918         var size = {
32919             height: this.maxY
32920         };
32921       
32922         if ( this.isFitWidth ) {
32923             size.width = this._getContainerFitWidth();
32924         }
32925       
32926         return size;
32927     },
32928     
32929     _getContainerFitWidth : function()
32930     {
32931         var unusedCols = 0;
32932         // count unused columns
32933         var i = this.cols;
32934         while ( --i ) {
32935           if ( this.colYs[i] !== 0 ) {
32936             break;
32937           }
32938           unusedCols++;
32939         }
32940         // fit container to columns that have been used
32941         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32942     },
32943     
32944     needsResizeLayout : function()
32945     {
32946         var previousWidth = this.containerWidth;
32947         this.getContainerWidth();
32948         return previousWidth !== this.containerWidth;
32949     }
32950  
32951 });
32952
32953  
32954
32955  /*
32956  * - LGPL
32957  *
32958  * element
32959  * 
32960  */
32961
32962 /**
32963  * @class Roo.bootstrap.MasonryBrick
32964  * @extends Roo.bootstrap.Component
32965  * Bootstrap MasonryBrick class
32966  * 
32967  * @constructor
32968  * Create a new MasonryBrick
32969  * @param {Object} config The config object
32970  */
32971
32972 Roo.bootstrap.MasonryBrick = function(config){
32973     
32974     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32975     
32976     Roo.bootstrap.MasonryBrick.register(this);
32977     
32978     this.addEvents({
32979         // raw events
32980         /**
32981          * @event click
32982          * When a MasonryBrick is clcik
32983          * @param {Roo.bootstrap.MasonryBrick} this
32984          * @param {Roo.EventObject} e
32985          */
32986         "click" : true
32987     });
32988 };
32989
32990 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32991     
32992     /**
32993      * @cfg {String} title
32994      */   
32995     title : '',
32996     /**
32997      * @cfg {String} html
32998      */   
32999     html : '',
33000     /**
33001      * @cfg {String} bgimage
33002      */   
33003     bgimage : '',
33004     /**
33005      * @cfg {String} videourl
33006      */   
33007     videourl : '',
33008     /**
33009      * @cfg {String} cls
33010      */   
33011     cls : '',
33012     /**
33013      * @cfg {String} href
33014      */   
33015     href : '',
33016     /**
33017      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
33018      */   
33019     size : 'xs',
33020     
33021     /**
33022      * @cfg {String} placetitle (center|bottom)
33023      */   
33024     placetitle : '',
33025     
33026     /**
33027      * @cfg {Boolean} isFitContainer defalut true
33028      */   
33029     isFitContainer : true, 
33030     
33031     /**
33032      * @cfg {Boolean} preventDefault defalut false
33033      */   
33034     preventDefault : false, 
33035     
33036     /**
33037      * @cfg {Boolean} inverse defalut false
33038      */   
33039     maskInverse : false, 
33040     
33041     getAutoCreate : function()
33042     {
33043         if(!this.isFitContainer){
33044             return this.getSplitAutoCreate();
33045         }
33046         
33047         var cls = 'masonry-brick masonry-brick-full';
33048         
33049         if(this.href.length){
33050             cls += ' masonry-brick-link';
33051         }
33052         
33053         if(this.bgimage.length){
33054             cls += ' masonry-brick-image';
33055         }
33056         
33057         if(this.maskInverse){
33058             cls += ' mask-inverse';
33059         }
33060         
33061         if(!this.html.length && !this.maskInverse && !this.videourl.length){
33062             cls += ' enable-mask';
33063         }
33064         
33065         if(this.size){
33066             cls += ' masonry-' + this.size + '-brick';
33067         }
33068         
33069         if(this.placetitle.length){
33070             
33071             switch (this.placetitle) {
33072                 case 'center' :
33073                     cls += ' masonry-center-title';
33074                     break;
33075                 case 'bottom' :
33076                     cls += ' masonry-bottom-title';
33077                     break;
33078                 default:
33079                     break;
33080             }
33081             
33082         } else {
33083             if(!this.html.length && !this.bgimage.length){
33084                 cls += ' masonry-center-title';
33085             }
33086
33087             if(!this.html.length && this.bgimage.length){
33088                 cls += ' masonry-bottom-title';
33089             }
33090         }
33091         
33092         if(this.cls){
33093             cls += ' ' + this.cls;
33094         }
33095         
33096         var cfg = {
33097             tag: (this.href.length) ? 'a' : 'div',
33098             cls: cls,
33099             cn: [
33100                 {
33101                     tag: 'div',
33102                     cls: 'masonry-brick-mask'
33103                 },
33104                 {
33105                     tag: 'div',
33106                     cls: 'masonry-brick-paragraph',
33107                     cn: []
33108                 }
33109             ]
33110         };
33111         
33112         if(this.href.length){
33113             cfg.href = this.href;
33114         }
33115         
33116         var cn = cfg.cn[1].cn;
33117         
33118         if(this.title.length){
33119             cn.push({
33120                 tag: 'h4',
33121                 cls: 'masonry-brick-title',
33122                 html: this.title
33123             });
33124         }
33125         
33126         if(this.html.length){
33127             cn.push({
33128                 tag: 'p',
33129                 cls: 'masonry-brick-text',
33130                 html: this.html
33131             });
33132         }
33133         
33134         if (!this.title.length && !this.html.length) {
33135             cfg.cn[1].cls += ' hide';
33136         }
33137         
33138         if(this.bgimage.length){
33139             cfg.cn.push({
33140                 tag: 'img',
33141                 cls: 'masonry-brick-image-view',
33142                 src: this.bgimage
33143             });
33144         }
33145         
33146         if(this.videourl.length){
33147             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33148             // youtube support only?
33149             cfg.cn.push({
33150                 tag: 'iframe',
33151                 cls: 'masonry-brick-image-view',
33152                 src: vurl,
33153                 frameborder : 0,
33154                 allowfullscreen : true
33155             });
33156         }
33157         
33158         return cfg;
33159         
33160     },
33161     
33162     getSplitAutoCreate : function()
33163     {
33164         var cls = 'masonry-brick masonry-brick-split';
33165         
33166         if(this.href.length){
33167             cls += ' masonry-brick-link';
33168         }
33169         
33170         if(this.bgimage.length){
33171             cls += ' masonry-brick-image';
33172         }
33173         
33174         if(this.size){
33175             cls += ' masonry-' + this.size + '-brick';
33176         }
33177         
33178         switch (this.placetitle) {
33179             case 'center' :
33180                 cls += ' masonry-center-title';
33181                 break;
33182             case 'bottom' :
33183                 cls += ' masonry-bottom-title';
33184                 break;
33185             default:
33186                 if(!this.bgimage.length){
33187                     cls += ' masonry-center-title';
33188                 }
33189
33190                 if(this.bgimage.length){
33191                     cls += ' masonry-bottom-title';
33192                 }
33193                 break;
33194         }
33195         
33196         if(this.cls){
33197             cls += ' ' + this.cls;
33198         }
33199         
33200         var cfg = {
33201             tag: (this.href.length) ? 'a' : 'div',
33202             cls: cls,
33203             cn: [
33204                 {
33205                     tag: 'div',
33206                     cls: 'masonry-brick-split-head',
33207                     cn: [
33208                         {
33209                             tag: 'div',
33210                             cls: 'masonry-brick-paragraph',
33211                             cn: []
33212                         }
33213                     ]
33214                 },
33215                 {
33216                     tag: 'div',
33217                     cls: 'masonry-brick-split-body',
33218                     cn: []
33219                 }
33220             ]
33221         };
33222         
33223         if(this.href.length){
33224             cfg.href = this.href;
33225         }
33226         
33227         if(this.title.length){
33228             cfg.cn[0].cn[0].cn.push({
33229                 tag: 'h4',
33230                 cls: 'masonry-brick-title',
33231                 html: this.title
33232             });
33233         }
33234         
33235         if(this.html.length){
33236             cfg.cn[1].cn.push({
33237                 tag: 'p',
33238                 cls: 'masonry-brick-text',
33239                 html: this.html
33240             });
33241         }
33242
33243         if(this.bgimage.length){
33244             cfg.cn[0].cn.push({
33245                 tag: 'img',
33246                 cls: 'masonry-brick-image-view',
33247                 src: this.bgimage
33248             });
33249         }
33250         
33251         if(this.videourl.length){
33252             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33253             // youtube support only?
33254             cfg.cn[0].cn.cn.push({
33255                 tag: 'iframe',
33256                 cls: 'masonry-brick-image-view',
33257                 src: vurl,
33258                 frameborder : 0,
33259                 allowfullscreen : true
33260             });
33261         }
33262         
33263         return cfg;
33264     },
33265     
33266     initEvents: function() 
33267     {
33268         switch (this.size) {
33269             case 'xs' :
33270                 this.x = 1;
33271                 this.y = 1;
33272                 break;
33273             case 'sm' :
33274                 this.x = 2;
33275                 this.y = 2;
33276                 break;
33277             case 'md' :
33278             case 'md-left' :
33279             case 'md-right' :
33280                 this.x = 3;
33281                 this.y = 3;
33282                 break;
33283             case 'tall' :
33284                 this.x = 2;
33285                 this.y = 3;
33286                 break;
33287             case 'wide' :
33288                 this.x = 3;
33289                 this.y = 2;
33290                 break;
33291             case 'wide-thin' :
33292                 this.x = 3;
33293                 this.y = 1;
33294                 break;
33295                         
33296             default :
33297                 break;
33298         }
33299         
33300         if(Roo.isTouch){
33301             this.el.on('touchstart', this.onTouchStart, this);
33302             this.el.on('touchmove', this.onTouchMove, this);
33303             this.el.on('touchend', this.onTouchEnd, this);
33304             this.el.on('contextmenu', this.onContextMenu, this);
33305         } else {
33306             this.el.on('mouseenter'  ,this.enter, this);
33307             this.el.on('mouseleave', this.leave, this);
33308             this.el.on('click', this.onClick, this);
33309         }
33310         
33311         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33312             this.parent().bricks.push(this);   
33313         }
33314         
33315     },
33316     
33317     onClick: function(e, el)
33318     {
33319         var time = this.endTimer - this.startTimer;
33320         // Roo.log(e.preventDefault());
33321         if(Roo.isTouch){
33322             if(time > 1000){
33323                 e.preventDefault();
33324                 return;
33325             }
33326         }
33327         
33328         if(!this.preventDefault){
33329             return;
33330         }
33331         
33332         e.preventDefault();
33333         
33334         if (this.activeClass != '') {
33335             this.selectBrick();
33336         }
33337         
33338         this.fireEvent('click', this, e);
33339     },
33340     
33341     enter: function(e, el)
33342     {
33343         e.preventDefault();
33344         
33345         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33346             return;
33347         }
33348         
33349         if(this.bgimage.length && this.html.length){
33350             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33351         }
33352     },
33353     
33354     leave: function(e, el)
33355     {
33356         e.preventDefault();
33357         
33358         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33359             return;
33360         }
33361         
33362         if(this.bgimage.length && this.html.length){
33363             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33364         }
33365     },
33366     
33367     onTouchStart: function(e, el)
33368     {
33369 //        e.preventDefault();
33370         
33371         this.touchmoved = false;
33372         
33373         if(!this.isFitContainer){
33374             return;
33375         }
33376         
33377         if(!this.bgimage.length || !this.html.length){
33378             return;
33379         }
33380         
33381         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33382         
33383         this.timer = new Date().getTime();
33384         
33385     },
33386     
33387     onTouchMove: function(e, el)
33388     {
33389         this.touchmoved = true;
33390     },
33391     
33392     onContextMenu : function(e,el)
33393     {
33394         e.preventDefault();
33395         e.stopPropagation();
33396         return false;
33397     },
33398     
33399     onTouchEnd: function(e, el)
33400     {
33401 //        e.preventDefault();
33402         
33403         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33404         
33405             this.leave(e,el);
33406             
33407             return;
33408         }
33409         
33410         if(!this.bgimage.length || !this.html.length){
33411             
33412             if(this.href.length){
33413                 window.location.href = this.href;
33414             }
33415             
33416             return;
33417         }
33418         
33419         if(!this.isFitContainer){
33420             return;
33421         }
33422         
33423         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33424         
33425         window.location.href = this.href;
33426     },
33427     
33428     //selection on single brick only
33429     selectBrick : function() {
33430         
33431         if (!this.parentId) {
33432             return;
33433         }
33434         
33435         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33436         var index = m.selectedBrick.indexOf(this.id);
33437         
33438         if ( index > -1) {
33439             m.selectedBrick.splice(index,1);
33440             this.el.removeClass(this.activeClass);
33441             return;
33442         }
33443         
33444         for(var i = 0; i < m.selectedBrick.length; i++) {
33445             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33446             b.el.removeClass(b.activeClass);
33447         }
33448         
33449         m.selectedBrick = [];
33450         
33451         m.selectedBrick.push(this.id);
33452         this.el.addClass(this.activeClass);
33453         return;
33454     },
33455     
33456     isSelected : function(){
33457         return this.el.hasClass(this.activeClass);
33458         
33459     }
33460 });
33461
33462 Roo.apply(Roo.bootstrap.MasonryBrick, {
33463     
33464     //groups: {},
33465     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33466      /**
33467     * register a Masonry Brick
33468     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33469     */
33470     
33471     register : function(brick)
33472     {
33473         //this.groups[brick.id] = brick;
33474         this.groups.add(brick.id, brick);
33475     },
33476     /**
33477     * fetch a  masonry brick based on the masonry brick ID
33478     * @param {string} the masonry brick to add
33479     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33480     */
33481     
33482     get: function(brick_id) 
33483     {
33484         // if (typeof(this.groups[brick_id]) == 'undefined') {
33485         //     return false;
33486         // }
33487         // return this.groups[brick_id] ;
33488         
33489         if(this.groups.key(brick_id)) {
33490             return this.groups.key(brick_id);
33491         }
33492         
33493         return false;
33494     }
33495     
33496     
33497     
33498 });
33499
33500  /*
33501  * - LGPL
33502  *
33503  * element
33504  * 
33505  */
33506
33507 /**
33508  * @class Roo.bootstrap.Brick
33509  * @extends Roo.bootstrap.Component
33510  * Bootstrap Brick class
33511  * 
33512  * @constructor
33513  * Create a new Brick
33514  * @param {Object} config The config object
33515  */
33516
33517 Roo.bootstrap.Brick = function(config){
33518     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33519     
33520     this.addEvents({
33521         // raw events
33522         /**
33523          * @event click
33524          * When a Brick is click
33525          * @param {Roo.bootstrap.Brick} this
33526          * @param {Roo.EventObject} e
33527          */
33528         "click" : true
33529     });
33530 };
33531
33532 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33533     
33534     /**
33535      * @cfg {String} title
33536      */   
33537     title : '',
33538     /**
33539      * @cfg {String} html
33540      */   
33541     html : '',
33542     /**
33543      * @cfg {String} bgimage
33544      */   
33545     bgimage : '',
33546     /**
33547      * @cfg {String} cls
33548      */   
33549     cls : '',
33550     /**
33551      * @cfg {String} href
33552      */   
33553     href : '',
33554     /**
33555      * @cfg {String} video
33556      */   
33557     video : '',
33558     /**
33559      * @cfg {Boolean} square
33560      */   
33561     square : true,
33562     
33563     getAutoCreate : function()
33564     {
33565         var cls = 'roo-brick';
33566         
33567         if(this.href.length){
33568             cls += ' roo-brick-link';
33569         }
33570         
33571         if(this.bgimage.length){
33572             cls += ' roo-brick-image';
33573         }
33574         
33575         if(!this.html.length && !this.bgimage.length){
33576             cls += ' roo-brick-center-title';
33577         }
33578         
33579         if(!this.html.length && this.bgimage.length){
33580             cls += ' roo-brick-bottom-title';
33581         }
33582         
33583         if(this.cls){
33584             cls += ' ' + this.cls;
33585         }
33586         
33587         var cfg = {
33588             tag: (this.href.length) ? 'a' : 'div',
33589             cls: cls,
33590             cn: [
33591                 {
33592                     tag: 'div',
33593                     cls: 'roo-brick-paragraph',
33594                     cn: []
33595                 }
33596             ]
33597         };
33598         
33599         if(this.href.length){
33600             cfg.href = this.href;
33601         }
33602         
33603         var cn = cfg.cn[0].cn;
33604         
33605         if(this.title.length){
33606             cn.push({
33607                 tag: 'h4',
33608                 cls: 'roo-brick-title',
33609                 html: this.title
33610             });
33611         }
33612         
33613         if(this.html.length){
33614             cn.push({
33615                 tag: 'p',
33616                 cls: 'roo-brick-text',
33617                 html: this.html
33618             });
33619         } else {
33620             cn.cls += ' hide';
33621         }
33622         
33623         if(this.bgimage.length){
33624             cfg.cn.push({
33625                 tag: 'img',
33626                 cls: 'roo-brick-image-view',
33627                 src: this.bgimage
33628             });
33629         }
33630         
33631         return cfg;
33632     },
33633     
33634     initEvents: function() 
33635     {
33636         if(this.title.length || this.html.length){
33637             this.el.on('mouseenter'  ,this.enter, this);
33638             this.el.on('mouseleave', this.leave, this);
33639         }
33640         
33641         Roo.EventManager.onWindowResize(this.resize, this); 
33642         
33643         if(this.bgimage.length){
33644             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33645             this.imageEl.on('load', this.onImageLoad, this);
33646             return;
33647         }
33648         
33649         this.resize();
33650     },
33651     
33652     onImageLoad : function()
33653     {
33654         this.resize();
33655     },
33656     
33657     resize : function()
33658     {
33659         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33660         
33661         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33662         
33663         if(this.bgimage.length){
33664             var image = this.el.select('.roo-brick-image-view', true).first();
33665             
33666             image.setWidth(paragraph.getWidth());
33667             
33668             if(this.square){
33669                 image.setHeight(paragraph.getWidth());
33670             }
33671             
33672             this.el.setHeight(image.getHeight());
33673             paragraph.setHeight(image.getHeight());
33674             
33675         }
33676         
33677     },
33678     
33679     enter: function(e, el)
33680     {
33681         e.preventDefault();
33682         
33683         if(this.bgimage.length){
33684             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33685             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33686         }
33687     },
33688     
33689     leave: function(e, el)
33690     {
33691         e.preventDefault();
33692         
33693         if(this.bgimage.length){
33694             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33695             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33696         }
33697     }
33698     
33699 });
33700
33701  
33702
33703  /*
33704  * - LGPL
33705  *
33706  * Number field 
33707  */
33708
33709 /**
33710  * @class Roo.bootstrap.NumberField
33711  * @extends Roo.bootstrap.Input
33712  * Bootstrap NumberField class
33713  * 
33714  * 
33715  * 
33716  * 
33717  * @constructor
33718  * Create a new NumberField
33719  * @param {Object} config The config object
33720  */
33721
33722 Roo.bootstrap.NumberField = function(config){
33723     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33724 };
33725
33726 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33727     
33728     /**
33729      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33730      */
33731     allowDecimals : true,
33732     /**
33733      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33734      */
33735     decimalSeparator : ".",
33736     /**
33737      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33738      */
33739     decimalPrecision : 2,
33740     /**
33741      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33742      */
33743     allowNegative : true,
33744     
33745     /**
33746      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33747      */
33748     allowZero: true,
33749     /**
33750      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33751      */
33752     minValue : Number.NEGATIVE_INFINITY,
33753     /**
33754      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33755      */
33756     maxValue : Number.MAX_VALUE,
33757     /**
33758      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33759      */
33760     minText : "The minimum value for this field is {0}",
33761     /**
33762      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33763      */
33764     maxText : "The maximum value for this field is {0}",
33765     /**
33766      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33767      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33768      */
33769     nanText : "{0} is not a valid number",
33770     /**
33771      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33772      */
33773     thousandsDelimiter : false,
33774     /**
33775      * @cfg {String} valueAlign alignment of value
33776      */
33777     valueAlign : "left",
33778
33779     getAutoCreate : function()
33780     {
33781         var hiddenInput = {
33782             tag: 'input',
33783             type: 'hidden',
33784             id: Roo.id(),
33785             cls: 'hidden-number-input'
33786         };
33787         
33788         if (this.name) {
33789             hiddenInput.name = this.name;
33790         }
33791         
33792         this.name = '';
33793         
33794         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33795         
33796         this.name = hiddenInput.name;
33797         
33798         if(cfg.cn.length > 0) {
33799             cfg.cn.push(hiddenInput);
33800         }
33801         
33802         return cfg;
33803     },
33804
33805     // private
33806     initEvents : function()
33807     {   
33808         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33809         
33810         var allowed = "0123456789";
33811         
33812         if(this.allowDecimals){
33813             allowed += this.decimalSeparator;
33814         }
33815         
33816         if(this.allowNegative){
33817             allowed += "-";
33818         }
33819         
33820         if(this.thousandsDelimiter) {
33821             allowed += ",";
33822         }
33823         
33824         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33825         
33826         var keyPress = function(e){
33827             
33828             var k = e.getKey();
33829             
33830             var c = e.getCharCode();
33831             
33832             if(
33833                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33834                     allowed.indexOf(String.fromCharCode(c)) === -1
33835             ){
33836                 e.stopEvent();
33837                 return;
33838             }
33839             
33840             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33841                 return;
33842             }
33843             
33844             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33845                 e.stopEvent();
33846             }
33847         };
33848         
33849         this.el.on("keypress", keyPress, this);
33850     },
33851     
33852     validateValue : function(value)
33853     {
33854         
33855         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33856             return false;
33857         }
33858         
33859         var num = this.parseValue(value);
33860         
33861         if(isNaN(num)){
33862             this.markInvalid(String.format(this.nanText, value));
33863             return false;
33864         }
33865         
33866         if(num < this.minValue){
33867             this.markInvalid(String.format(this.minText, this.minValue));
33868             return false;
33869         }
33870         
33871         if(num > this.maxValue){
33872             this.markInvalid(String.format(this.maxText, this.maxValue));
33873             return false;
33874         }
33875         
33876         return true;
33877     },
33878
33879     getValue : function()
33880     {
33881         var v = this.hiddenEl().getValue();
33882         
33883         return this.fixPrecision(this.parseValue(v));
33884     },
33885
33886     parseValue : function(value)
33887     {
33888         if(this.thousandsDelimiter) {
33889             value += "";
33890             r = new RegExp(",", "g");
33891             value = value.replace(r, "");
33892         }
33893         
33894         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33895         return isNaN(value) ? '' : value;
33896     },
33897
33898     fixPrecision : function(value)
33899     {
33900         if(this.thousandsDelimiter) {
33901             value += "";
33902             r = new RegExp(",", "g");
33903             value = value.replace(r, "");
33904         }
33905         
33906         var nan = isNaN(value);
33907         
33908         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33909             return nan ? '' : value;
33910         }
33911         return parseFloat(value).toFixed(this.decimalPrecision);
33912     },
33913
33914     setValue : function(v)
33915     {
33916         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33917         
33918         this.value = v;
33919         
33920         if(this.rendered){
33921             
33922             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33923             
33924             this.inputEl().dom.value = (v == '') ? '' :
33925                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33926             
33927             if(!this.allowZero && v === '0') {
33928                 this.hiddenEl().dom.value = '';
33929                 this.inputEl().dom.value = '';
33930             }
33931             
33932             this.validate();
33933         }
33934     },
33935
33936     decimalPrecisionFcn : function(v)
33937     {
33938         return Math.floor(v);
33939     },
33940
33941     beforeBlur : function()
33942     {
33943         var v = this.parseValue(this.getRawValue());
33944         
33945         if(v || v === 0 || v === ''){
33946             this.setValue(v);
33947         }
33948     },
33949     
33950     hiddenEl : function()
33951     {
33952         return this.el.select('input.hidden-number-input',true).first();
33953     }
33954     
33955 });
33956
33957  
33958
33959 /*
33960 * Licence: LGPL
33961 */
33962
33963 /**
33964  * @class Roo.bootstrap.DocumentSlider
33965  * @extends Roo.bootstrap.Component
33966  * Bootstrap DocumentSlider class
33967  * 
33968  * @constructor
33969  * Create a new DocumentViewer
33970  * @param {Object} config The config object
33971  */
33972
33973 Roo.bootstrap.DocumentSlider = function(config){
33974     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33975     
33976     this.files = [];
33977     
33978     this.addEvents({
33979         /**
33980          * @event initial
33981          * Fire after initEvent
33982          * @param {Roo.bootstrap.DocumentSlider} this
33983          */
33984         "initial" : true,
33985         /**
33986          * @event update
33987          * Fire after update
33988          * @param {Roo.bootstrap.DocumentSlider} this
33989          */
33990         "update" : true,
33991         /**
33992          * @event click
33993          * Fire after click
33994          * @param {Roo.bootstrap.DocumentSlider} this
33995          */
33996         "click" : true
33997     });
33998 };
33999
34000 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
34001     
34002     files : false,
34003     
34004     indicator : 0,
34005     
34006     getAutoCreate : function()
34007     {
34008         var cfg = {
34009             tag : 'div',
34010             cls : 'roo-document-slider',
34011             cn : [
34012                 {
34013                     tag : 'div',
34014                     cls : 'roo-document-slider-header',
34015                     cn : [
34016                         {
34017                             tag : 'div',
34018                             cls : 'roo-document-slider-header-title'
34019                         }
34020                     ]
34021                 },
34022                 {
34023                     tag : 'div',
34024                     cls : 'roo-document-slider-body',
34025                     cn : [
34026                         {
34027                             tag : 'div',
34028                             cls : 'roo-document-slider-prev',
34029                             cn : [
34030                                 {
34031                                     tag : 'i',
34032                                     cls : 'fa fa-chevron-left'
34033                                 }
34034                             ]
34035                         },
34036                         {
34037                             tag : 'div',
34038                             cls : 'roo-document-slider-thumb',
34039                             cn : [
34040                                 {
34041                                     tag : 'img',
34042                                     cls : 'roo-document-slider-image'
34043                                 }
34044                             ]
34045                         },
34046                         {
34047                             tag : 'div',
34048                             cls : 'roo-document-slider-next',
34049                             cn : [
34050                                 {
34051                                     tag : 'i',
34052                                     cls : 'fa fa-chevron-right'
34053                                 }
34054                             ]
34055                         }
34056                     ]
34057                 }
34058             ]
34059         };
34060         
34061         return cfg;
34062     },
34063     
34064     initEvents : function()
34065     {
34066         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
34067         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
34068         
34069         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
34070         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
34071         
34072         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
34073         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
34074         
34075         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
34076         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
34077         
34078         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
34079         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
34080         
34081         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
34082         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34083         
34084         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
34085         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34086         
34087         this.thumbEl.on('click', this.onClick, this);
34088         
34089         this.prevIndicator.on('click', this.prev, this);
34090         
34091         this.nextIndicator.on('click', this.next, this);
34092         
34093     },
34094     
34095     initial : function()
34096     {
34097         if(this.files.length){
34098             this.indicator = 1;
34099             this.update()
34100         }
34101         
34102         this.fireEvent('initial', this);
34103     },
34104     
34105     update : function()
34106     {
34107         this.imageEl.attr('src', this.files[this.indicator - 1]);
34108         
34109         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
34110         
34111         this.prevIndicator.show();
34112         
34113         if(this.indicator == 1){
34114             this.prevIndicator.hide();
34115         }
34116         
34117         this.nextIndicator.show();
34118         
34119         if(this.indicator == this.files.length){
34120             this.nextIndicator.hide();
34121         }
34122         
34123         this.thumbEl.scrollTo('top');
34124         
34125         this.fireEvent('update', this);
34126     },
34127     
34128     onClick : function(e)
34129     {
34130         e.preventDefault();
34131         
34132         this.fireEvent('click', this);
34133     },
34134     
34135     prev : function(e)
34136     {
34137         e.preventDefault();
34138         
34139         this.indicator = Math.max(1, this.indicator - 1);
34140         
34141         this.update();
34142     },
34143     
34144     next : function(e)
34145     {
34146         e.preventDefault();
34147         
34148         this.indicator = Math.min(this.files.length, this.indicator + 1);
34149         
34150         this.update();
34151     }
34152 });
34153 /*
34154  * - LGPL
34155  *
34156  * RadioSet
34157  *
34158  *
34159  */
34160
34161 /**
34162  * @class Roo.bootstrap.RadioSet
34163  * @extends Roo.bootstrap.Input
34164  * Bootstrap RadioSet class
34165  * @cfg {String} indicatorpos (left|right) default left
34166  * @cfg {Boolean} inline (true|false) inline the element (default true)
34167  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
34168  * @constructor
34169  * Create a new RadioSet
34170  * @param {Object} config The config object
34171  */
34172
34173 Roo.bootstrap.RadioSet = function(config){
34174     
34175     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
34176     
34177     this.radioes = [];
34178     
34179     Roo.bootstrap.RadioSet.register(this);
34180     
34181     this.addEvents({
34182         /**
34183         * @event check
34184         * Fires when the element is checked or unchecked.
34185         * @param {Roo.bootstrap.RadioSet} this This radio
34186         * @param {Roo.bootstrap.Radio} item The checked item
34187         */
34188        check : true,
34189        /**
34190         * @event click
34191         * Fires when the element is click.
34192         * @param {Roo.bootstrap.RadioSet} this This radio set
34193         * @param {Roo.bootstrap.Radio} item The checked item
34194         * @param {Roo.EventObject} e The event object
34195         */
34196        click : true
34197     });
34198     
34199 };
34200
34201 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
34202
34203     radioes : false,
34204     
34205     inline : true,
34206     
34207     weight : '',
34208     
34209     indicatorpos : 'left',
34210     
34211     getAutoCreate : function()
34212     {
34213         var label = {
34214             tag : 'label',
34215             cls : 'roo-radio-set-label',
34216             cn : [
34217                 {
34218                     tag : 'span',
34219                     html : this.fieldLabel
34220                 }
34221             ]
34222         };
34223         if (Roo.bootstrap.version == 3) {
34224             
34225             
34226             if(this.indicatorpos == 'left'){
34227                 label.cn.unshift({
34228                     tag : 'i',
34229                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34230                     tooltip : 'This field is required'
34231                 });
34232             } else {
34233                 label.cn.push({
34234                     tag : 'i',
34235                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34236                     tooltip : 'This field is required'
34237                 });
34238             }
34239         }
34240         var items = {
34241             tag : 'div',
34242             cls : 'roo-radio-set-items'
34243         };
34244         
34245         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34246         
34247         if (align === 'left' && this.fieldLabel.length) {
34248             
34249             items = {
34250                 cls : "roo-radio-set-right", 
34251                 cn: [
34252                     items
34253                 ]
34254             };
34255             
34256             if(this.labelWidth > 12){
34257                 label.style = "width: " + this.labelWidth + 'px';
34258             }
34259             
34260             if(this.labelWidth < 13 && this.labelmd == 0){
34261                 this.labelmd = this.labelWidth;
34262             }
34263             
34264             if(this.labellg > 0){
34265                 label.cls += ' col-lg-' + this.labellg;
34266                 items.cls += ' col-lg-' + (12 - this.labellg);
34267             }
34268             
34269             if(this.labelmd > 0){
34270                 label.cls += ' col-md-' + this.labelmd;
34271                 items.cls += ' col-md-' + (12 - this.labelmd);
34272             }
34273             
34274             if(this.labelsm > 0){
34275                 label.cls += ' col-sm-' + this.labelsm;
34276                 items.cls += ' col-sm-' + (12 - this.labelsm);
34277             }
34278             
34279             if(this.labelxs > 0){
34280                 label.cls += ' col-xs-' + this.labelxs;
34281                 items.cls += ' col-xs-' + (12 - this.labelxs);
34282             }
34283         }
34284         
34285         var cfg = {
34286             tag : 'div',
34287             cls : 'roo-radio-set',
34288             cn : [
34289                 {
34290                     tag : 'input',
34291                     cls : 'roo-radio-set-input',
34292                     type : 'hidden',
34293                     name : this.name,
34294                     value : this.value ? this.value :  ''
34295                 },
34296                 label,
34297                 items
34298             ]
34299         };
34300         
34301         if(this.weight.length){
34302             cfg.cls += ' roo-radio-' + this.weight;
34303         }
34304         
34305         if(this.inline) {
34306             cfg.cls += ' roo-radio-set-inline';
34307         }
34308         
34309         var settings=this;
34310         ['xs','sm','md','lg'].map(function(size){
34311             if (settings[size]) {
34312                 cfg.cls += ' col-' + size + '-' + settings[size];
34313             }
34314         });
34315         
34316         return cfg;
34317         
34318     },
34319
34320     initEvents : function()
34321     {
34322         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34323         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34324         
34325         if(!this.fieldLabel.length){
34326             this.labelEl.hide();
34327         }
34328         
34329         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34330         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34331         
34332         this.indicator = this.indicatorEl();
34333         
34334         if(this.indicator){
34335             this.indicator.addClass('invisible');
34336         }
34337         
34338         this.originalValue = this.getValue();
34339         
34340     },
34341     
34342     inputEl: function ()
34343     {
34344         return this.el.select('.roo-radio-set-input', true).first();
34345     },
34346     
34347     getChildContainer : function()
34348     {
34349         return this.itemsEl;
34350     },
34351     
34352     register : function(item)
34353     {
34354         this.radioes.push(item);
34355         
34356     },
34357     
34358     validate : function()
34359     {   
34360         if(this.getVisibilityEl().hasClass('hidden')){
34361             return true;
34362         }
34363         
34364         var valid = false;
34365         
34366         Roo.each(this.radioes, function(i){
34367             if(!i.checked){
34368                 return;
34369             }
34370             
34371             valid = true;
34372             return false;
34373         });
34374         
34375         if(this.allowBlank) {
34376             return true;
34377         }
34378         
34379         if(this.disabled || valid){
34380             this.markValid();
34381             return true;
34382         }
34383         
34384         this.markInvalid();
34385         return false;
34386         
34387     },
34388     
34389     markValid : function()
34390     {
34391         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34392             this.indicatorEl().removeClass('visible');
34393             this.indicatorEl().addClass('invisible');
34394         }
34395         
34396         
34397         if (Roo.bootstrap.version == 3) {
34398             this.el.removeClass([this.invalidClass, this.validClass]);
34399             this.el.addClass(this.validClass);
34400         } else {
34401             this.el.removeClass(['is-invalid','is-valid']);
34402             this.el.addClass(['is-valid']);
34403         }
34404         this.fireEvent('valid', this);
34405     },
34406     
34407     markInvalid : function(msg)
34408     {
34409         if(this.allowBlank || this.disabled){
34410             return;
34411         }
34412         
34413         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34414             this.indicatorEl().removeClass('invisible');
34415             this.indicatorEl().addClass('visible');
34416         }
34417         if (Roo.bootstrap.version == 3) {
34418             this.el.removeClass([this.invalidClass, this.validClass]);
34419             this.el.addClass(this.invalidClass);
34420         } else {
34421             this.el.removeClass(['is-invalid','is-valid']);
34422             this.el.addClass(['is-invalid']);
34423         }
34424         
34425         this.fireEvent('invalid', this, msg);
34426         
34427     },
34428     
34429     setValue : function(v, suppressEvent)
34430     {   
34431         if(this.value === v){
34432             return;
34433         }
34434         
34435         this.value = v;
34436         
34437         if(this.rendered){
34438             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34439         }
34440         
34441         Roo.each(this.radioes, function(i){
34442             i.checked = false;
34443             i.el.removeClass('checked');
34444         });
34445         
34446         Roo.each(this.radioes, function(i){
34447             
34448             if(i.value === v || i.value.toString() === v.toString()){
34449                 i.checked = true;
34450                 i.el.addClass('checked');
34451                 
34452                 if(suppressEvent !== true){
34453                     this.fireEvent('check', this, i);
34454                 }
34455                 
34456                 return false;
34457             }
34458             
34459         }, this);
34460         
34461         this.validate();
34462     },
34463     
34464     clearInvalid : function(){
34465         
34466         if(!this.el || this.preventMark){
34467             return;
34468         }
34469         
34470         this.el.removeClass([this.invalidClass]);
34471         
34472         this.fireEvent('valid', this);
34473     }
34474     
34475 });
34476
34477 Roo.apply(Roo.bootstrap.RadioSet, {
34478     
34479     groups: {},
34480     
34481     register : function(set)
34482     {
34483         this.groups[set.name] = set;
34484     },
34485     
34486     get: function(name) 
34487     {
34488         if (typeof(this.groups[name]) == 'undefined') {
34489             return false;
34490         }
34491         
34492         return this.groups[name] ;
34493     }
34494     
34495 });
34496 /*
34497  * Based on:
34498  * Ext JS Library 1.1.1
34499  * Copyright(c) 2006-2007, Ext JS, LLC.
34500  *
34501  * Originally Released Under LGPL - original licence link has changed is not relivant.
34502  *
34503  * Fork - LGPL
34504  * <script type="text/javascript">
34505  */
34506
34507
34508 /**
34509  * @class Roo.bootstrap.SplitBar
34510  * @extends Roo.util.Observable
34511  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34512  * <br><br>
34513  * Usage:
34514  * <pre><code>
34515 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34516                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34517 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34518 split.minSize = 100;
34519 split.maxSize = 600;
34520 split.animate = true;
34521 split.on('moved', splitterMoved);
34522 </code></pre>
34523  * @constructor
34524  * Create a new SplitBar
34525  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34526  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34527  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34528  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34529                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34530                         position of the SplitBar).
34531  */
34532 Roo.bootstrap.SplitBar = function(cfg){
34533     
34534     /** @private */
34535     
34536     //{
34537     //  dragElement : elm
34538     //  resizingElement: el,
34539         // optional..
34540     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34541     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34542         // existingProxy ???
34543     //}
34544     
34545     this.el = Roo.get(cfg.dragElement, true);
34546     this.el.dom.unselectable = "on";
34547     /** @private */
34548     this.resizingEl = Roo.get(cfg.resizingElement, true);
34549
34550     /**
34551      * @private
34552      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34553      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34554      * @type Number
34555      */
34556     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34557     
34558     /**
34559      * The minimum size of the resizing element. (Defaults to 0)
34560      * @type Number
34561      */
34562     this.minSize = 0;
34563     
34564     /**
34565      * The maximum size of the resizing element. (Defaults to 2000)
34566      * @type Number
34567      */
34568     this.maxSize = 2000;
34569     
34570     /**
34571      * Whether to animate the transition to the new size
34572      * @type Boolean
34573      */
34574     this.animate = false;
34575     
34576     /**
34577      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34578      * @type Boolean
34579      */
34580     this.useShim = false;
34581     
34582     /** @private */
34583     this.shim = null;
34584     
34585     if(!cfg.existingProxy){
34586         /** @private */
34587         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34588     }else{
34589         this.proxy = Roo.get(cfg.existingProxy).dom;
34590     }
34591     /** @private */
34592     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34593     
34594     /** @private */
34595     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34596     
34597     /** @private */
34598     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34599     
34600     /** @private */
34601     this.dragSpecs = {};
34602     
34603     /**
34604      * @private The adapter to use to positon and resize elements
34605      */
34606     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34607     this.adapter.init(this);
34608     
34609     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34610         /** @private */
34611         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34612         this.el.addClass("roo-splitbar-h");
34613     }else{
34614         /** @private */
34615         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34616         this.el.addClass("roo-splitbar-v");
34617     }
34618     
34619     this.addEvents({
34620         /**
34621          * @event resize
34622          * Fires when the splitter is moved (alias for {@link #event-moved})
34623          * @param {Roo.bootstrap.SplitBar} this
34624          * @param {Number} newSize the new width or height
34625          */
34626         "resize" : true,
34627         /**
34628          * @event moved
34629          * Fires when the splitter is moved
34630          * @param {Roo.bootstrap.SplitBar} this
34631          * @param {Number} newSize the new width or height
34632          */
34633         "moved" : true,
34634         /**
34635          * @event beforeresize
34636          * Fires before the splitter is dragged
34637          * @param {Roo.bootstrap.SplitBar} this
34638          */
34639         "beforeresize" : true,
34640
34641         "beforeapply" : true
34642     });
34643
34644     Roo.util.Observable.call(this);
34645 };
34646
34647 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34648     onStartProxyDrag : function(x, y){
34649         this.fireEvent("beforeresize", this);
34650         if(!this.overlay){
34651             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34652             o.unselectable();
34653             o.enableDisplayMode("block");
34654             // all splitbars share the same overlay
34655             Roo.bootstrap.SplitBar.prototype.overlay = o;
34656         }
34657         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34658         this.overlay.show();
34659         Roo.get(this.proxy).setDisplayed("block");
34660         var size = this.adapter.getElementSize(this);
34661         this.activeMinSize = this.getMinimumSize();;
34662         this.activeMaxSize = this.getMaximumSize();;
34663         var c1 = size - this.activeMinSize;
34664         var c2 = Math.max(this.activeMaxSize - size, 0);
34665         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34666             this.dd.resetConstraints();
34667             this.dd.setXConstraint(
34668                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34669                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34670             );
34671             this.dd.setYConstraint(0, 0);
34672         }else{
34673             this.dd.resetConstraints();
34674             this.dd.setXConstraint(0, 0);
34675             this.dd.setYConstraint(
34676                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34677                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34678             );
34679          }
34680         this.dragSpecs.startSize = size;
34681         this.dragSpecs.startPoint = [x, y];
34682         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34683     },
34684     
34685     /** 
34686      * @private Called after the drag operation by the DDProxy
34687      */
34688     onEndProxyDrag : function(e){
34689         Roo.get(this.proxy).setDisplayed(false);
34690         var endPoint = Roo.lib.Event.getXY(e);
34691         if(this.overlay){
34692             this.overlay.hide();
34693         }
34694         var newSize;
34695         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34696             newSize = this.dragSpecs.startSize + 
34697                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34698                     endPoint[0] - this.dragSpecs.startPoint[0] :
34699                     this.dragSpecs.startPoint[0] - endPoint[0]
34700                 );
34701         }else{
34702             newSize = this.dragSpecs.startSize + 
34703                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34704                     endPoint[1] - this.dragSpecs.startPoint[1] :
34705                     this.dragSpecs.startPoint[1] - endPoint[1]
34706                 );
34707         }
34708         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34709         if(newSize != this.dragSpecs.startSize){
34710             if(this.fireEvent('beforeapply', this, newSize) !== false){
34711                 this.adapter.setElementSize(this, newSize);
34712                 this.fireEvent("moved", this, newSize);
34713                 this.fireEvent("resize", this, newSize);
34714             }
34715         }
34716     },
34717     
34718     /**
34719      * Get the adapter this SplitBar uses
34720      * @return The adapter object
34721      */
34722     getAdapter : function(){
34723         return this.adapter;
34724     },
34725     
34726     /**
34727      * Set the adapter this SplitBar uses
34728      * @param {Object} adapter A SplitBar adapter object
34729      */
34730     setAdapter : function(adapter){
34731         this.adapter = adapter;
34732         this.adapter.init(this);
34733     },
34734     
34735     /**
34736      * Gets the minimum size for the resizing element
34737      * @return {Number} The minimum size
34738      */
34739     getMinimumSize : function(){
34740         return this.minSize;
34741     },
34742     
34743     /**
34744      * Sets the minimum size for the resizing element
34745      * @param {Number} minSize The minimum size
34746      */
34747     setMinimumSize : function(minSize){
34748         this.minSize = minSize;
34749     },
34750     
34751     /**
34752      * Gets the maximum size for the resizing element
34753      * @return {Number} The maximum size
34754      */
34755     getMaximumSize : function(){
34756         return this.maxSize;
34757     },
34758     
34759     /**
34760      * Sets the maximum size for the resizing element
34761      * @param {Number} maxSize The maximum size
34762      */
34763     setMaximumSize : function(maxSize){
34764         this.maxSize = maxSize;
34765     },
34766     
34767     /**
34768      * Sets the initialize size for the resizing element
34769      * @param {Number} size The initial size
34770      */
34771     setCurrentSize : function(size){
34772         var oldAnimate = this.animate;
34773         this.animate = false;
34774         this.adapter.setElementSize(this, size);
34775         this.animate = oldAnimate;
34776     },
34777     
34778     /**
34779      * Destroy this splitbar. 
34780      * @param {Boolean} removeEl True to remove the element
34781      */
34782     destroy : function(removeEl){
34783         if(this.shim){
34784             this.shim.remove();
34785         }
34786         this.dd.unreg();
34787         this.proxy.parentNode.removeChild(this.proxy);
34788         if(removeEl){
34789             this.el.remove();
34790         }
34791     }
34792 });
34793
34794 /**
34795  * @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.
34796  */
34797 Roo.bootstrap.SplitBar.createProxy = function(dir){
34798     var proxy = new Roo.Element(document.createElement("div"));
34799     proxy.unselectable();
34800     var cls = 'roo-splitbar-proxy';
34801     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34802     document.body.appendChild(proxy.dom);
34803     return proxy.dom;
34804 };
34805
34806 /** 
34807  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34808  * Default Adapter. It assumes the splitter and resizing element are not positioned
34809  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34810  */
34811 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34812 };
34813
34814 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34815     // do nothing for now
34816     init : function(s){
34817     
34818     },
34819     /**
34820      * Called before drag operations to get the current size of the resizing element. 
34821      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34822      */
34823      getElementSize : function(s){
34824         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34825             return s.resizingEl.getWidth();
34826         }else{
34827             return s.resizingEl.getHeight();
34828         }
34829     },
34830     
34831     /**
34832      * Called after drag operations to set the size of the resizing element.
34833      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34834      * @param {Number} newSize The new size to set
34835      * @param {Function} onComplete A function to be invoked when resizing is complete
34836      */
34837     setElementSize : function(s, newSize, onComplete){
34838         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34839             if(!s.animate){
34840                 s.resizingEl.setWidth(newSize);
34841                 if(onComplete){
34842                     onComplete(s, newSize);
34843                 }
34844             }else{
34845                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34846             }
34847         }else{
34848             
34849             if(!s.animate){
34850                 s.resizingEl.setHeight(newSize);
34851                 if(onComplete){
34852                     onComplete(s, newSize);
34853                 }
34854             }else{
34855                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34856             }
34857         }
34858     }
34859 };
34860
34861 /** 
34862  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34863  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34864  * Adapter that  moves the splitter element to align with the resized sizing element. 
34865  * Used with an absolute positioned SplitBar.
34866  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34867  * document.body, make sure you assign an id to the body element.
34868  */
34869 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34870     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34871     this.container = Roo.get(container);
34872 };
34873
34874 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34875     init : function(s){
34876         this.basic.init(s);
34877     },
34878     
34879     getElementSize : function(s){
34880         return this.basic.getElementSize(s);
34881     },
34882     
34883     setElementSize : function(s, newSize, onComplete){
34884         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34885     },
34886     
34887     moveSplitter : function(s){
34888         var yes = Roo.bootstrap.SplitBar;
34889         switch(s.placement){
34890             case yes.LEFT:
34891                 s.el.setX(s.resizingEl.getRight());
34892                 break;
34893             case yes.RIGHT:
34894                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34895                 break;
34896             case yes.TOP:
34897                 s.el.setY(s.resizingEl.getBottom());
34898                 break;
34899             case yes.BOTTOM:
34900                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34901                 break;
34902         }
34903     }
34904 };
34905
34906 /**
34907  * Orientation constant - Create a vertical SplitBar
34908  * @static
34909  * @type Number
34910  */
34911 Roo.bootstrap.SplitBar.VERTICAL = 1;
34912
34913 /**
34914  * Orientation constant - Create a horizontal SplitBar
34915  * @static
34916  * @type Number
34917  */
34918 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34919
34920 /**
34921  * Placement constant - The resizing element is to the left of the splitter element
34922  * @static
34923  * @type Number
34924  */
34925 Roo.bootstrap.SplitBar.LEFT = 1;
34926
34927 /**
34928  * Placement constant - The resizing element is to the right of the splitter element
34929  * @static
34930  * @type Number
34931  */
34932 Roo.bootstrap.SplitBar.RIGHT = 2;
34933
34934 /**
34935  * Placement constant - The resizing element is positioned above the splitter element
34936  * @static
34937  * @type Number
34938  */
34939 Roo.bootstrap.SplitBar.TOP = 3;
34940
34941 /**
34942  * Placement constant - The resizing element is positioned under splitter element
34943  * @static
34944  * @type Number
34945  */
34946 Roo.bootstrap.SplitBar.BOTTOM = 4;
34947 Roo.namespace("Roo.bootstrap.layout");/*
34948  * Based on:
34949  * Ext JS Library 1.1.1
34950  * Copyright(c) 2006-2007, Ext JS, LLC.
34951  *
34952  * Originally Released Under LGPL - original licence link has changed is not relivant.
34953  *
34954  * Fork - LGPL
34955  * <script type="text/javascript">
34956  */
34957
34958 /**
34959  * @class Roo.bootstrap.layout.Manager
34960  * @extends Roo.bootstrap.Component
34961  * Base class for layout managers.
34962  */
34963 Roo.bootstrap.layout.Manager = function(config)
34964 {
34965     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34966
34967
34968
34969
34970
34971     /** false to disable window resize monitoring @type Boolean */
34972     this.monitorWindowResize = true;
34973     this.regions = {};
34974     this.addEvents({
34975         /**
34976          * @event layout
34977          * Fires when a layout is performed.
34978          * @param {Roo.LayoutManager} this
34979          */
34980         "layout" : true,
34981         /**
34982          * @event regionresized
34983          * Fires when the user resizes a region.
34984          * @param {Roo.LayoutRegion} region The resized region
34985          * @param {Number} newSize The new size (width for east/west, height for north/south)
34986          */
34987         "regionresized" : true,
34988         /**
34989          * @event regioncollapsed
34990          * Fires when a region is collapsed.
34991          * @param {Roo.LayoutRegion} region The collapsed region
34992          */
34993         "regioncollapsed" : true,
34994         /**
34995          * @event regionexpanded
34996          * Fires when a region is expanded.
34997          * @param {Roo.LayoutRegion} region The expanded region
34998          */
34999         "regionexpanded" : true
35000     });
35001     this.updating = false;
35002
35003     if (config.el) {
35004         this.el = Roo.get(config.el);
35005         this.initEvents();
35006     }
35007
35008 };
35009
35010 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
35011
35012
35013     regions : null,
35014
35015     monitorWindowResize : true,
35016
35017
35018     updating : false,
35019
35020
35021     onRender : function(ct, position)
35022     {
35023         if(!this.el){
35024             this.el = Roo.get(ct);
35025             this.initEvents();
35026         }
35027         //this.fireEvent('render',this);
35028     },
35029
35030
35031     initEvents: function()
35032     {
35033
35034
35035         // ie scrollbar fix
35036         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
35037             document.body.scroll = "no";
35038         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
35039             this.el.position('relative');
35040         }
35041         this.id = this.el.id;
35042         this.el.addClass("roo-layout-container");
35043         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
35044         if(this.el.dom != document.body ) {
35045             this.el.on('resize', this.layout,this);
35046             this.el.on('show', this.layout,this);
35047         }
35048
35049     },
35050
35051     /**
35052      * Returns true if this layout is currently being updated
35053      * @return {Boolean}
35054      */
35055     isUpdating : function(){
35056         return this.updating;
35057     },
35058
35059     /**
35060      * Suspend the LayoutManager from doing auto-layouts while
35061      * making multiple add or remove calls
35062      */
35063     beginUpdate : function(){
35064         this.updating = true;
35065     },
35066
35067     /**
35068      * Restore auto-layouts and optionally disable the manager from performing a layout
35069      * @param {Boolean} noLayout true to disable a layout update
35070      */
35071     endUpdate : function(noLayout){
35072         this.updating = false;
35073         if(!noLayout){
35074             this.layout();
35075         }
35076     },
35077
35078     layout: function(){
35079         // abstract...
35080     },
35081
35082     onRegionResized : function(region, newSize){
35083         this.fireEvent("regionresized", region, newSize);
35084         this.layout();
35085     },
35086
35087     onRegionCollapsed : function(region){
35088         this.fireEvent("regioncollapsed", region);
35089     },
35090
35091     onRegionExpanded : function(region){
35092         this.fireEvent("regionexpanded", region);
35093     },
35094
35095     /**
35096      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
35097      * performs box-model adjustments.
35098      * @return {Object} The size as an object {width: (the width), height: (the height)}
35099      */
35100     getViewSize : function()
35101     {
35102         var size;
35103         if(this.el.dom != document.body){
35104             size = this.el.getSize();
35105         }else{
35106             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
35107         }
35108         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
35109         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35110         return size;
35111     },
35112
35113     /**
35114      * Returns the Element this layout is bound to.
35115      * @return {Roo.Element}
35116      */
35117     getEl : function(){
35118         return this.el;
35119     },
35120
35121     /**
35122      * Returns the specified region.
35123      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
35124      * @return {Roo.LayoutRegion}
35125      */
35126     getRegion : function(target){
35127         return this.regions[target.toLowerCase()];
35128     },
35129
35130     onWindowResize : function(){
35131         if(this.monitorWindowResize){
35132             this.layout();
35133         }
35134     }
35135 });
35136 /*
35137  * Based on:
35138  * Ext JS Library 1.1.1
35139  * Copyright(c) 2006-2007, Ext JS, LLC.
35140  *
35141  * Originally Released Under LGPL - original licence link has changed is not relivant.
35142  *
35143  * Fork - LGPL
35144  * <script type="text/javascript">
35145  */
35146 /**
35147  * @class Roo.bootstrap.layout.Border
35148  * @extends Roo.bootstrap.layout.Manager
35149  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
35150  * please see: examples/bootstrap/nested.html<br><br>
35151  
35152 <b>The container the layout is rendered into can be either the body element or any other element.
35153 If it is not the body element, the container needs to either be an absolute positioned element,
35154 or you will need to add "position:relative" to the css of the container.  You will also need to specify
35155 the container size if it is not the body element.</b>
35156
35157 * @constructor
35158 * Create a new Border
35159 * @param {Object} config Configuration options
35160  */
35161 Roo.bootstrap.layout.Border = function(config){
35162     config = config || {};
35163     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
35164     
35165     
35166     
35167     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35168         if(config[region]){
35169             config[region].region = region;
35170             this.addRegion(config[region]);
35171         }
35172     },this);
35173     
35174 };
35175
35176 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
35177
35178 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
35179     
35180     parent : false, // this might point to a 'nest' or a ???
35181     
35182     /**
35183      * Creates and adds a new region if it doesn't already exist.
35184      * @param {String} target The target region key (north, south, east, west or center).
35185      * @param {Object} config The regions config object
35186      * @return {BorderLayoutRegion} The new region
35187      */
35188     addRegion : function(config)
35189     {
35190         if(!this.regions[config.region]){
35191             var r = this.factory(config);
35192             this.bindRegion(r);
35193         }
35194         return this.regions[config.region];
35195     },
35196
35197     // private (kinda)
35198     bindRegion : function(r){
35199         this.regions[r.config.region] = r;
35200         
35201         r.on("visibilitychange",    this.layout, this);
35202         r.on("paneladded",          this.layout, this);
35203         r.on("panelremoved",        this.layout, this);
35204         r.on("invalidated",         this.layout, this);
35205         r.on("resized",             this.onRegionResized, this);
35206         r.on("collapsed",           this.onRegionCollapsed, this);
35207         r.on("expanded",            this.onRegionExpanded, this);
35208     },
35209
35210     /**
35211      * Performs a layout update.
35212      */
35213     layout : function()
35214     {
35215         if(this.updating) {
35216             return;
35217         }
35218         
35219         // render all the rebions if they have not been done alreayd?
35220         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35221             if(this.regions[region] && !this.regions[region].bodyEl){
35222                 this.regions[region].onRender(this.el)
35223             }
35224         },this);
35225         
35226         var size = this.getViewSize();
35227         var w = size.width;
35228         var h = size.height;
35229         var centerW = w;
35230         var centerH = h;
35231         var centerY = 0;
35232         var centerX = 0;
35233         //var x = 0, y = 0;
35234
35235         var rs = this.regions;
35236         var north = rs["north"];
35237         var south = rs["south"]; 
35238         var west = rs["west"];
35239         var east = rs["east"];
35240         var center = rs["center"];
35241         //if(this.hideOnLayout){ // not supported anymore
35242             //c.el.setStyle("display", "none");
35243         //}
35244         if(north && north.isVisible()){
35245             var b = north.getBox();
35246             var m = north.getMargins();
35247             b.width = w - (m.left+m.right);
35248             b.x = m.left;
35249             b.y = m.top;
35250             centerY = b.height + b.y + m.bottom;
35251             centerH -= centerY;
35252             north.updateBox(this.safeBox(b));
35253         }
35254         if(south && south.isVisible()){
35255             var b = south.getBox();
35256             var m = south.getMargins();
35257             b.width = w - (m.left+m.right);
35258             b.x = m.left;
35259             var totalHeight = (b.height + m.top + m.bottom);
35260             b.y = h - totalHeight + m.top;
35261             centerH -= totalHeight;
35262             south.updateBox(this.safeBox(b));
35263         }
35264         if(west && west.isVisible()){
35265             var b = west.getBox();
35266             var m = west.getMargins();
35267             b.height = centerH - (m.top+m.bottom);
35268             b.x = m.left;
35269             b.y = centerY + m.top;
35270             var totalWidth = (b.width + m.left + m.right);
35271             centerX += totalWidth;
35272             centerW -= totalWidth;
35273             west.updateBox(this.safeBox(b));
35274         }
35275         if(east && east.isVisible()){
35276             var b = east.getBox();
35277             var m = east.getMargins();
35278             b.height = centerH - (m.top+m.bottom);
35279             var totalWidth = (b.width + m.left + m.right);
35280             b.x = w - totalWidth + m.left;
35281             b.y = centerY + m.top;
35282             centerW -= totalWidth;
35283             east.updateBox(this.safeBox(b));
35284         }
35285         if(center){
35286             var m = center.getMargins();
35287             var centerBox = {
35288                 x: centerX + m.left,
35289                 y: centerY + m.top,
35290                 width: centerW - (m.left+m.right),
35291                 height: centerH - (m.top+m.bottom)
35292             };
35293             //if(this.hideOnLayout){
35294                 //center.el.setStyle("display", "block");
35295             //}
35296             center.updateBox(this.safeBox(centerBox));
35297         }
35298         this.el.repaint();
35299         this.fireEvent("layout", this);
35300     },
35301
35302     // private
35303     safeBox : function(box){
35304         box.width = Math.max(0, box.width);
35305         box.height = Math.max(0, box.height);
35306         return box;
35307     },
35308
35309     /**
35310      * Adds a ContentPanel (or subclass) to this layout.
35311      * @param {String} target The target region key (north, south, east, west or center).
35312      * @param {Roo.ContentPanel} panel The panel to add
35313      * @return {Roo.ContentPanel} The added panel
35314      */
35315     add : function(target, panel){
35316          
35317         target = target.toLowerCase();
35318         return this.regions[target].add(panel);
35319     },
35320
35321     /**
35322      * Remove a ContentPanel (or subclass) to this layout.
35323      * @param {String} target The target region key (north, south, east, west or center).
35324      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35325      * @return {Roo.ContentPanel} The removed panel
35326      */
35327     remove : function(target, panel){
35328         target = target.toLowerCase();
35329         return this.regions[target].remove(panel);
35330     },
35331
35332     /**
35333      * Searches all regions for a panel with the specified id
35334      * @param {String} panelId
35335      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35336      */
35337     findPanel : function(panelId){
35338         var rs = this.regions;
35339         for(var target in rs){
35340             if(typeof rs[target] != "function"){
35341                 var p = rs[target].getPanel(panelId);
35342                 if(p){
35343                     return p;
35344                 }
35345             }
35346         }
35347         return null;
35348     },
35349
35350     /**
35351      * Searches all regions for a panel with the specified id and activates (shows) it.
35352      * @param {String/ContentPanel} panelId The panels id or the panel itself
35353      * @return {Roo.ContentPanel} The shown panel or null
35354      */
35355     showPanel : function(panelId) {
35356       var rs = this.regions;
35357       for(var target in rs){
35358          var r = rs[target];
35359          if(typeof r != "function"){
35360             if(r.hasPanel(panelId)){
35361                return r.showPanel(panelId);
35362             }
35363          }
35364       }
35365       return null;
35366    },
35367
35368    /**
35369      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35370      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35371      */
35372    /*
35373     restoreState : function(provider){
35374         if(!provider){
35375             provider = Roo.state.Manager;
35376         }
35377         var sm = new Roo.LayoutStateManager();
35378         sm.init(this, provider);
35379     },
35380 */
35381  
35382  
35383     /**
35384      * Adds a xtype elements to the layout.
35385      * <pre><code>
35386
35387 layout.addxtype({
35388        xtype : 'ContentPanel',
35389        region: 'west',
35390        items: [ .... ]
35391    }
35392 );
35393
35394 layout.addxtype({
35395         xtype : 'NestedLayoutPanel',
35396         region: 'west',
35397         layout: {
35398            center: { },
35399            west: { }   
35400         },
35401         items : [ ... list of content panels or nested layout panels.. ]
35402    }
35403 );
35404 </code></pre>
35405      * @param {Object} cfg Xtype definition of item to add.
35406      */
35407     addxtype : function(cfg)
35408     {
35409         // basically accepts a pannel...
35410         // can accept a layout region..!?!?
35411         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35412         
35413         
35414         // theory?  children can only be panels??
35415         
35416         //if (!cfg.xtype.match(/Panel$/)) {
35417         //    return false;
35418         //}
35419         var ret = false;
35420         
35421         if (typeof(cfg.region) == 'undefined') {
35422             Roo.log("Failed to add Panel, region was not set");
35423             Roo.log(cfg);
35424             return false;
35425         }
35426         var region = cfg.region;
35427         delete cfg.region;
35428         
35429           
35430         var xitems = [];
35431         if (cfg.items) {
35432             xitems = cfg.items;
35433             delete cfg.items;
35434         }
35435         var nb = false;
35436         
35437         if ( region == 'center') {
35438             Roo.log("Center: " + cfg.title);
35439         }
35440         
35441         
35442         switch(cfg.xtype) 
35443         {
35444             case 'Content':  // ContentPanel (el, cfg)
35445             case 'Scroll':  // ContentPanel (el, cfg)
35446             case 'View': 
35447                 cfg.autoCreate = cfg.autoCreate || true;
35448                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35449                 //} else {
35450                 //    var el = this.el.createChild();
35451                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35452                 //}
35453                 
35454                 this.add(region, ret);
35455                 break;
35456             
35457             /*
35458             case 'TreePanel': // our new panel!
35459                 cfg.el = this.el.createChild();
35460                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35461                 this.add(region, ret);
35462                 break;
35463             */
35464             
35465             case 'Nest': 
35466                 // create a new Layout (which is  a Border Layout...
35467                 
35468                 var clayout = cfg.layout;
35469                 clayout.el  = this.el.createChild();
35470                 clayout.items   = clayout.items  || [];
35471                 
35472                 delete cfg.layout;
35473                 
35474                 // replace this exitems with the clayout ones..
35475                 xitems = clayout.items;
35476                  
35477                 // force background off if it's in center...
35478                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35479                     cfg.background = false;
35480                 }
35481                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35482                 
35483                 
35484                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35485                 //console.log('adding nested layout panel '  + cfg.toSource());
35486                 this.add(region, ret);
35487                 nb = {}; /// find first...
35488                 break;
35489             
35490             case 'Grid':
35491                 
35492                 // needs grid and region
35493                 
35494                 //var el = this.getRegion(region).el.createChild();
35495                 /*
35496                  *var el = this.el.createChild();
35497                 // create the grid first...
35498                 cfg.grid.container = el;
35499                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35500                 */
35501                 
35502                 if (region == 'center' && this.active ) {
35503                     cfg.background = false;
35504                 }
35505                 
35506                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35507                 
35508                 this.add(region, ret);
35509                 /*
35510                 if (cfg.background) {
35511                     // render grid on panel activation (if panel background)
35512                     ret.on('activate', function(gp) {
35513                         if (!gp.grid.rendered) {
35514                     //        gp.grid.render(el);
35515                         }
35516                     });
35517                 } else {
35518                   //  cfg.grid.render(el);
35519                 }
35520                 */
35521                 break;
35522            
35523            
35524             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35525                 // it was the old xcomponent building that caused this before.
35526                 // espeically if border is the top element in the tree.
35527                 ret = this;
35528                 break; 
35529                 
35530                     
35531                 
35532                 
35533                 
35534             default:
35535                 /*
35536                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35537                     
35538                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35539                     this.add(region, ret);
35540                 } else {
35541                 */
35542                     Roo.log(cfg);
35543                     throw "Can not add '" + cfg.xtype + "' to Border";
35544                     return null;
35545              
35546                                 
35547              
35548         }
35549         this.beginUpdate();
35550         // add children..
35551         var region = '';
35552         var abn = {};
35553         Roo.each(xitems, function(i)  {
35554             region = nb && i.region ? i.region : false;
35555             
35556             var add = ret.addxtype(i);
35557            
35558             if (region) {
35559                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35560                 if (!i.background) {
35561                     abn[region] = nb[region] ;
35562                 }
35563             }
35564             
35565         });
35566         this.endUpdate();
35567
35568         // make the last non-background panel active..
35569         //if (nb) { Roo.log(abn); }
35570         if (nb) {
35571             
35572             for(var r in abn) {
35573                 region = this.getRegion(r);
35574                 if (region) {
35575                     // tried using nb[r], but it does not work..
35576                      
35577                     region.showPanel(abn[r]);
35578                    
35579                 }
35580             }
35581         }
35582         return ret;
35583         
35584     },
35585     
35586     
35587 // private
35588     factory : function(cfg)
35589     {
35590         
35591         var validRegions = Roo.bootstrap.layout.Border.regions;
35592
35593         var target = cfg.region;
35594         cfg.mgr = this;
35595         
35596         var r = Roo.bootstrap.layout;
35597         Roo.log(target);
35598         switch(target){
35599             case "north":
35600                 return new r.North(cfg);
35601             case "south":
35602                 return new r.South(cfg);
35603             case "east":
35604                 return new r.East(cfg);
35605             case "west":
35606                 return new r.West(cfg);
35607             case "center":
35608                 return new r.Center(cfg);
35609         }
35610         throw 'Layout region "'+target+'" not supported.';
35611     }
35612     
35613     
35614 });
35615  /*
35616  * Based on:
35617  * Ext JS Library 1.1.1
35618  * Copyright(c) 2006-2007, Ext JS, LLC.
35619  *
35620  * Originally Released Under LGPL - original licence link has changed is not relivant.
35621  *
35622  * Fork - LGPL
35623  * <script type="text/javascript">
35624  */
35625  
35626 /**
35627  * @class Roo.bootstrap.layout.Basic
35628  * @extends Roo.util.Observable
35629  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35630  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35631  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35632  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35633  * @cfg {string}   region  the region that it inhabits..
35634  * @cfg {bool}   skipConfig skip config?
35635  * 
35636
35637  */
35638 Roo.bootstrap.layout.Basic = function(config){
35639     
35640     this.mgr = config.mgr;
35641     
35642     this.position = config.region;
35643     
35644     var skipConfig = config.skipConfig;
35645     
35646     this.events = {
35647         /**
35648          * @scope Roo.BasicLayoutRegion
35649          */
35650         
35651         /**
35652          * @event beforeremove
35653          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35654          * @param {Roo.LayoutRegion} this
35655          * @param {Roo.ContentPanel} panel The panel
35656          * @param {Object} e The cancel event object
35657          */
35658         "beforeremove" : true,
35659         /**
35660          * @event invalidated
35661          * Fires when the layout for this region is changed.
35662          * @param {Roo.LayoutRegion} this
35663          */
35664         "invalidated" : true,
35665         /**
35666          * @event visibilitychange
35667          * Fires when this region is shown or hidden 
35668          * @param {Roo.LayoutRegion} this
35669          * @param {Boolean} visibility true or false
35670          */
35671         "visibilitychange" : true,
35672         /**
35673          * @event paneladded
35674          * Fires when a panel is added. 
35675          * @param {Roo.LayoutRegion} this
35676          * @param {Roo.ContentPanel} panel The panel
35677          */
35678         "paneladded" : true,
35679         /**
35680          * @event panelremoved
35681          * Fires when a panel is removed. 
35682          * @param {Roo.LayoutRegion} this
35683          * @param {Roo.ContentPanel} panel The panel
35684          */
35685         "panelremoved" : true,
35686         /**
35687          * @event beforecollapse
35688          * Fires when this region before collapse.
35689          * @param {Roo.LayoutRegion} this
35690          */
35691         "beforecollapse" : true,
35692         /**
35693          * @event collapsed
35694          * Fires when this region is collapsed.
35695          * @param {Roo.LayoutRegion} this
35696          */
35697         "collapsed" : true,
35698         /**
35699          * @event expanded
35700          * Fires when this region is expanded.
35701          * @param {Roo.LayoutRegion} this
35702          */
35703         "expanded" : true,
35704         /**
35705          * @event slideshow
35706          * Fires when this region is slid into view.
35707          * @param {Roo.LayoutRegion} this
35708          */
35709         "slideshow" : true,
35710         /**
35711          * @event slidehide
35712          * Fires when this region slides out of view. 
35713          * @param {Roo.LayoutRegion} this
35714          */
35715         "slidehide" : true,
35716         /**
35717          * @event panelactivated
35718          * Fires when a panel is activated. 
35719          * @param {Roo.LayoutRegion} this
35720          * @param {Roo.ContentPanel} panel The activated panel
35721          */
35722         "panelactivated" : true,
35723         /**
35724          * @event resized
35725          * Fires when the user resizes this region. 
35726          * @param {Roo.LayoutRegion} this
35727          * @param {Number} newSize The new size (width for east/west, height for north/south)
35728          */
35729         "resized" : true
35730     };
35731     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35732     this.panels = new Roo.util.MixedCollection();
35733     this.panels.getKey = this.getPanelId.createDelegate(this);
35734     this.box = null;
35735     this.activePanel = null;
35736     // ensure listeners are added...
35737     
35738     if (config.listeners || config.events) {
35739         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35740             listeners : config.listeners || {},
35741             events : config.events || {}
35742         });
35743     }
35744     
35745     if(skipConfig !== true){
35746         this.applyConfig(config);
35747     }
35748 };
35749
35750 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35751 {
35752     getPanelId : function(p){
35753         return p.getId();
35754     },
35755     
35756     applyConfig : function(config){
35757         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35758         this.config = config;
35759         
35760     },
35761     
35762     /**
35763      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35764      * the width, for horizontal (north, south) the height.
35765      * @param {Number} newSize The new width or height
35766      */
35767     resizeTo : function(newSize){
35768         var el = this.el ? this.el :
35769                  (this.activePanel ? this.activePanel.getEl() : null);
35770         if(el){
35771             switch(this.position){
35772                 case "east":
35773                 case "west":
35774                     el.setWidth(newSize);
35775                     this.fireEvent("resized", this, newSize);
35776                 break;
35777                 case "north":
35778                 case "south":
35779                     el.setHeight(newSize);
35780                     this.fireEvent("resized", this, newSize);
35781                 break;                
35782             }
35783         }
35784     },
35785     
35786     getBox : function(){
35787         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35788     },
35789     
35790     getMargins : function(){
35791         return this.margins;
35792     },
35793     
35794     updateBox : function(box){
35795         this.box = box;
35796         var el = this.activePanel.getEl();
35797         el.dom.style.left = box.x + "px";
35798         el.dom.style.top = box.y + "px";
35799         this.activePanel.setSize(box.width, box.height);
35800     },
35801     
35802     /**
35803      * Returns the container element for this region.
35804      * @return {Roo.Element}
35805      */
35806     getEl : function(){
35807         return this.activePanel;
35808     },
35809     
35810     /**
35811      * Returns true if this region is currently visible.
35812      * @return {Boolean}
35813      */
35814     isVisible : function(){
35815         return this.activePanel ? true : false;
35816     },
35817     
35818     setActivePanel : function(panel){
35819         panel = this.getPanel(panel);
35820         if(this.activePanel && this.activePanel != panel){
35821             this.activePanel.setActiveState(false);
35822             this.activePanel.getEl().setLeftTop(-10000,-10000);
35823         }
35824         this.activePanel = panel;
35825         panel.setActiveState(true);
35826         if(this.box){
35827             panel.setSize(this.box.width, this.box.height);
35828         }
35829         this.fireEvent("panelactivated", this, panel);
35830         this.fireEvent("invalidated");
35831     },
35832     
35833     /**
35834      * Show the specified panel.
35835      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35836      * @return {Roo.ContentPanel} The shown panel or null
35837      */
35838     showPanel : function(panel){
35839         panel = this.getPanel(panel);
35840         if(panel){
35841             this.setActivePanel(panel);
35842         }
35843         return panel;
35844     },
35845     
35846     /**
35847      * Get the active panel for this region.
35848      * @return {Roo.ContentPanel} The active panel or null
35849      */
35850     getActivePanel : function(){
35851         return this.activePanel;
35852     },
35853     
35854     /**
35855      * Add the passed ContentPanel(s)
35856      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35857      * @return {Roo.ContentPanel} The panel added (if only one was added)
35858      */
35859     add : function(panel){
35860         if(arguments.length > 1){
35861             for(var i = 0, len = arguments.length; i < len; i++) {
35862                 this.add(arguments[i]);
35863             }
35864             return null;
35865         }
35866         if(this.hasPanel(panel)){
35867             this.showPanel(panel);
35868             return panel;
35869         }
35870         var el = panel.getEl();
35871         if(el.dom.parentNode != this.mgr.el.dom){
35872             this.mgr.el.dom.appendChild(el.dom);
35873         }
35874         if(panel.setRegion){
35875             panel.setRegion(this);
35876         }
35877         this.panels.add(panel);
35878         el.setStyle("position", "absolute");
35879         if(!panel.background){
35880             this.setActivePanel(panel);
35881             if(this.config.initialSize && this.panels.getCount()==1){
35882                 this.resizeTo(this.config.initialSize);
35883             }
35884         }
35885         this.fireEvent("paneladded", this, panel);
35886         return panel;
35887     },
35888     
35889     /**
35890      * Returns true if the panel is in this region.
35891      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35892      * @return {Boolean}
35893      */
35894     hasPanel : function(panel){
35895         if(typeof panel == "object"){ // must be panel obj
35896             panel = panel.getId();
35897         }
35898         return this.getPanel(panel) ? true : false;
35899     },
35900     
35901     /**
35902      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35903      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35904      * @param {Boolean} preservePanel Overrides the config preservePanel option
35905      * @return {Roo.ContentPanel} The panel that was removed
35906      */
35907     remove : function(panel, preservePanel){
35908         panel = this.getPanel(panel);
35909         if(!panel){
35910             return null;
35911         }
35912         var e = {};
35913         this.fireEvent("beforeremove", this, panel, e);
35914         if(e.cancel === true){
35915             return null;
35916         }
35917         var panelId = panel.getId();
35918         this.panels.removeKey(panelId);
35919         return panel;
35920     },
35921     
35922     /**
35923      * Returns the panel specified or null if it's not in this region.
35924      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35925      * @return {Roo.ContentPanel}
35926      */
35927     getPanel : function(id){
35928         if(typeof id == "object"){ // must be panel obj
35929             return id;
35930         }
35931         return this.panels.get(id);
35932     },
35933     
35934     /**
35935      * Returns this regions position (north/south/east/west/center).
35936      * @return {String} 
35937      */
35938     getPosition: function(){
35939         return this.position;    
35940     }
35941 });/*
35942  * Based on:
35943  * Ext JS Library 1.1.1
35944  * Copyright(c) 2006-2007, Ext JS, LLC.
35945  *
35946  * Originally Released Under LGPL - original licence link has changed is not relivant.
35947  *
35948  * Fork - LGPL
35949  * <script type="text/javascript">
35950  */
35951  
35952 /**
35953  * @class Roo.bootstrap.layout.Region
35954  * @extends Roo.bootstrap.layout.Basic
35955  * This class represents a region in a layout manager.
35956  
35957  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35958  * @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})
35959  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35960  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35961  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35962  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35963  * @cfg {String}    title           The title for the region (overrides panel titles)
35964  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35965  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35966  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35967  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35968  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35969  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35970  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35971  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35972  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35973  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35974
35975  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35976  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35977  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35978  * @cfg {Number}    width           For East/West panels
35979  * @cfg {Number}    height          For North/South panels
35980  * @cfg {Boolean}   split           To show the splitter
35981  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35982  * 
35983  * @cfg {string}   cls             Extra CSS classes to add to region
35984  * 
35985  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35986  * @cfg {string}   region  the region that it inhabits..
35987  *
35988
35989  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35990  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35991
35992  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35993  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35994  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35995  */
35996 Roo.bootstrap.layout.Region = function(config)
35997 {
35998     this.applyConfig(config);
35999
36000     var mgr = config.mgr;
36001     var pos = config.region;
36002     config.skipConfig = true;
36003     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
36004     
36005     if (mgr.el) {
36006         this.onRender(mgr.el);   
36007     }
36008      
36009     this.visible = true;
36010     this.collapsed = false;
36011     this.unrendered_panels = [];
36012 };
36013
36014 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
36015
36016     position: '', // set by wrapper (eg. north/south etc..)
36017     unrendered_panels : null,  // unrendered panels.
36018     
36019     tabPosition : false,
36020     
36021     mgr: false, // points to 'Border'
36022     
36023     
36024     createBody : function(){
36025         /** This region's body element 
36026         * @type Roo.Element */
36027         this.bodyEl = this.el.createChild({
36028                 tag: "div",
36029                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
36030         });
36031     },
36032
36033     onRender: function(ctr, pos)
36034     {
36035         var dh = Roo.DomHelper;
36036         /** This region's container element 
36037         * @type Roo.Element */
36038         this.el = dh.append(ctr.dom, {
36039                 tag: "div",
36040                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
36041             }, true);
36042         /** This region's title element 
36043         * @type Roo.Element */
36044     
36045         this.titleEl = dh.append(this.el.dom,  {
36046                 tag: "div",
36047                 unselectable: "on",
36048                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
36049                 children:[
36050                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
36051                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
36052                 ]
36053             }, true);
36054         
36055         this.titleEl.enableDisplayMode();
36056         /** This region's title text element 
36057         * @type HTMLElement */
36058         this.titleTextEl = this.titleEl.dom.firstChild;
36059         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
36060         /*
36061         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
36062         this.closeBtn.enableDisplayMode();
36063         this.closeBtn.on("click", this.closeClicked, this);
36064         this.closeBtn.hide();
36065     */
36066         this.createBody(this.config);
36067         if(this.config.hideWhenEmpty){
36068             this.hide();
36069             this.on("paneladded", this.validateVisibility, this);
36070             this.on("panelremoved", this.validateVisibility, this);
36071         }
36072         if(this.autoScroll){
36073             this.bodyEl.setStyle("overflow", "auto");
36074         }else{
36075             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
36076         }
36077         //if(c.titlebar !== false){
36078             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
36079                 this.titleEl.hide();
36080             }else{
36081                 this.titleEl.show();
36082                 if(this.config.title){
36083                     this.titleTextEl.innerHTML = this.config.title;
36084                 }
36085             }
36086         //}
36087         if(this.config.collapsed){
36088             this.collapse(true);
36089         }
36090         if(this.config.hidden){
36091             this.hide();
36092         }
36093         
36094         if (this.unrendered_panels && this.unrendered_panels.length) {
36095             for (var i =0;i< this.unrendered_panels.length; i++) {
36096                 this.add(this.unrendered_panels[i]);
36097             }
36098             this.unrendered_panels = null;
36099             
36100         }
36101         
36102     },
36103     
36104     applyConfig : function(c)
36105     {
36106         /*
36107          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
36108             var dh = Roo.DomHelper;
36109             if(c.titlebar !== false){
36110                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
36111                 this.collapseBtn.on("click", this.collapse, this);
36112                 this.collapseBtn.enableDisplayMode();
36113                 /*
36114                 if(c.showPin === true || this.showPin){
36115                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
36116                     this.stickBtn.enableDisplayMode();
36117                     this.stickBtn.on("click", this.expand, this);
36118                     this.stickBtn.hide();
36119                 }
36120                 
36121             }
36122             */
36123             /** This region's collapsed element
36124             * @type Roo.Element */
36125             /*
36126              *
36127             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
36128                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
36129             ]}, true);
36130             
36131             if(c.floatable !== false){
36132                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
36133                this.collapsedEl.on("click", this.collapseClick, this);
36134             }
36135
36136             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
36137                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
36138                    id: "message", unselectable: "on", style:{"float":"left"}});
36139                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
36140              }
36141             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
36142             this.expandBtn.on("click", this.expand, this);
36143             
36144         }
36145         
36146         if(this.collapseBtn){
36147             this.collapseBtn.setVisible(c.collapsible == true);
36148         }
36149         
36150         this.cmargins = c.cmargins || this.cmargins ||
36151                          (this.position == "west" || this.position == "east" ?
36152                              {top: 0, left: 2, right:2, bottom: 0} :
36153                              {top: 2, left: 0, right:0, bottom: 2});
36154         */
36155         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36156         
36157         
36158         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
36159         
36160         this.autoScroll = c.autoScroll || false;
36161         
36162         
36163        
36164         
36165         this.duration = c.duration || .30;
36166         this.slideDuration = c.slideDuration || .45;
36167         this.config = c;
36168        
36169     },
36170     /**
36171      * Returns true if this region is currently visible.
36172      * @return {Boolean}
36173      */
36174     isVisible : function(){
36175         return this.visible;
36176     },
36177
36178     /**
36179      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
36180      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
36181      */
36182     //setCollapsedTitle : function(title){
36183     //    title = title || "&#160;";
36184      //   if(this.collapsedTitleTextEl){
36185       //      this.collapsedTitleTextEl.innerHTML = title;
36186        // }
36187     //},
36188
36189     getBox : function(){
36190         var b;
36191       //  if(!this.collapsed){
36192             b = this.el.getBox(false, true);
36193        // }else{
36194           //  b = this.collapsedEl.getBox(false, true);
36195         //}
36196         return b;
36197     },
36198
36199     getMargins : function(){
36200         return this.margins;
36201         //return this.collapsed ? this.cmargins : this.margins;
36202     },
36203 /*
36204     highlight : function(){
36205         this.el.addClass("x-layout-panel-dragover");
36206     },
36207
36208     unhighlight : function(){
36209         this.el.removeClass("x-layout-panel-dragover");
36210     },
36211 */
36212     updateBox : function(box)
36213     {
36214         if (!this.bodyEl) {
36215             return; // not rendered yet..
36216         }
36217         
36218         this.box = box;
36219         if(!this.collapsed){
36220             this.el.dom.style.left = box.x + "px";
36221             this.el.dom.style.top = box.y + "px";
36222             this.updateBody(box.width, box.height);
36223         }else{
36224             this.collapsedEl.dom.style.left = box.x + "px";
36225             this.collapsedEl.dom.style.top = box.y + "px";
36226             this.collapsedEl.setSize(box.width, box.height);
36227         }
36228         if(this.tabs){
36229             this.tabs.autoSizeTabs();
36230         }
36231     },
36232
36233     updateBody : function(w, h)
36234     {
36235         if(w !== null){
36236             this.el.setWidth(w);
36237             w -= this.el.getBorderWidth("rl");
36238             if(this.config.adjustments){
36239                 w += this.config.adjustments[0];
36240             }
36241         }
36242         if(h !== null && h > 0){
36243             this.el.setHeight(h);
36244             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36245             h -= this.el.getBorderWidth("tb");
36246             if(this.config.adjustments){
36247                 h += this.config.adjustments[1];
36248             }
36249             this.bodyEl.setHeight(h);
36250             if(this.tabs){
36251                 h = this.tabs.syncHeight(h);
36252             }
36253         }
36254         if(this.panelSize){
36255             w = w !== null ? w : this.panelSize.width;
36256             h = h !== null ? h : this.panelSize.height;
36257         }
36258         if(this.activePanel){
36259             var el = this.activePanel.getEl();
36260             w = w !== null ? w : el.getWidth();
36261             h = h !== null ? h : el.getHeight();
36262             this.panelSize = {width: w, height: h};
36263             this.activePanel.setSize(w, h);
36264         }
36265         if(Roo.isIE && this.tabs){
36266             this.tabs.el.repaint();
36267         }
36268     },
36269
36270     /**
36271      * Returns the container element for this region.
36272      * @return {Roo.Element}
36273      */
36274     getEl : function(){
36275         return this.el;
36276     },
36277
36278     /**
36279      * Hides this region.
36280      */
36281     hide : function(){
36282         //if(!this.collapsed){
36283             this.el.dom.style.left = "-2000px";
36284             this.el.hide();
36285         //}else{
36286          //   this.collapsedEl.dom.style.left = "-2000px";
36287          //   this.collapsedEl.hide();
36288        // }
36289         this.visible = false;
36290         this.fireEvent("visibilitychange", this, false);
36291     },
36292
36293     /**
36294      * Shows this region if it was previously hidden.
36295      */
36296     show : function(){
36297         //if(!this.collapsed){
36298             this.el.show();
36299         //}else{
36300         //    this.collapsedEl.show();
36301        // }
36302         this.visible = true;
36303         this.fireEvent("visibilitychange", this, true);
36304     },
36305 /*
36306     closeClicked : function(){
36307         if(this.activePanel){
36308             this.remove(this.activePanel);
36309         }
36310     },
36311
36312     collapseClick : function(e){
36313         if(this.isSlid){
36314            e.stopPropagation();
36315            this.slideIn();
36316         }else{
36317            e.stopPropagation();
36318            this.slideOut();
36319         }
36320     },
36321 */
36322     /**
36323      * Collapses this region.
36324      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36325      */
36326     /*
36327     collapse : function(skipAnim, skipCheck = false){
36328         if(this.collapsed) {
36329             return;
36330         }
36331         
36332         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36333             
36334             this.collapsed = true;
36335             if(this.split){
36336                 this.split.el.hide();
36337             }
36338             if(this.config.animate && skipAnim !== true){
36339                 this.fireEvent("invalidated", this);
36340                 this.animateCollapse();
36341             }else{
36342                 this.el.setLocation(-20000,-20000);
36343                 this.el.hide();
36344                 this.collapsedEl.show();
36345                 this.fireEvent("collapsed", this);
36346                 this.fireEvent("invalidated", this);
36347             }
36348         }
36349         
36350     },
36351 */
36352     animateCollapse : function(){
36353         // overridden
36354     },
36355
36356     /**
36357      * Expands this region if it was previously collapsed.
36358      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36359      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36360      */
36361     /*
36362     expand : function(e, skipAnim){
36363         if(e) {
36364             e.stopPropagation();
36365         }
36366         if(!this.collapsed || this.el.hasActiveFx()) {
36367             return;
36368         }
36369         if(this.isSlid){
36370             this.afterSlideIn();
36371             skipAnim = true;
36372         }
36373         this.collapsed = false;
36374         if(this.config.animate && skipAnim !== true){
36375             this.animateExpand();
36376         }else{
36377             this.el.show();
36378             if(this.split){
36379                 this.split.el.show();
36380             }
36381             this.collapsedEl.setLocation(-2000,-2000);
36382             this.collapsedEl.hide();
36383             this.fireEvent("invalidated", this);
36384             this.fireEvent("expanded", this);
36385         }
36386     },
36387 */
36388     animateExpand : function(){
36389         // overridden
36390     },
36391
36392     initTabs : function()
36393     {
36394         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36395         
36396         var ts = new Roo.bootstrap.panel.Tabs({
36397             el: this.bodyEl.dom,
36398             region : this,
36399             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
36400             disableTooltips: this.config.disableTabTips,
36401             toolbar : this.config.toolbar
36402         });
36403         
36404         if(this.config.hideTabs){
36405             ts.stripWrap.setDisplayed(false);
36406         }
36407         this.tabs = ts;
36408         ts.resizeTabs = this.config.resizeTabs === true;
36409         ts.minTabWidth = this.config.minTabWidth || 40;
36410         ts.maxTabWidth = this.config.maxTabWidth || 250;
36411         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36412         ts.monitorResize = false;
36413         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36414         ts.bodyEl.addClass('roo-layout-tabs-body');
36415         this.panels.each(this.initPanelAsTab, this);
36416     },
36417
36418     initPanelAsTab : function(panel){
36419         var ti = this.tabs.addTab(
36420             panel.getEl().id,
36421             panel.getTitle(),
36422             null,
36423             this.config.closeOnTab && panel.isClosable(),
36424             panel.tpl
36425         );
36426         if(panel.tabTip !== undefined){
36427             ti.setTooltip(panel.tabTip);
36428         }
36429         ti.on("activate", function(){
36430               this.setActivePanel(panel);
36431         }, this);
36432         
36433         if(this.config.closeOnTab){
36434             ti.on("beforeclose", function(t, e){
36435                 e.cancel = true;
36436                 this.remove(panel);
36437             }, this);
36438         }
36439         
36440         panel.tabItem = ti;
36441         
36442         return ti;
36443     },
36444
36445     updatePanelTitle : function(panel, title)
36446     {
36447         if(this.activePanel == panel){
36448             this.updateTitle(title);
36449         }
36450         if(this.tabs){
36451             var ti = this.tabs.getTab(panel.getEl().id);
36452             ti.setText(title);
36453             if(panel.tabTip !== undefined){
36454                 ti.setTooltip(panel.tabTip);
36455             }
36456         }
36457     },
36458
36459     updateTitle : function(title){
36460         if(this.titleTextEl && !this.config.title){
36461             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36462         }
36463     },
36464
36465     setActivePanel : function(panel)
36466     {
36467         panel = this.getPanel(panel);
36468         if(this.activePanel && this.activePanel != panel){
36469             if(this.activePanel.setActiveState(false) === false){
36470                 return;
36471             }
36472         }
36473         this.activePanel = panel;
36474         panel.setActiveState(true);
36475         if(this.panelSize){
36476             panel.setSize(this.panelSize.width, this.panelSize.height);
36477         }
36478         if(this.closeBtn){
36479             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36480         }
36481         this.updateTitle(panel.getTitle());
36482         if(this.tabs){
36483             this.fireEvent("invalidated", this);
36484         }
36485         this.fireEvent("panelactivated", this, panel);
36486     },
36487
36488     /**
36489      * Shows the specified panel.
36490      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36491      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36492      */
36493     showPanel : function(panel)
36494     {
36495         panel = this.getPanel(panel);
36496         if(panel){
36497             if(this.tabs){
36498                 var tab = this.tabs.getTab(panel.getEl().id);
36499                 if(tab.isHidden()){
36500                     this.tabs.unhideTab(tab.id);
36501                 }
36502                 tab.activate();
36503             }else{
36504                 this.setActivePanel(panel);
36505             }
36506         }
36507         return panel;
36508     },
36509
36510     /**
36511      * Get the active panel for this region.
36512      * @return {Roo.ContentPanel} The active panel or null
36513      */
36514     getActivePanel : function(){
36515         return this.activePanel;
36516     },
36517
36518     validateVisibility : function(){
36519         if(this.panels.getCount() < 1){
36520             this.updateTitle("&#160;");
36521             this.closeBtn.hide();
36522             this.hide();
36523         }else{
36524             if(!this.isVisible()){
36525                 this.show();
36526             }
36527         }
36528     },
36529
36530     /**
36531      * Adds the passed ContentPanel(s) to this region.
36532      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36533      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36534      */
36535     add : function(panel)
36536     {
36537         if(arguments.length > 1){
36538             for(var i = 0, len = arguments.length; i < len; i++) {
36539                 this.add(arguments[i]);
36540             }
36541             return null;
36542         }
36543         
36544         // if we have not been rendered yet, then we can not really do much of this..
36545         if (!this.bodyEl) {
36546             this.unrendered_panels.push(panel);
36547             return panel;
36548         }
36549         
36550         
36551         
36552         
36553         if(this.hasPanel(panel)){
36554             this.showPanel(panel);
36555             return panel;
36556         }
36557         panel.setRegion(this);
36558         this.panels.add(panel);
36559        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36560             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36561             // and hide them... ???
36562             this.bodyEl.dom.appendChild(panel.getEl().dom);
36563             if(panel.background !== true){
36564                 this.setActivePanel(panel);
36565             }
36566             this.fireEvent("paneladded", this, panel);
36567             return panel;
36568         }
36569         */
36570         if(!this.tabs){
36571             this.initTabs();
36572         }else{
36573             this.initPanelAsTab(panel);
36574         }
36575         
36576         
36577         if(panel.background !== true){
36578             this.tabs.activate(panel.getEl().id);
36579         }
36580         this.fireEvent("paneladded", this, panel);
36581         return panel;
36582     },
36583
36584     /**
36585      * Hides the tab for the specified panel.
36586      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36587      */
36588     hidePanel : function(panel){
36589         if(this.tabs && (panel = this.getPanel(panel))){
36590             this.tabs.hideTab(panel.getEl().id);
36591         }
36592     },
36593
36594     /**
36595      * Unhides the tab for a previously hidden panel.
36596      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36597      */
36598     unhidePanel : function(panel){
36599         if(this.tabs && (panel = this.getPanel(panel))){
36600             this.tabs.unhideTab(panel.getEl().id);
36601         }
36602     },
36603
36604     clearPanels : function(){
36605         while(this.panels.getCount() > 0){
36606              this.remove(this.panels.first());
36607         }
36608     },
36609
36610     /**
36611      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36612      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36613      * @param {Boolean} preservePanel Overrides the config preservePanel option
36614      * @return {Roo.ContentPanel} The panel that was removed
36615      */
36616     remove : function(panel, preservePanel)
36617     {
36618         panel = this.getPanel(panel);
36619         if(!panel){
36620             return null;
36621         }
36622         var e = {};
36623         this.fireEvent("beforeremove", this, panel, e);
36624         if(e.cancel === true){
36625             return null;
36626         }
36627         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36628         var panelId = panel.getId();
36629         this.panels.removeKey(panelId);
36630         if(preservePanel){
36631             document.body.appendChild(panel.getEl().dom);
36632         }
36633         if(this.tabs){
36634             this.tabs.removeTab(panel.getEl().id);
36635         }else if (!preservePanel){
36636             this.bodyEl.dom.removeChild(panel.getEl().dom);
36637         }
36638         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36639             var p = this.panels.first();
36640             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36641             tempEl.appendChild(p.getEl().dom);
36642             this.bodyEl.update("");
36643             this.bodyEl.dom.appendChild(p.getEl().dom);
36644             tempEl = null;
36645             this.updateTitle(p.getTitle());
36646             this.tabs = null;
36647             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36648             this.setActivePanel(p);
36649         }
36650         panel.setRegion(null);
36651         if(this.activePanel == panel){
36652             this.activePanel = null;
36653         }
36654         if(this.config.autoDestroy !== false && preservePanel !== true){
36655             try{panel.destroy();}catch(e){}
36656         }
36657         this.fireEvent("panelremoved", this, panel);
36658         return panel;
36659     },
36660
36661     /**
36662      * Returns the TabPanel component used by this region
36663      * @return {Roo.TabPanel}
36664      */
36665     getTabs : function(){
36666         return this.tabs;
36667     },
36668
36669     createTool : function(parentEl, className){
36670         var btn = Roo.DomHelper.append(parentEl, {
36671             tag: "div",
36672             cls: "x-layout-tools-button",
36673             children: [ {
36674                 tag: "div",
36675                 cls: "roo-layout-tools-button-inner " + className,
36676                 html: "&#160;"
36677             }]
36678         }, true);
36679         btn.addClassOnOver("roo-layout-tools-button-over");
36680         return btn;
36681     }
36682 });/*
36683  * Based on:
36684  * Ext JS Library 1.1.1
36685  * Copyright(c) 2006-2007, Ext JS, LLC.
36686  *
36687  * Originally Released Under LGPL - original licence link has changed is not relivant.
36688  *
36689  * Fork - LGPL
36690  * <script type="text/javascript">
36691  */
36692  
36693
36694
36695 /**
36696  * @class Roo.SplitLayoutRegion
36697  * @extends Roo.LayoutRegion
36698  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36699  */
36700 Roo.bootstrap.layout.Split = function(config){
36701     this.cursor = config.cursor;
36702     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36703 };
36704
36705 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36706 {
36707     splitTip : "Drag to resize.",
36708     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36709     useSplitTips : false,
36710
36711     applyConfig : function(config){
36712         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36713     },
36714     
36715     onRender : function(ctr,pos) {
36716         
36717         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36718         if(!this.config.split){
36719             return;
36720         }
36721         if(!this.split){
36722             
36723             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36724                             tag: "div",
36725                             id: this.el.id + "-split",
36726                             cls: "roo-layout-split roo-layout-split-"+this.position,
36727                             html: "&#160;"
36728             });
36729             /** The SplitBar for this region 
36730             * @type Roo.SplitBar */
36731             // does not exist yet...
36732             Roo.log([this.position, this.orientation]);
36733             
36734             this.split = new Roo.bootstrap.SplitBar({
36735                 dragElement : splitEl,
36736                 resizingElement: this.el,
36737                 orientation : this.orientation
36738             });
36739             
36740             this.split.on("moved", this.onSplitMove, this);
36741             this.split.useShim = this.config.useShim === true;
36742             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36743             if(this.useSplitTips){
36744                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36745             }
36746             //if(config.collapsible){
36747             //    this.split.el.on("dblclick", this.collapse,  this);
36748             //}
36749         }
36750         if(typeof this.config.minSize != "undefined"){
36751             this.split.minSize = this.config.minSize;
36752         }
36753         if(typeof this.config.maxSize != "undefined"){
36754             this.split.maxSize = this.config.maxSize;
36755         }
36756         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36757             this.hideSplitter();
36758         }
36759         
36760     },
36761
36762     getHMaxSize : function(){
36763          var cmax = this.config.maxSize || 10000;
36764          var center = this.mgr.getRegion("center");
36765          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36766     },
36767
36768     getVMaxSize : function(){
36769          var cmax = this.config.maxSize || 10000;
36770          var center = this.mgr.getRegion("center");
36771          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36772     },
36773
36774     onSplitMove : function(split, newSize){
36775         this.fireEvent("resized", this, newSize);
36776     },
36777     
36778     /** 
36779      * Returns the {@link Roo.SplitBar} for this region.
36780      * @return {Roo.SplitBar}
36781      */
36782     getSplitBar : function(){
36783         return this.split;
36784     },
36785     
36786     hide : function(){
36787         this.hideSplitter();
36788         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36789     },
36790
36791     hideSplitter : function(){
36792         if(this.split){
36793             this.split.el.setLocation(-2000,-2000);
36794             this.split.el.hide();
36795         }
36796     },
36797
36798     show : function(){
36799         if(this.split){
36800             this.split.el.show();
36801         }
36802         Roo.bootstrap.layout.Split.superclass.show.call(this);
36803     },
36804     
36805     beforeSlide: function(){
36806         if(Roo.isGecko){// firefox overflow auto bug workaround
36807             this.bodyEl.clip();
36808             if(this.tabs) {
36809                 this.tabs.bodyEl.clip();
36810             }
36811             if(this.activePanel){
36812                 this.activePanel.getEl().clip();
36813                 
36814                 if(this.activePanel.beforeSlide){
36815                     this.activePanel.beforeSlide();
36816                 }
36817             }
36818         }
36819     },
36820     
36821     afterSlide : function(){
36822         if(Roo.isGecko){// firefox overflow auto bug workaround
36823             this.bodyEl.unclip();
36824             if(this.tabs) {
36825                 this.tabs.bodyEl.unclip();
36826             }
36827             if(this.activePanel){
36828                 this.activePanel.getEl().unclip();
36829                 if(this.activePanel.afterSlide){
36830                     this.activePanel.afterSlide();
36831                 }
36832             }
36833         }
36834     },
36835
36836     initAutoHide : function(){
36837         if(this.autoHide !== false){
36838             if(!this.autoHideHd){
36839                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36840                 this.autoHideHd = {
36841                     "mouseout": function(e){
36842                         if(!e.within(this.el, true)){
36843                             st.delay(500);
36844                         }
36845                     },
36846                     "mouseover" : function(e){
36847                         st.cancel();
36848                     },
36849                     scope : this
36850                 };
36851             }
36852             this.el.on(this.autoHideHd);
36853         }
36854     },
36855
36856     clearAutoHide : function(){
36857         if(this.autoHide !== false){
36858             this.el.un("mouseout", this.autoHideHd.mouseout);
36859             this.el.un("mouseover", this.autoHideHd.mouseover);
36860         }
36861     },
36862
36863     clearMonitor : function(){
36864         Roo.get(document).un("click", this.slideInIf, this);
36865     },
36866
36867     // these names are backwards but not changed for compat
36868     slideOut : function(){
36869         if(this.isSlid || this.el.hasActiveFx()){
36870             return;
36871         }
36872         this.isSlid = true;
36873         if(this.collapseBtn){
36874             this.collapseBtn.hide();
36875         }
36876         this.closeBtnState = this.closeBtn.getStyle('display');
36877         this.closeBtn.hide();
36878         if(this.stickBtn){
36879             this.stickBtn.show();
36880         }
36881         this.el.show();
36882         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36883         this.beforeSlide();
36884         this.el.setStyle("z-index", 10001);
36885         this.el.slideIn(this.getSlideAnchor(), {
36886             callback: function(){
36887                 this.afterSlide();
36888                 this.initAutoHide();
36889                 Roo.get(document).on("click", this.slideInIf, this);
36890                 this.fireEvent("slideshow", this);
36891             },
36892             scope: this,
36893             block: true
36894         });
36895     },
36896
36897     afterSlideIn : function(){
36898         this.clearAutoHide();
36899         this.isSlid = false;
36900         this.clearMonitor();
36901         this.el.setStyle("z-index", "");
36902         if(this.collapseBtn){
36903             this.collapseBtn.show();
36904         }
36905         this.closeBtn.setStyle('display', this.closeBtnState);
36906         if(this.stickBtn){
36907             this.stickBtn.hide();
36908         }
36909         this.fireEvent("slidehide", this);
36910     },
36911
36912     slideIn : function(cb){
36913         if(!this.isSlid || this.el.hasActiveFx()){
36914             Roo.callback(cb);
36915             return;
36916         }
36917         this.isSlid = false;
36918         this.beforeSlide();
36919         this.el.slideOut(this.getSlideAnchor(), {
36920             callback: function(){
36921                 this.el.setLeftTop(-10000, -10000);
36922                 this.afterSlide();
36923                 this.afterSlideIn();
36924                 Roo.callback(cb);
36925             },
36926             scope: this,
36927             block: true
36928         });
36929     },
36930     
36931     slideInIf : function(e){
36932         if(!e.within(this.el)){
36933             this.slideIn();
36934         }
36935     },
36936
36937     animateCollapse : function(){
36938         this.beforeSlide();
36939         this.el.setStyle("z-index", 20000);
36940         var anchor = this.getSlideAnchor();
36941         this.el.slideOut(anchor, {
36942             callback : function(){
36943                 this.el.setStyle("z-index", "");
36944                 this.collapsedEl.slideIn(anchor, {duration:.3});
36945                 this.afterSlide();
36946                 this.el.setLocation(-10000,-10000);
36947                 this.el.hide();
36948                 this.fireEvent("collapsed", this);
36949             },
36950             scope: this,
36951             block: true
36952         });
36953     },
36954
36955     animateExpand : function(){
36956         this.beforeSlide();
36957         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36958         this.el.setStyle("z-index", 20000);
36959         this.collapsedEl.hide({
36960             duration:.1
36961         });
36962         this.el.slideIn(this.getSlideAnchor(), {
36963             callback : function(){
36964                 this.el.setStyle("z-index", "");
36965                 this.afterSlide();
36966                 if(this.split){
36967                     this.split.el.show();
36968                 }
36969                 this.fireEvent("invalidated", this);
36970                 this.fireEvent("expanded", this);
36971             },
36972             scope: this,
36973             block: true
36974         });
36975     },
36976
36977     anchors : {
36978         "west" : "left",
36979         "east" : "right",
36980         "north" : "top",
36981         "south" : "bottom"
36982     },
36983
36984     sanchors : {
36985         "west" : "l",
36986         "east" : "r",
36987         "north" : "t",
36988         "south" : "b"
36989     },
36990
36991     canchors : {
36992         "west" : "tl-tr",
36993         "east" : "tr-tl",
36994         "north" : "tl-bl",
36995         "south" : "bl-tl"
36996     },
36997
36998     getAnchor : function(){
36999         return this.anchors[this.position];
37000     },
37001
37002     getCollapseAnchor : function(){
37003         return this.canchors[this.position];
37004     },
37005
37006     getSlideAnchor : function(){
37007         return this.sanchors[this.position];
37008     },
37009
37010     getAlignAdj : function(){
37011         var cm = this.cmargins;
37012         switch(this.position){
37013             case "west":
37014                 return [0, 0];
37015             break;
37016             case "east":
37017                 return [0, 0];
37018             break;
37019             case "north":
37020                 return [0, 0];
37021             break;
37022             case "south":
37023                 return [0, 0];
37024             break;
37025         }
37026     },
37027
37028     getExpandAdj : function(){
37029         var c = this.collapsedEl, cm = this.cmargins;
37030         switch(this.position){
37031             case "west":
37032                 return [-(cm.right+c.getWidth()+cm.left), 0];
37033             break;
37034             case "east":
37035                 return [cm.right+c.getWidth()+cm.left, 0];
37036             break;
37037             case "north":
37038                 return [0, -(cm.top+cm.bottom+c.getHeight())];
37039             break;
37040             case "south":
37041                 return [0, cm.top+cm.bottom+c.getHeight()];
37042             break;
37043         }
37044     }
37045 });/*
37046  * Based on:
37047  * Ext JS Library 1.1.1
37048  * Copyright(c) 2006-2007, Ext JS, LLC.
37049  *
37050  * Originally Released Under LGPL - original licence link has changed is not relivant.
37051  *
37052  * Fork - LGPL
37053  * <script type="text/javascript">
37054  */
37055 /*
37056  * These classes are private internal classes
37057  */
37058 Roo.bootstrap.layout.Center = function(config){
37059     config.region = "center";
37060     Roo.bootstrap.layout.Region.call(this, config);
37061     this.visible = true;
37062     this.minWidth = config.minWidth || 20;
37063     this.minHeight = config.minHeight || 20;
37064 };
37065
37066 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
37067     hide : function(){
37068         // center panel can't be hidden
37069     },
37070     
37071     show : function(){
37072         // center panel can't be hidden
37073     },
37074     
37075     getMinWidth: function(){
37076         return this.minWidth;
37077     },
37078     
37079     getMinHeight: function(){
37080         return this.minHeight;
37081     }
37082 });
37083
37084
37085
37086
37087  
37088
37089
37090
37091
37092
37093
37094 Roo.bootstrap.layout.North = function(config)
37095 {
37096     config.region = 'north';
37097     config.cursor = 'n-resize';
37098     
37099     Roo.bootstrap.layout.Split.call(this, config);
37100     
37101     
37102     if(this.split){
37103         this.split.placement = Roo.bootstrap.SplitBar.TOP;
37104         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37105         this.split.el.addClass("roo-layout-split-v");
37106     }
37107     var size = config.initialSize || config.height;
37108     if(typeof size != "undefined"){
37109         this.el.setHeight(size);
37110     }
37111 };
37112 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
37113 {
37114     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37115     
37116     
37117     
37118     getBox : function(){
37119         if(this.collapsed){
37120             return this.collapsedEl.getBox();
37121         }
37122         var box = this.el.getBox();
37123         if(this.split){
37124             box.height += this.split.el.getHeight();
37125         }
37126         return box;
37127     },
37128     
37129     updateBox : function(box){
37130         if(this.split && !this.collapsed){
37131             box.height -= this.split.el.getHeight();
37132             this.split.el.setLeft(box.x);
37133             this.split.el.setTop(box.y+box.height);
37134             this.split.el.setWidth(box.width);
37135         }
37136         if(this.collapsed){
37137             this.updateBody(box.width, null);
37138         }
37139         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37140     }
37141 });
37142
37143
37144
37145
37146
37147 Roo.bootstrap.layout.South = function(config){
37148     config.region = 'south';
37149     config.cursor = 's-resize';
37150     Roo.bootstrap.layout.Split.call(this, config);
37151     if(this.split){
37152         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
37153         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37154         this.split.el.addClass("roo-layout-split-v");
37155     }
37156     var size = config.initialSize || config.height;
37157     if(typeof size != "undefined"){
37158         this.el.setHeight(size);
37159     }
37160 };
37161
37162 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
37163     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37164     getBox : function(){
37165         if(this.collapsed){
37166             return this.collapsedEl.getBox();
37167         }
37168         var box = this.el.getBox();
37169         if(this.split){
37170             var sh = this.split.el.getHeight();
37171             box.height += sh;
37172             box.y -= sh;
37173         }
37174         return box;
37175     },
37176     
37177     updateBox : function(box){
37178         if(this.split && !this.collapsed){
37179             var sh = this.split.el.getHeight();
37180             box.height -= sh;
37181             box.y += sh;
37182             this.split.el.setLeft(box.x);
37183             this.split.el.setTop(box.y-sh);
37184             this.split.el.setWidth(box.width);
37185         }
37186         if(this.collapsed){
37187             this.updateBody(box.width, null);
37188         }
37189         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37190     }
37191 });
37192
37193 Roo.bootstrap.layout.East = function(config){
37194     config.region = "east";
37195     config.cursor = "e-resize";
37196     Roo.bootstrap.layout.Split.call(this, config);
37197     if(this.split){
37198         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
37199         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37200         this.split.el.addClass("roo-layout-split-h");
37201     }
37202     var size = config.initialSize || config.width;
37203     if(typeof size != "undefined"){
37204         this.el.setWidth(size);
37205     }
37206 };
37207 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
37208     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37209     getBox : function(){
37210         if(this.collapsed){
37211             return this.collapsedEl.getBox();
37212         }
37213         var box = this.el.getBox();
37214         if(this.split){
37215             var sw = this.split.el.getWidth();
37216             box.width += sw;
37217             box.x -= sw;
37218         }
37219         return box;
37220     },
37221
37222     updateBox : function(box){
37223         if(this.split && !this.collapsed){
37224             var sw = this.split.el.getWidth();
37225             box.width -= sw;
37226             this.split.el.setLeft(box.x);
37227             this.split.el.setTop(box.y);
37228             this.split.el.setHeight(box.height);
37229             box.x += sw;
37230         }
37231         if(this.collapsed){
37232             this.updateBody(null, box.height);
37233         }
37234         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37235     }
37236 });
37237
37238 Roo.bootstrap.layout.West = function(config){
37239     config.region = "west";
37240     config.cursor = "w-resize";
37241     
37242     Roo.bootstrap.layout.Split.call(this, config);
37243     if(this.split){
37244         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37245         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37246         this.split.el.addClass("roo-layout-split-h");
37247     }
37248     
37249 };
37250 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37251     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37252     
37253     onRender: function(ctr, pos)
37254     {
37255         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37256         var size = this.config.initialSize || this.config.width;
37257         if(typeof size != "undefined"){
37258             this.el.setWidth(size);
37259         }
37260     },
37261     
37262     getBox : function(){
37263         if(this.collapsed){
37264             return this.collapsedEl.getBox();
37265         }
37266         var box = this.el.getBox();
37267         if(this.split){
37268             box.width += this.split.el.getWidth();
37269         }
37270         return box;
37271     },
37272     
37273     updateBox : function(box){
37274         if(this.split && !this.collapsed){
37275             var sw = this.split.el.getWidth();
37276             box.width -= sw;
37277             this.split.el.setLeft(box.x+box.width);
37278             this.split.el.setTop(box.y);
37279             this.split.el.setHeight(box.height);
37280         }
37281         if(this.collapsed){
37282             this.updateBody(null, box.height);
37283         }
37284         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37285     }
37286 });Roo.namespace("Roo.bootstrap.panel");/*
37287  * Based on:
37288  * Ext JS Library 1.1.1
37289  * Copyright(c) 2006-2007, Ext JS, LLC.
37290  *
37291  * Originally Released Under LGPL - original licence link has changed is not relivant.
37292  *
37293  * Fork - LGPL
37294  * <script type="text/javascript">
37295  */
37296 /**
37297  * @class Roo.ContentPanel
37298  * @extends Roo.util.Observable
37299  * A basic ContentPanel element.
37300  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
37301  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
37302  * @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
37303  * @cfg {Boolean}   closable      True if the panel can be closed/removed
37304  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
37305  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37306  * @cfg {Toolbar}   toolbar       A toolbar for this panel
37307  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
37308  * @cfg {String} title          The title for this panel
37309  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37310  * @cfg {String} url            Calls {@link #setUrl} with this value
37311  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37312  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37313  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37314  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37315  * @cfg {Boolean} badges render the badges
37316
37317  * @constructor
37318  * Create a new ContentPanel.
37319  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37320  * @param {String/Object} config A string to set only the title or a config object
37321  * @param {String} content (optional) Set the HTML content for this panel
37322  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37323  */
37324 Roo.bootstrap.panel.Content = function( config){
37325     
37326     this.tpl = config.tpl || false;
37327     
37328     var el = config.el;
37329     var content = config.content;
37330
37331     if(config.autoCreate){ // xtype is available if this is called from factory
37332         el = Roo.id();
37333     }
37334     this.el = Roo.get(el);
37335     if(!this.el && config && config.autoCreate){
37336         if(typeof config.autoCreate == "object"){
37337             if(!config.autoCreate.id){
37338                 config.autoCreate.id = config.id||el;
37339             }
37340             this.el = Roo.DomHelper.append(document.body,
37341                         config.autoCreate, true);
37342         }else{
37343             var elcfg =  {   tag: "div",
37344                             cls: "roo-layout-inactive-content",
37345                             id: config.id||el
37346                             };
37347             if (config.html) {
37348                 elcfg.html = config.html;
37349                 
37350             }
37351                         
37352             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37353         }
37354     } 
37355     this.closable = false;
37356     this.loaded = false;
37357     this.active = false;
37358    
37359       
37360     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37361         
37362         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37363         
37364         this.wrapEl = this.el; //this.el.wrap();
37365         var ti = [];
37366         if (config.toolbar.items) {
37367             ti = config.toolbar.items ;
37368             delete config.toolbar.items ;
37369         }
37370         
37371         var nitems = [];
37372         this.toolbar.render(this.wrapEl, 'before');
37373         for(var i =0;i < ti.length;i++) {
37374           //  Roo.log(['add child', items[i]]);
37375             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37376         }
37377         this.toolbar.items = nitems;
37378         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37379         delete config.toolbar;
37380         
37381     }
37382     /*
37383     // xtype created footer. - not sure if will work as we normally have to render first..
37384     if (this.footer && !this.footer.el && this.footer.xtype) {
37385         if (!this.wrapEl) {
37386             this.wrapEl = this.el.wrap();
37387         }
37388     
37389         this.footer.container = this.wrapEl.createChild();
37390          
37391         this.footer = Roo.factory(this.footer, Roo);
37392         
37393     }
37394     */
37395     
37396      if(typeof config == "string"){
37397         this.title = config;
37398     }else{
37399         Roo.apply(this, config);
37400     }
37401     
37402     if(this.resizeEl){
37403         this.resizeEl = Roo.get(this.resizeEl, true);
37404     }else{
37405         this.resizeEl = this.el;
37406     }
37407     // handle view.xtype
37408     
37409  
37410     
37411     
37412     this.addEvents({
37413         /**
37414          * @event activate
37415          * Fires when this panel is activated. 
37416          * @param {Roo.ContentPanel} this
37417          */
37418         "activate" : true,
37419         /**
37420          * @event deactivate
37421          * Fires when this panel is activated. 
37422          * @param {Roo.ContentPanel} this
37423          */
37424         "deactivate" : true,
37425
37426         /**
37427          * @event resize
37428          * Fires when this panel is resized if fitToFrame is true.
37429          * @param {Roo.ContentPanel} this
37430          * @param {Number} width The width after any component adjustments
37431          * @param {Number} height The height after any component adjustments
37432          */
37433         "resize" : true,
37434         
37435          /**
37436          * @event render
37437          * Fires when this tab is created
37438          * @param {Roo.ContentPanel} this
37439          */
37440         "render" : true
37441         
37442         
37443         
37444     });
37445     
37446
37447     
37448     
37449     if(this.autoScroll){
37450         this.resizeEl.setStyle("overflow", "auto");
37451     } else {
37452         // fix randome scrolling
37453         //this.el.on('scroll', function() {
37454         //    Roo.log('fix random scolling');
37455         //    this.scrollTo('top',0); 
37456         //});
37457     }
37458     content = content || this.content;
37459     if(content){
37460         this.setContent(content);
37461     }
37462     if(config && config.url){
37463         this.setUrl(this.url, this.params, this.loadOnce);
37464     }
37465     
37466     
37467     
37468     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37469     
37470     if (this.view && typeof(this.view.xtype) != 'undefined') {
37471         this.view.el = this.el.appendChild(document.createElement("div"));
37472         this.view = Roo.factory(this.view); 
37473         this.view.render  &&  this.view.render(false, '');  
37474     }
37475     
37476     
37477     this.fireEvent('render', this);
37478 };
37479
37480 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37481     
37482     tabTip : '',
37483     
37484     setRegion : function(region){
37485         this.region = region;
37486         this.setActiveClass(region && !this.background);
37487     },
37488     
37489     
37490     setActiveClass: function(state)
37491     {
37492         if(state){
37493            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37494            this.el.setStyle('position','relative');
37495         }else{
37496            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37497            this.el.setStyle('position', 'absolute');
37498         } 
37499     },
37500     
37501     /**
37502      * Returns the toolbar for this Panel if one was configured. 
37503      * @return {Roo.Toolbar} 
37504      */
37505     getToolbar : function(){
37506         return this.toolbar;
37507     },
37508     
37509     setActiveState : function(active)
37510     {
37511         this.active = active;
37512         this.setActiveClass(active);
37513         if(!active){
37514             if(this.fireEvent("deactivate", this) === false){
37515                 return false;
37516             }
37517             return true;
37518         }
37519         this.fireEvent("activate", this);
37520         return true;
37521     },
37522     /**
37523      * Updates this panel's element
37524      * @param {String} content The new content
37525      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37526     */
37527     setContent : function(content, loadScripts){
37528         this.el.update(content, loadScripts);
37529     },
37530
37531     ignoreResize : function(w, h){
37532         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37533             return true;
37534         }else{
37535             this.lastSize = {width: w, height: h};
37536             return false;
37537         }
37538     },
37539     /**
37540      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37541      * @return {Roo.UpdateManager} The UpdateManager
37542      */
37543     getUpdateManager : function(){
37544         return this.el.getUpdateManager();
37545     },
37546      /**
37547      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37548      * @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:
37549 <pre><code>
37550 panel.load({
37551     url: "your-url.php",
37552     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37553     callback: yourFunction,
37554     scope: yourObject, //(optional scope)
37555     discardUrl: false,
37556     nocache: false,
37557     text: "Loading...",
37558     timeout: 30,
37559     scripts: false
37560 });
37561 </code></pre>
37562      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37563      * 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.
37564      * @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}
37565      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37566      * @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.
37567      * @return {Roo.ContentPanel} this
37568      */
37569     load : function(){
37570         var um = this.el.getUpdateManager();
37571         um.update.apply(um, arguments);
37572         return this;
37573     },
37574
37575
37576     /**
37577      * 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.
37578      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37579      * @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)
37580      * @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)
37581      * @return {Roo.UpdateManager} The UpdateManager
37582      */
37583     setUrl : function(url, params, loadOnce){
37584         if(this.refreshDelegate){
37585             this.removeListener("activate", this.refreshDelegate);
37586         }
37587         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37588         this.on("activate", this.refreshDelegate);
37589         return this.el.getUpdateManager();
37590     },
37591     
37592     _handleRefresh : function(url, params, loadOnce){
37593         if(!loadOnce || !this.loaded){
37594             var updater = this.el.getUpdateManager();
37595             updater.update(url, params, this._setLoaded.createDelegate(this));
37596         }
37597     },
37598     
37599     _setLoaded : function(){
37600         this.loaded = true;
37601     }, 
37602     
37603     /**
37604      * Returns this panel's id
37605      * @return {String} 
37606      */
37607     getId : function(){
37608         return this.el.id;
37609     },
37610     
37611     /** 
37612      * Returns this panel's element - used by regiosn to add.
37613      * @return {Roo.Element} 
37614      */
37615     getEl : function(){
37616         return this.wrapEl || this.el;
37617     },
37618     
37619    
37620     
37621     adjustForComponents : function(width, height)
37622     {
37623         //Roo.log('adjustForComponents ');
37624         if(this.resizeEl != this.el){
37625             width -= this.el.getFrameWidth('lr');
37626             height -= this.el.getFrameWidth('tb');
37627         }
37628         if(this.toolbar){
37629             var te = this.toolbar.getEl();
37630             te.setWidth(width);
37631             height -= te.getHeight();
37632         }
37633         if(this.footer){
37634             var te = this.footer.getEl();
37635             te.setWidth(width);
37636             height -= te.getHeight();
37637         }
37638         
37639         
37640         if(this.adjustments){
37641             width += this.adjustments[0];
37642             height += this.adjustments[1];
37643         }
37644         return {"width": width, "height": height};
37645     },
37646     
37647     setSize : function(width, height){
37648         if(this.fitToFrame && !this.ignoreResize(width, height)){
37649             if(this.fitContainer && this.resizeEl != this.el){
37650                 this.el.setSize(width, height);
37651             }
37652             var size = this.adjustForComponents(width, height);
37653             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37654             this.fireEvent('resize', this, size.width, size.height);
37655         }
37656     },
37657     
37658     /**
37659      * Returns this panel's title
37660      * @return {String} 
37661      */
37662     getTitle : function(){
37663         
37664         if (typeof(this.title) != 'object') {
37665             return this.title;
37666         }
37667         
37668         var t = '';
37669         for (var k in this.title) {
37670             if (!this.title.hasOwnProperty(k)) {
37671                 continue;
37672             }
37673             
37674             if (k.indexOf('-') >= 0) {
37675                 var s = k.split('-');
37676                 for (var i = 0; i<s.length; i++) {
37677                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37678                 }
37679             } else {
37680                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37681             }
37682         }
37683         return t;
37684     },
37685     
37686     /**
37687      * Set this panel's title
37688      * @param {String} title
37689      */
37690     setTitle : function(title){
37691         this.title = title;
37692         if(this.region){
37693             this.region.updatePanelTitle(this, title);
37694         }
37695     },
37696     
37697     /**
37698      * Returns true is this panel was configured to be closable
37699      * @return {Boolean} 
37700      */
37701     isClosable : function(){
37702         return this.closable;
37703     },
37704     
37705     beforeSlide : function(){
37706         this.el.clip();
37707         this.resizeEl.clip();
37708     },
37709     
37710     afterSlide : function(){
37711         this.el.unclip();
37712         this.resizeEl.unclip();
37713     },
37714     
37715     /**
37716      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37717      *   Will fail silently if the {@link #setUrl} method has not been called.
37718      *   This does not activate the panel, just updates its content.
37719      */
37720     refresh : function(){
37721         if(this.refreshDelegate){
37722            this.loaded = false;
37723            this.refreshDelegate();
37724         }
37725     },
37726     
37727     /**
37728      * Destroys this panel
37729      */
37730     destroy : function(){
37731         this.el.removeAllListeners();
37732         var tempEl = document.createElement("span");
37733         tempEl.appendChild(this.el.dom);
37734         tempEl.innerHTML = "";
37735         this.el.remove();
37736         this.el = null;
37737     },
37738     
37739     /**
37740      * form - if the content panel contains a form - this is a reference to it.
37741      * @type {Roo.form.Form}
37742      */
37743     form : false,
37744     /**
37745      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37746      *    This contains a reference to it.
37747      * @type {Roo.View}
37748      */
37749     view : false,
37750     
37751       /**
37752      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37753      * <pre><code>
37754
37755 layout.addxtype({
37756        xtype : 'Form',
37757        items: [ .... ]
37758    }
37759 );
37760
37761 </code></pre>
37762      * @param {Object} cfg Xtype definition of item to add.
37763      */
37764     
37765     
37766     getChildContainer: function () {
37767         return this.getEl();
37768     }
37769     
37770     
37771     /*
37772         var  ret = new Roo.factory(cfg);
37773         return ret;
37774         
37775         
37776         // add form..
37777         if (cfg.xtype.match(/^Form$/)) {
37778             
37779             var el;
37780             //if (this.footer) {
37781             //    el = this.footer.container.insertSibling(false, 'before');
37782             //} else {
37783                 el = this.el.createChild();
37784             //}
37785
37786             this.form = new  Roo.form.Form(cfg);
37787             
37788             
37789             if ( this.form.allItems.length) {
37790                 this.form.render(el.dom);
37791             }
37792             return this.form;
37793         }
37794         // should only have one of theses..
37795         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37796             // views.. should not be just added - used named prop 'view''
37797             
37798             cfg.el = this.el.appendChild(document.createElement("div"));
37799             // factory?
37800             
37801             var ret = new Roo.factory(cfg);
37802              
37803              ret.render && ret.render(false, ''); // render blank..
37804             this.view = ret;
37805             return ret;
37806         }
37807         return false;
37808     }
37809     \*/
37810 });
37811  
37812 /**
37813  * @class Roo.bootstrap.panel.Grid
37814  * @extends Roo.bootstrap.panel.Content
37815  * @constructor
37816  * Create a new GridPanel.
37817  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37818  * @param {Object} config A the config object
37819   
37820  */
37821
37822
37823
37824 Roo.bootstrap.panel.Grid = function(config)
37825 {
37826     
37827       
37828     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37829         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37830
37831     config.el = this.wrapper;
37832     //this.el = this.wrapper;
37833     
37834       if (config.container) {
37835         // ctor'ed from a Border/panel.grid
37836         
37837         
37838         this.wrapper.setStyle("overflow", "hidden");
37839         this.wrapper.addClass('roo-grid-container');
37840
37841     }
37842     
37843     
37844     if(config.toolbar){
37845         var tool_el = this.wrapper.createChild();    
37846         this.toolbar = Roo.factory(config.toolbar);
37847         var ti = [];
37848         if (config.toolbar.items) {
37849             ti = config.toolbar.items ;
37850             delete config.toolbar.items ;
37851         }
37852         
37853         var nitems = [];
37854         this.toolbar.render(tool_el);
37855         for(var i =0;i < ti.length;i++) {
37856           //  Roo.log(['add child', items[i]]);
37857             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37858         }
37859         this.toolbar.items = nitems;
37860         
37861         delete config.toolbar;
37862     }
37863     
37864     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37865     config.grid.scrollBody = true;;
37866     config.grid.monitorWindowResize = false; // turn off autosizing
37867     config.grid.autoHeight = false;
37868     config.grid.autoWidth = false;
37869     
37870     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37871     
37872     if (config.background) {
37873         // render grid on panel activation (if panel background)
37874         this.on('activate', function(gp) {
37875             if (!gp.grid.rendered) {
37876                 gp.grid.render(this.wrapper);
37877                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37878             }
37879         });
37880             
37881     } else {
37882         this.grid.render(this.wrapper);
37883         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37884
37885     }
37886     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37887     // ??? needed ??? config.el = this.wrapper;
37888     
37889     
37890     
37891   
37892     // xtype created footer. - not sure if will work as we normally have to render first..
37893     if (this.footer && !this.footer.el && this.footer.xtype) {
37894         
37895         var ctr = this.grid.getView().getFooterPanel(true);
37896         this.footer.dataSource = this.grid.dataSource;
37897         this.footer = Roo.factory(this.footer, Roo);
37898         this.footer.render(ctr);
37899         
37900     }
37901     
37902     
37903     
37904     
37905      
37906 };
37907
37908 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37909     getId : function(){
37910         return this.grid.id;
37911     },
37912     
37913     /**
37914      * Returns the grid for this panel
37915      * @return {Roo.bootstrap.Table} 
37916      */
37917     getGrid : function(){
37918         return this.grid;    
37919     },
37920     
37921     setSize : function(width, height){
37922         if(!this.ignoreResize(width, height)){
37923             var grid = this.grid;
37924             var size = this.adjustForComponents(width, height);
37925             var gridel = grid.getGridEl();
37926             gridel.setSize(size.width, size.height);
37927             /*
37928             var thd = grid.getGridEl().select('thead',true).first();
37929             var tbd = grid.getGridEl().select('tbody', true).first();
37930             if (tbd) {
37931                 tbd.setSize(width, height - thd.getHeight());
37932             }
37933             */
37934             grid.autoSize();
37935         }
37936     },
37937      
37938     
37939     
37940     beforeSlide : function(){
37941         this.grid.getView().scroller.clip();
37942     },
37943     
37944     afterSlide : function(){
37945         this.grid.getView().scroller.unclip();
37946     },
37947     
37948     destroy : function(){
37949         this.grid.destroy();
37950         delete this.grid;
37951         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37952     }
37953 });
37954
37955 /**
37956  * @class Roo.bootstrap.panel.Nest
37957  * @extends Roo.bootstrap.panel.Content
37958  * @constructor
37959  * Create a new Panel, that can contain a layout.Border.
37960  * 
37961  * 
37962  * @param {Roo.BorderLayout} layout The layout for this panel
37963  * @param {String/Object} config A string to set only the title or a config object
37964  */
37965 Roo.bootstrap.panel.Nest = function(config)
37966 {
37967     // construct with only one argument..
37968     /* FIXME - implement nicer consturctors
37969     if (layout.layout) {
37970         config = layout;
37971         layout = config.layout;
37972         delete config.layout;
37973     }
37974     if (layout.xtype && !layout.getEl) {
37975         // then layout needs constructing..
37976         layout = Roo.factory(layout, Roo);
37977     }
37978     */
37979     
37980     config.el =  config.layout.getEl();
37981     
37982     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37983     
37984     config.layout.monitorWindowResize = false; // turn off autosizing
37985     this.layout = config.layout;
37986     this.layout.getEl().addClass("roo-layout-nested-layout");
37987     this.layout.parent = this;
37988     
37989     
37990     
37991     
37992 };
37993
37994 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37995
37996     setSize : function(width, height){
37997         if(!this.ignoreResize(width, height)){
37998             var size = this.adjustForComponents(width, height);
37999             var el = this.layout.getEl();
38000             if (size.height < 1) {
38001                 el.setWidth(size.width);   
38002             } else {
38003                 el.setSize(size.width, size.height);
38004             }
38005             var touch = el.dom.offsetWidth;
38006             this.layout.layout();
38007             // ie requires a double layout on the first pass
38008             if(Roo.isIE && !this.initialized){
38009                 this.initialized = true;
38010                 this.layout.layout();
38011             }
38012         }
38013     },
38014     
38015     // activate all subpanels if not currently active..
38016     
38017     setActiveState : function(active){
38018         this.active = active;
38019         this.setActiveClass(active);
38020         
38021         if(!active){
38022             this.fireEvent("deactivate", this);
38023             return;
38024         }
38025         
38026         this.fireEvent("activate", this);
38027         // not sure if this should happen before or after..
38028         if (!this.layout) {
38029             return; // should not happen..
38030         }
38031         var reg = false;
38032         for (var r in this.layout.regions) {
38033             reg = this.layout.getRegion(r);
38034             if (reg.getActivePanel()) {
38035                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
38036                 reg.setActivePanel(reg.getActivePanel());
38037                 continue;
38038             }
38039             if (!reg.panels.length) {
38040                 continue;
38041             }
38042             reg.showPanel(reg.getPanel(0));
38043         }
38044         
38045         
38046         
38047         
38048     },
38049     
38050     /**
38051      * Returns the nested BorderLayout for this panel
38052      * @return {Roo.BorderLayout} 
38053      */
38054     getLayout : function(){
38055         return this.layout;
38056     },
38057     
38058      /**
38059      * Adds a xtype elements to the layout of the nested panel
38060      * <pre><code>
38061
38062 panel.addxtype({
38063        xtype : 'ContentPanel',
38064        region: 'west',
38065        items: [ .... ]
38066    }
38067 );
38068
38069 panel.addxtype({
38070         xtype : 'NestedLayoutPanel',
38071         region: 'west',
38072         layout: {
38073            center: { },
38074            west: { }   
38075         },
38076         items : [ ... list of content panels or nested layout panels.. ]
38077    }
38078 );
38079 </code></pre>
38080      * @param {Object} cfg Xtype definition of item to add.
38081      */
38082     addxtype : function(cfg) {
38083         return this.layout.addxtype(cfg);
38084     
38085     }
38086 });/*
38087  * Based on:
38088  * Ext JS Library 1.1.1
38089  * Copyright(c) 2006-2007, Ext JS, LLC.
38090  *
38091  * Originally Released Under LGPL - original licence link has changed is not relivant.
38092  *
38093  * Fork - LGPL
38094  * <script type="text/javascript">
38095  */
38096 /**
38097  * @class Roo.TabPanel
38098  * @extends Roo.util.Observable
38099  * A lightweight tab container.
38100  * <br><br>
38101  * Usage:
38102  * <pre><code>
38103 // basic tabs 1, built from existing content
38104 var tabs = new Roo.TabPanel("tabs1");
38105 tabs.addTab("script", "View Script");
38106 tabs.addTab("markup", "View Markup");
38107 tabs.activate("script");
38108
38109 // more advanced tabs, built from javascript
38110 var jtabs = new Roo.TabPanel("jtabs");
38111 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
38112
38113 // set up the UpdateManager
38114 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
38115 var updater = tab2.getUpdateManager();
38116 updater.setDefaultUrl("ajax1.htm");
38117 tab2.on('activate', updater.refresh, updater, true);
38118
38119 // Use setUrl for Ajax loading
38120 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
38121 tab3.setUrl("ajax2.htm", null, true);
38122
38123 // Disabled tab
38124 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
38125 tab4.disable();
38126
38127 jtabs.activate("jtabs-1");
38128  * </code></pre>
38129  * @constructor
38130  * Create a new TabPanel.
38131  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
38132  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
38133  */
38134 Roo.bootstrap.panel.Tabs = function(config){
38135     /**
38136     * The container element for this TabPanel.
38137     * @type Roo.Element
38138     */
38139     this.el = Roo.get(config.el);
38140     delete config.el;
38141     if(config){
38142         if(typeof config == "boolean"){
38143             this.tabPosition = config ? "bottom" : "top";
38144         }else{
38145             Roo.apply(this, config);
38146         }
38147     }
38148     
38149     if(this.tabPosition == "bottom"){
38150         // if tabs are at the bottom = create the body first.
38151         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38152         this.el.addClass("roo-tabs-bottom");
38153     }
38154     // next create the tabs holders
38155     
38156     if (this.tabPosition == "west"){
38157         
38158         var reg = this.region; // fake it..
38159         while (reg) {
38160             if (!reg.mgr.parent) {
38161                 break;
38162             }
38163             reg = reg.mgr.parent.region;
38164         }
38165         Roo.log("got nest?");
38166         Roo.log(reg);
38167         if (reg.mgr.getRegion('west')) {
38168             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
38169             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
38170             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38171             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38172             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38173         
38174             
38175         }
38176         
38177         
38178     } else {
38179      
38180         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
38181         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38182         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38183         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38184     }
38185     
38186     
38187     if(Roo.isIE){
38188         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
38189     }
38190     
38191     // finally - if tabs are at the top, then create the body last..
38192     if(this.tabPosition != "bottom"){
38193         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
38194          * @type Roo.Element
38195          */
38196         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38197         this.el.addClass("roo-tabs-top");
38198     }
38199     this.items = [];
38200
38201     this.bodyEl.setStyle("position", "relative");
38202
38203     this.active = null;
38204     this.activateDelegate = this.activate.createDelegate(this);
38205
38206     this.addEvents({
38207         /**
38208          * @event tabchange
38209          * Fires when the active tab changes
38210          * @param {Roo.TabPanel} this
38211          * @param {Roo.TabPanelItem} activePanel The new active tab
38212          */
38213         "tabchange": true,
38214         /**
38215          * @event beforetabchange
38216          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
38217          * @param {Roo.TabPanel} this
38218          * @param {Object} e Set cancel to true on this object to cancel the tab change
38219          * @param {Roo.TabPanelItem} tab The tab being changed to
38220          */
38221         "beforetabchange" : true
38222     });
38223
38224     Roo.EventManager.onWindowResize(this.onResize, this);
38225     this.cpad = this.el.getPadding("lr");
38226     this.hiddenCount = 0;
38227
38228
38229     // toolbar on the tabbar support...
38230     if (this.toolbar) {
38231         alert("no toolbar support yet");
38232         this.toolbar  = false;
38233         /*
38234         var tcfg = this.toolbar;
38235         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
38236         this.toolbar = new Roo.Toolbar(tcfg);
38237         if (Roo.isSafari) {
38238             var tbl = tcfg.container.child('table', true);
38239             tbl.setAttribute('width', '100%');
38240         }
38241         */
38242         
38243     }
38244    
38245
38246
38247     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
38248 };
38249
38250 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
38251     /*
38252      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
38253      */
38254     tabPosition : "top",
38255     /*
38256      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
38257      */
38258     currentTabWidth : 0,
38259     /*
38260      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
38261      */
38262     minTabWidth : 40,
38263     /*
38264      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
38265      */
38266     maxTabWidth : 250,
38267     /*
38268      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
38269      */
38270     preferredTabWidth : 175,
38271     /*
38272      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
38273      */
38274     resizeTabs : false,
38275     /*
38276      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38277      */
38278     monitorResize : true,
38279     /*
38280      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
38281      */
38282     toolbar : false,  // set by caller..
38283     
38284     region : false, /// set by caller
38285     
38286     disableTooltips : true, // not used yet...
38287
38288     /**
38289      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38290      * @param {String} id The id of the div to use <b>or create</b>
38291      * @param {String} text The text for the tab
38292      * @param {String} content (optional) Content to put in the TabPanelItem body
38293      * @param {Boolean} closable (optional) True to create a close icon on the tab
38294      * @return {Roo.TabPanelItem} The created TabPanelItem
38295      */
38296     addTab : function(id, text, content, closable, tpl)
38297     {
38298         var item = new Roo.bootstrap.panel.TabItem({
38299             panel: this,
38300             id : id,
38301             text : text,
38302             closable : closable,
38303             tpl : tpl
38304         });
38305         this.addTabItem(item);
38306         if(content){
38307             item.setContent(content);
38308         }
38309         return item;
38310     },
38311
38312     /**
38313      * Returns the {@link Roo.TabPanelItem} with the specified id/index
38314      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38315      * @return {Roo.TabPanelItem}
38316      */
38317     getTab : function(id){
38318         return this.items[id];
38319     },
38320
38321     /**
38322      * Hides the {@link Roo.TabPanelItem} with the specified id/index
38323      * @param {String/Number} id The id or index of the TabPanelItem to hide.
38324      */
38325     hideTab : function(id){
38326         var t = this.items[id];
38327         if(!t.isHidden()){
38328            t.setHidden(true);
38329            this.hiddenCount++;
38330            this.autoSizeTabs();
38331         }
38332     },
38333
38334     /**
38335      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38336      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38337      */
38338     unhideTab : function(id){
38339         var t = this.items[id];
38340         if(t.isHidden()){
38341            t.setHidden(false);
38342            this.hiddenCount--;
38343            this.autoSizeTabs();
38344         }
38345     },
38346
38347     /**
38348      * Adds an existing {@link Roo.TabPanelItem}.
38349      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38350      */
38351     addTabItem : function(item)
38352     {
38353         this.items[item.id] = item;
38354         this.items.push(item);
38355         this.autoSizeTabs();
38356       //  if(this.resizeTabs){
38357     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38358   //         this.autoSizeTabs();
38359 //        }else{
38360 //            item.autoSize();
38361        // }
38362     },
38363
38364     /**
38365      * Removes a {@link Roo.TabPanelItem}.
38366      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38367      */
38368     removeTab : function(id){
38369         var items = this.items;
38370         var tab = items[id];
38371         if(!tab) { return; }
38372         var index = items.indexOf(tab);
38373         if(this.active == tab && items.length > 1){
38374             var newTab = this.getNextAvailable(index);
38375             if(newTab) {
38376                 newTab.activate();
38377             }
38378         }
38379         this.stripEl.dom.removeChild(tab.pnode.dom);
38380         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38381             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38382         }
38383         items.splice(index, 1);
38384         delete this.items[tab.id];
38385         tab.fireEvent("close", tab);
38386         tab.purgeListeners();
38387         this.autoSizeTabs();
38388     },
38389
38390     getNextAvailable : function(start){
38391         var items = this.items;
38392         var index = start;
38393         // look for a next tab that will slide over to
38394         // replace the one being removed
38395         while(index < items.length){
38396             var item = items[++index];
38397             if(item && !item.isHidden()){
38398                 return item;
38399             }
38400         }
38401         // if one isn't found select the previous tab (on the left)
38402         index = start;
38403         while(index >= 0){
38404             var item = items[--index];
38405             if(item && !item.isHidden()){
38406                 return item;
38407             }
38408         }
38409         return null;
38410     },
38411
38412     /**
38413      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38414      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38415      */
38416     disableTab : function(id){
38417         var tab = this.items[id];
38418         if(tab && this.active != tab){
38419             tab.disable();
38420         }
38421     },
38422
38423     /**
38424      * Enables a {@link Roo.TabPanelItem} that is disabled.
38425      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38426      */
38427     enableTab : function(id){
38428         var tab = this.items[id];
38429         tab.enable();
38430     },
38431
38432     /**
38433      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38434      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38435      * @return {Roo.TabPanelItem} The TabPanelItem.
38436      */
38437     activate : function(id)
38438     {
38439         //Roo.log('activite:'  + id);
38440         
38441         var tab = this.items[id];
38442         if(!tab){
38443             return null;
38444         }
38445         if(tab == this.active || tab.disabled){
38446             return tab;
38447         }
38448         var e = {};
38449         this.fireEvent("beforetabchange", this, e, tab);
38450         if(e.cancel !== true && !tab.disabled){
38451             if(this.active){
38452                 this.active.hide();
38453             }
38454             this.active = this.items[id];
38455             this.active.show();
38456             this.fireEvent("tabchange", this, this.active);
38457         }
38458         return tab;
38459     },
38460
38461     /**
38462      * Gets the active {@link Roo.TabPanelItem}.
38463      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38464      */
38465     getActiveTab : function(){
38466         return this.active;
38467     },
38468
38469     /**
38470      * Updates the tab body element to fit the height of the container element
38471      * for overflow scrolling
38472      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38473      */
38474     syncHeight : function(targetHeight){
38475         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38476         var bm = this.bodyEl.getMargins();
38477         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38478         this.bodyEl.setHeight(newHeight);
38479         return newHeight;
38480     },
38481
38482     onResize : function(){
38483         if(this.monitorResize){
38484             this.autoSizeTabs();
38485         }
38486     },
38487
38488     /**
38489      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38490      */
38491     beginUpdate : function(){
38492         this.updating = true;
38493     },
38494
38495     /**
38496      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38497      */
38498     endUpdate : function(){
38499         this.updating = false;
38500         this.autoSizeTabs();
38501     },
38502
38503     /**
38504      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38505      */
38506     autoSizeTabs : function()
38507     {
38508         var count = this.items.length;
38509         var vcount = count - this.hiddenCount;
38510         
38511         if (vcount < 2) {
38512             this.stripEl.hide();
38513         } else {
38514             this.stripEl.show();
38515         }
38516         
38517         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38518             return;
38519         }
38520         
38521         
38522         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38523         var availWidth = Math.floor(w / vcount);
38524         var b = this.stripBody;
38525         if(b.getWidth() > w){
38526             var tabs = this.items;
38527             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38528             if(availWidth < this.minTabWidth){
38529                 /*if(!this.sleft){    // incomplete scrolling code
38530                     this.createScrollButtons();
38531                 }
38532                 this.showScroll();
38533                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38534             }
38535         }else{
38536             if(this.currentTabWidth < this.preferredTabWidth){
38537                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38538             }
38539         }
38540     },
38541
38542     /**
38543      * Returns the number of tabs in this TabPanel.
38544      * @return {Number}
38545      */
38546      getCount : function(){
38547          return this.items.length;
38548      },
38549
38550     /**
38551      * Resizes all the tabs to the passed width
38552      * @param {Number} The new width
38553      */
38554     setTabWidth : function(width){
38555         this.currentTabWidth = width;
38556         for(var i = 0, len = this.items.length; i < len; i++) {
38557                 if(!this.items[i].isHidden()) {
38558                 this.items[i].setWidth(width);
38559             }
38560         }
38561     },
38562
38563     /**
38564      * Destroys this TabPanel
38565      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38566      */
38567     destroy : function(removeEl){
38568         Roo.EventManager.removeResizeListener(this.onResize, this);
38569         for(var i = 0, len = this.items.length; i < len; i++){
38570             this.items[i].purgeListeners();
38571         }
38572         if(removeEl === true){
38573             this.el.update("");
38574             this.el.remove();
38575         }
38576     },
38577     
38578     createStrip : function(container)
38579     {
38580         var strip = document.createElement("nav");
38581         strip.className = Roo.bootstrap.version == 4 ?
38582             "navbar-light bg-light" : 
38583             "navbar navbar-default"; //"x-tabs-wrap";
38584         container.appendChild(strip);
38585         return strip;
38586     },
38587     
38588     createStripList : function(strip)
38589     {
38590         // div wrapper for retard IE
38591         // returns the "tr" element.
38592         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38593         //'<div class="x-tabs-strip-wrap">'+
38594           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38595           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38596         return strip.firstChild; //.firstChild.firstChild.firstChild;
38597     },
38598     createBody : function(container)
38599     {
38600         var body = document.createElement("div");
38601         Roo.id(body, "tab-body");
38602         //Roo.fly(body).addClass("x-tabs-body");
38603         Roo.fly(body).addClass("tab-content");
38604         container.appendChild(body);
38605         return body;
38606     },
38607     createItemBody :function(bodyEl, id){
38608         var body = Roo.getDom(id);
38609         if(!body){
38610             body = document.createElement("div");
38611             body.id = id;
38612         }
38613         //Roo.fly(body).addClass("x-tabs-item-body");
38614         Roo.fly(body).addClass("tab-pane");
38615          bodyEl.insertBefore(body, bodyEl.firstChild);
38616         return body;
38617     },
38618     /** @private */
38619     createStripElements :  function(stripEl, text, closable, tpl)
38620     {
38621         var td = document.createElement("li"); // was td..
38622         td.className = 'nav-item';
38623         
38624         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38625         
38626         
38627         stripEl.appendChild(td);
38628         /*if(closable){
38629             td.className = "x-tabs-closable";
38630             if(!this.closeTpl){
38631                 this.closeTpl = new Roo.Template(
38632                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38633                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38634                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38635                 );
38636             }
38637             var el = this.closeTpl.overwrite(td, {"text": text});
38638             var close = el.getElementsByTagName("div")[0];
38639             var inner = el.getElementsByTagName("em")[0];
38640             return {"el": el, "close": close, "inner": inner};
38641         } else {
38642         */
38643         // not sure what this is..
38644 //            if(!this.tabTpl){
38645                 //this.tabTpl = new Roo.Template(
38646                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38647                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38648                 //);
38649 //                this.tabTpl = new Roo.Template(
38650 //                   '<a href="#">' +
38651 //                   '<span unselectable="on"' +
38652 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38653 //                            ' >{text}</span></a>'
38654 //                );
38655 //                
38656 //            }
38657
38658
38659             var template = tpl || this.tabTpl || false;
38660             
38661             if(!template){
38662                 template =  new Roo.Template(
38663                         Roo.bootstrap.version == 4 ? 
38664                             (
38665                                 '<a class="nav-link" href="#" unselectable="on"' +
38666                                      (this.disableTooltips ? '' : ' title="{text}"') +
38667                                      ' >{text}</a>'
38668                             ) : (
38669                                 '<a class="nav-link" href="#">' +
38670                                 '<span unselectable="on"' +
38671                                          (this.disableTooltips ? '' : ' title="{text}"') +
38672                                     ' >{text}</span></a>'
38673                             )
38674                 );
38675             }
38676             
38677             switch (typeof(template)) {
38678                 case 'object' :
38679                     break;
38680                 case 'string' :
38681                     template = new Roo.Template(template);
38682                     break;
38683                 default :
38684                     break;
38685             }
38686             
38687             var el = template.overwrite(td, {"text": text});
38688             
38689             var inner = el.getElementsByTagName("span")[0];
38690             
38691             return {"el": el, "inner": inner};
38692             
38693     }
38694         
38695     
38696 });
38697
38698 /**
38699  * @class Roo.TabPanelItem
38700  * @extends Roo.util.Observable
38701  * Represents an individual item (tab plus body) in a TabPanel.
38702  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38703  * @param {String} id The id of this TabPanelItem
38704  * @param {String} text The text for the tab of this TabPanelItem
38705  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38706  */
38707 Roo.bootstrap.panel.TabItem = function(config){
38708     /**
38709      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38710      * @type Roo.TabPanel
38711      */
38712     this.tabPanel = config.panel;
38713     /**
38714      * The id for this TabPanelItem
38715      * @type String
38716      */
38717     this.id = config.id;
38718     /** @private */
38719     this.disabled = false;
38720     /** @private */
38721     this.text = config.text;
38722     /** @private */
38723     this.loaded = false;
38724     this.closable = config.closable;
38725
38726     /**
38727      * The body element for this TabPanelItem.
38728      * @type Roo.Element
38729      */
38730     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38731     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38732     this.bodyEl.setStyle("display", "block");
38733     this.bodyEl.setStyle("zoom", "1");
38734     //this.hideAction();
38735
38736     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38737     /** @private */
38738     this.el = Roo.get(els.el);
38739     this.inner = Roo.get(els.inner, true);
38740      this.textEl = Roo.bootstrap.version == 4 ?
38741         this.el : Roo.get(this.el.dom.firstChild, true);
38742
38743     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
38744     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
38745
38746     
38747 //    this.el.on("mousedown", this.onTabMouseDown, this);
38748     this.el.on("click", this.onTabClick, this);
38749     /** @private */
38750     if(config.closable){
38751         var c = Roo.get(els.close, true);
38752         c.dom.title = this.closeText;
38753         c.addClassOnOver("close-over");
38754         c.on("click", this.closeClick, this);
38755      }
38756
38757     this.addEvents({
38758          /**
38759          * @event activate
38760          * Fires when this tab becomes the active tab.
38761          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38762          * @param {Roo.TabPanelItem} this
38763          */
38764         "activate": true,
38765         /**
38766          * @event beforeclose
38767          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38768          * @param {Roo.TabPanelItem} this
38769          * @param {Object} e Set cancel to true on this object to cancel the close.
38770          */
38771         "beforeclose": true,
38772         /**
38773          * @event close
38774          * Fires when this tab is closed.
38775          * @param {Roo.TabPanelItem} this
38776          */
38777          "close": true,
38778         /**
38779          * @event deactivate
38780          * Fires when this tab is no longer the active tab.
38781          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38782          * @param {Roo.TabPanelItem} this
38783          */
38784          "deactivate" : true
38785     });
38786     this.hidden = false;
38787
38788     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38789 };
38790
38791 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38792            {
38793     purgeListeners : function(){
38794        Roo.util.Observable.prototype.purgeListeners.call(this);
38795        this.el.removeAllListeners();
38796     },
38797     /**
38798      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38799      */
38800     show : function(){
38801         this.status_node.addClass("active");
38802         this.showAction();
38803         if(Roo.isOpera){
38804             this.tabPanel.stripWrap.repaint();
38805         }
38806         this.fireEvent("activate", this.tabPanel, this);
38807     },
38808
38809     /**
38810      * Returns true if this tab is the active tab.
38811      * @return {Boolean}
38812      */
38813     isActive : function(){
38814         return this.tabPanel.getActiveTab() == this;
38815     },
38816
38817     /**
38818      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38819      */
38820     hide : function(){
38821         this.status_node.removeClass("active");
38822         this.hideAction();
38823         this.fireEvent("deactivate", this.tabPanel, this);
38824     },
38825
38826     hideAction : function(){
38827         this.bodyEl.hide();
38828         this.bodyEl.setStyle("position", "absolute");
38829         this.bodyEl.setLeft("-20000px");
38830         this.bodyEl.setTop("-20000px");
38831     },
38832
38833     showAction : function(){
38834         this.bodyEl.setStyle("position", "relative");
38835         this.bodyEl.setTop("");
38836         this.bodyEl.setLeft("");
38837         this.bodyEl.show();
38838     },
38839
38840     /**
38841      * Set the tooltip for the tab.
38842      * @param {String} tooltip The tab's tooltip
38843      */
38844     setTooltip : function(text){
38845         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38846             this.textEl.dom.qtip = text;
38847             this.textEl.dom.removeAttribute('title');
38848         }else{
38849             this.textEl.dom.title = text;
38850         }
38851     },
38852
38853     onTabClick : function(e){
38854         e.preventDefault();
38855         this.tabPanel.activate(this.id);
38856     },
38857
38858     onTabMouseDown : function(e){
38859         e.preventDefault();
38860         this.tabPanel.activate(this.id);
38861     },
38862 /*
38863     getWidth : function(){
38864         return this.inner.getWidth();
38865     },
38866
38867     setWidth : function(width){
38868         var iwidth = width - this.linode.getPadding("lr");
38869         this.inner.setWidth(iwidth);
38870         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38871         this.linode.setWidth(width);
38872     },
38873 */
38874     /**
38875      * Show or hide the tab
38876      * @param {Boolean} hidden True to hide or false to show.
38877      */
38878     setHidden : function(hidden){
38879         this.hidden = hidden;
38880         this.linode.setStyle("display", hidden ? "none" : "");
38881     },
38882
38883     /**
38884      * Returns true if this tab is "hidden"
38885      * @return {Boolean}
38886      */
38887     isHidden : function(){
38888         return this.hidden;
38889     },
38890
38891     /**
38892      * Returns the text for this tab
38893      * @return {String}
38894      */
38895     getText : function(){
38896         return this.text;
38897     },
38898     /*
38899     autoSize : function(){
38900         //this.el.beginMeasure();
38901         this.textEl.setWidth(1);
38902         /*
38903          *  #2804 [new] Tabs in Roojs
38904          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38905          */
38906         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38907         //this.el.endMeasure();
38908     //},
38909
38910     /**
38911      * Sets the text for the tab (Note: this also sets the tooltip text)
38912      * @param {String} text The tab's text and tooltip
38913      */
38914     setText : function(text){
38915         this.text = text;
38916         this.textEl.update(text);
38917         this.setTooltip(text);
38918         //if(!this.tabPanel.resizeTabs){
38919         //    this.autoSize();
38920         //}
38921     },
38922     /**
38923      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38924      */
38925     activate : function(){
38926         this.tabPanel.activate(this.id);
38927     },
38928
38929     /**
38930      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38931      */
38932     disable : function(){
38933         if(this.tabPanel.active != this){
38934             this.disabled = true;
38935             this.status_node.addClass("disabled");
38936         }
38937     },
38938
38939     /**
38940      * Enables this TabPanelItem if it was previously disabled.
38941      */
38942     enable : function(){
38943         this.disabled = false;
38944         this.status_node.removeClass("disabled");
38945     },
38946
38947     /**
38948      * Sets the content for this TabPanelItem.
38949      * @param {String} content The content
38950      * @param {Boolean} loadScripts true to look for and load scripts
38951      */
38952     setContent : function(content, loadScripts){
38953         this.bodyEl.update(content, loadScripts);
38954     },
38955
38956     /**
38957      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38958      * @return {Roo.UpdateManager} The UpdateManager
38959      */
38960     getUpdateManager : function(){
38961         return this.bodyEl.getUpdateManager();
38962     },
38963
38964     /**
38965      * Set a URL to be used to load the content for this TabPanelItem.
38966      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38967      * @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)
38968      * @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)
38969      * @return {Roo.UpdateManager} The UpdateManager
38970      */
38971     setUrl : function(url, params, loadOnce){
38972         if(this.refreshDelegate){
38973             this.un('activate', this.refreshDelegate);
38974         }
38975         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38976         this.on("activate", this.refreshDelegate);
38977         return this.bodyEl.getUpdateManager();
38978     },
38979
38980     /** @private */
38981     _handleRefresh : function(url, params, loadOnce){
38982         if(!loadOnce || !this.loaded){
38983             var updater = this.bodyEl.getUpdateManager();
38984             updater.update(url, params, this._setLoaded.createDelegate(this));
38985         }
38986     },
38987
38988     /**
38989      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38990      *   Will fail silently if the setUrl method has not been called.
38991      *   This does not activate the panel, just updates its content.
38992      */
38993     refresh : function(){
38994         if(this.refreshDelegate){
38995            this.loaded = false;
38996            this.refreshDelegate();
38997         }
38998     },
38999
39000     /** @private */
39001     _setLoaded : function(){
39002         this.loaded = true;
39003     },
39004
39005     /** @private */
39006     closeClick : function(e){
39007         var o = {};
39008         e.stopEvent();
39009         this.fireEvent("beforeclose", this, o);
39010         if(o.cancel !== true){
39011             this.tabPanel.removeTab(this.id);
39012         }
39013     },
39014     /**
39015      * The text displayed in the tooltip for the close icon.
39016      * @type String
39017      */
39018     closeText : "Close this tab"
39019 });
39020 /**
39021 *    This script refer to:
39022 *    Title: International Telephone Input
39023 *    Author: Jack O'Connor
39024 *    Code version:  v12.1.12
39025 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39026 **/
39027
39028 Roo.bootstrap.PhoneInputData = function() {
39029     var d = [
39030       [
39031         "Afghanistan (‫افغانستان‬‎)",
39032         "af",
39033         "93"
39034       ],
39035       [
39036         "Albania (Shqipëri)",
39037         "al",
39038         "355"
39039       ],
39040       [
39041         "Algeria (‫الجزائر‬‎)",
39042         "dz",
39043         "213"
39044       ],
39045       [
39046         "American Samoa",
39047         "as",
39048         "1684"
39049       ],
39050       [
39051         "Andorra",
39052         "ad",
39053         "376"
39054       ],
39055       [
39056         "Angola",
39057         "ao",
39058         "244"
39059       ],
39060       [
39061         "Anguilla",
39062         "ai",
39063         "1264"
39064       ],
39065       [
39066         "Antigua and Barbuda",
39067         "ag",
39068         "1268"
39069       ],
39070       [
39071         "Argentina",
39072         "ar",
39073         "54"
39074       ],
39075       [
39076         "Armenia (Հայաստան)",
39077         "am",
39078         "374"
39079       ],
39080       [
39081         "Aruba",
39082         "aw",
39083         "297"
39084       ],
39085       [
39086         "Australia",
39087         "au",
39088         "61",
39089         0
39090       ],
39091       [
39092         "Austria (Österreich)",
39093         "at",
39094         "43"
39095       ],
39096       [
39097         "Azerbaijan (Azərbaycan)",
39098         "az",
39099         "994"
39100       ],
39101       [
39102         "Bahamas",
39103         "bs",
39104         "1242"
39105       ],
39106       [
39107         "Bahrain (‫البحرين‬‎)",
39108         "bh",
39109         "973"
39110       ],
39111       [
39112         "Bangladesh (বাংলাদেশ)",
39113         "bd",
39114         "880"
39115       ],
39116       [
39117         "Barbados",
39118         "bb",
39119         "1246"
39120       ],
39121       [
39122         "Belarus (Беларусь)",
39123         "by",
39124         "375"
39125       ],
39126       [
39127         "Belgium (België)",
39128         "be",
39129         "32"
39130       ],
39131       [
39132         "Belize",
39133         "bz",
39134         "501"
39135       ],
39136       [
39137         "Benin (Bénin)",
39138         "bj",
39139         "229"
39140       ],
39141       [
39142         "Bermuda",
39143         "bm",
39144         "1441"
39145       ],
39146       [
39147         "Bhutan (འབྲུག)",
39148         "bt",
39149         "975"
39150       ],
39151       [
39152         "Bolivia",
39153         "bo",
39154         "591"
39155       ],
39156       [
39157         "Bosnia and Herzegovina (Босна и Херцеговина)",
39158         "ba",
39159         "387"
39160       ],
39161       [
39162         "Botswana",
39163         "bw",
39164         "267"
39165       ],
39166       [
39167         "Brazil (Brasil)",
39168         "br",
39169         "55"
39170       ],
39171       [
39172         "British Indian Ocean Territory",
39173         "io",
39174         "246"
39175       ],
39176       [
39177         "British Virgin Islands",
39178         "vg",
39179         "1284"
39180       ],
39181       [
39182         "Brunei",
39183         "bn",
39184         "673"
39185       ],
39186       [
39187         "Bulgaria (България)",
39188         "bg",
39189         "359"
39190       ],
39191       [
39192         "Burkina Faso",
39193         "bf",
39194         "226"
39195       ],
39196       [
39197         "Burundi (Uburundi)",
39198         "bi",
39199         "257"
39200       ],
39201       [
39202         "Cambodia (កម្ពុជា)",
39203         "kh",
39204         "855"
39205       ],
39206       [
39207         "Cameroon (Cameroun)",
39208         "cm",
39209         "237"
39210       ],
39211       [
39212         "Canada",
39213         "ca",
39214         "1",
39215         1,
39216         ["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"]
39217       ],
39218       [
39219         "Cape Verde (Kabu Verdi)",
39220         "cv",
39221         "238"
39222       ],
39223       [
39224         "Caribbean Netherlands",
39225         "bq",
39226         "599",
39227         1
39228       ],
39229       [
39230         "Cayman Islands",
39231         "ky",
39232         "1345"
39233       ],
39234       [
39235         "Central African Republic (République centrafricaine)",
39236         "cf",
39237         "236"
39238       ],
39239       [
39240         "Chad (Tchad)",
39241         "td",
39242         "235"
39243       ],
39244       [
39245         "Chile",
39246         "cl",
39247         "56"
39248       ],
39249       [
39250         "China (中国)",
39251         "cn",
39252         "86"
39253       ],
39254       [
39255         "Christmas Island",
39256         "cx",
39257         "61",
39258         2
39259       ],
39260       [
39261         "Cocos (Keeling) Islands",
39262         "cc",
39263         "61",
39264         1
39265       ],
39266       [
39267         "Colombia",
39268         "co",
39269         "57"
39270       ],
39271       [
39272         "Comoros (‫جزر القمر‬‎)",
39273         "km",
39274         "269"
39275       ],
39276       [
39277         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
39278         "cd",
39279         "243"
39280       ],
39281       [
39282         "Congo (Republic) (Congo-Brazzaville)",
39283         "cg",
39284         "242"
39285       ],
39286       [
39287         "Cook Islands",
39288         "ck",
39289         "682"
39290       ],
39291       [
39292         "Costa Rica",
39293         "cr",
39294         "506"
39295       ],
39296       [
39297         "Côte d’Ivoire",
39298         "ci",
39299         "225"
39300       ],
39301       [
39302         "Croatia (Hrvatska)",
39303         "hr",
39304         "385"
39305       ],
39306       [
39307         "Cuba",
39308         "cu",
39309         "53"
39310       ],
39311       [
39312         "Curaçao",
39313         "cw",
39314         "599",
39315         0
39316       ],
39317       [
39318         "Cyprus (Κύπρος)",
39319         "cy",
39320         "357"
39321       ],
39322       [
39323         "Czech Republic (Česká republika)",
39324         "cz",
39325         "420"
39326       ],
39327       [
39328         "Denmark (Danmark)",
39329         "dk",
39330         "45"
39331       ],
39332       [
39333         "Djibouti",
39334         "dj",
39335         "253"
39336       ],
39337       [
39338         "Dominica",
39339         "dm",
39340         "1767"
39341       ],
39342       [
39343         "Dominican Republic (República Dominicana)",
39344         "do",
39345         "1",
39346         2,
39347         ["809", "829", "849"]
39348       ],
39349       [
39350         "Ecuador",
39351         "ec",
39352         "593"
39353       ],
39354       [
39355         "Egypt (‫مصر‬‎)",
39356         "eg",
39357         "20"
39358       ],
39359       [
39360         "El Salvador",
39361         "sv",
39362         "503"
39363       ],
39364       [
39365         "Equatorial Guinea (Guinea Ecuatorial)",
39366         "gq",
39367         "240"
39368       ],
39369       [
39370         "Eritrea",
39371         "er",
39372         "291"
39373       ],
39374       [
39375         "Estonia (Eesti)",
39376         "ee",
39377         "372"
39378       ],
39379       [
39380         "Ethiopia",
39381         "et",
39382         "251"
39383       ],
39384       [
39385         "Falkland Islands (Islas Malvinas)",
39386         "fk",
39387         "500"
39388       ],
39389       [
39390         "Faroe Islands (Føroyar)",
39391         "fo",
39392         "298"
39393       ],
39394       [
39395         "Fiji",
39396         "fj",
39397         "679"
39398       ],
39399       [
39400         "Finland (Suomi)",
39401         "fi",
39402         "358",
39403         0
39404       ],
39405       [
39406         "France",
39407         "fr",
39408         "33"
39409       ],
39410       [
39411         "French Guiana (Guyane française)",
39412         "gf",
39413         "594"
39414       ],
39415       [
39416         "French Polynesia (Polynésie française)",
39417         "pf",
39418         "689"
39419       ],
39420       [
39421         "Gabon",
39422         "ga",
39423         "241"
39424       ],
39425       [
39426         "Gambia",
39427         "gm",
39428         "220"
39429       ],
39430       [
39431         "Georgia (საქართველო)",
39432         "ge",
39433         "995"
39434       ],
39435       [
39436         "Germany (Deutschland)",
39437         "de",
39438         "49"
39439       ],
39440       [
39441         "Ghana (Gaana)",
39442         "gh",
39443         "233"
39444       ],
39445       [
39446         "Gibraltar",
39447         "gi",
39448         "350"
39449       ],
39450       [
39451         "Greece (Ελλάδα)",
39452         "gr",
39453         "30"
39454       ],
39455       [
39456         "Greenland (Kalaallit Nunaat)",
39457         "gl",
39458         "299"
39459       ],
39460       [
39461         "Grenada",
39462         "gd",
39463         "1473"
39464       ],
39465       [
39466         "Guadeloupe",
39467         "gp",
39468         "590",
39469         0
39470       ],
39471       [
39472         "Guam",
39473         "gu",
39474         "1671"
39475       ],
39476       [
39477         "Guatemala",
39478         "gt",
39479         "502"
39480       ],
39481       [
39482         "Guernsey",
39483         "gg",
39484         "44",
39485         1
39486       ],
39487       [
39488         "Guinea (Guinée)",
39489         "gn",
39490         "224"
39491       ],
39492       [
39493         "Guinea-Bissau (Guiné Bissau)",
39494         "gw",
39495         "245"
39496       ],
39497       [
39498         "Guyana",
39499         "gy",
39500         "592"
39501       ],
39502       [
39503         "Haiti",
39504         "ht",
39505         "509"
39506       ],
39507       [
39508         "Honduras",
39509         "hn",
39510         "504"
39511       ],
39512       [
39513         "Hong Kong (香港)",
39514         "hk",
39515         "852"
39516       ],
39517       [
39518         "Hungary (Magyarország)",
39519         "hu",
39520         "36"
39521       ],
39522       [
39523         "Iceland (Ísland)",
39524         "is",
39525         "354"
39526       ],
39527       [
39528         "India (भारत)",
39529         "in",
39530         "91"
39531       ],
39532       [
39533         "Indonesia",
39534         "id",
39535         "62"
39536       ],
39537       [
39538         "Iran (‫ایران‬‎)",
39539         "ir",
39540         "98"
39541       ],
39542       [
39543         "Iraq (‫العراق‬‎)",
39544         "iq",
39545         "964"
39546       ],
39547       [
39548         "Ireland",
39549         "ie",
39550         "353"
39551       ],
39552       [
39553         "Isle of Man",
39554         "im",
39555         "44",
39556         2
39557       ],
39558       [
39559         "Israel (‫ישראל‬‎)",
39560         "il",
39561         "972"
39562       ],
39563       [
39564         "Italy (Italia)",
39565         "it",
39566         "39",
39567         0
39568       ],
39569       [
39570         "Jamaica",
39571         "jm",
39572         "1876"
39573       ],
39574       [
39575         "Japan (日本)",
39576         "jp",
39577         "81"
39578       ],
39579       [
39580         "Jersey",
39581         "je",
39582         "44",
39583         3
39584       ],
39585       [
39586         "Jordan (‫الأردن‬‎)",
39587         "jo",
39588         "962"
39589       ],
39590       [
39591         "Kazakhstan (Казахстан)",
39592         "kz",
39593         "7",
39594         1
39595       ],
39596       [
39597         "Kenya",
39598         "ke",
39599         "254"
39600       ],
39601       [
39602         "Kiribati",
39603         "ki",
39604         "686"
39605       ],
39606       [
39607         "Kosovo",
39608         "xk",
39609         "383"
39610       ],
39611       [
39612         "Kuwait (‫الكويت‬‎)",
39613         "kw",
39614         "965"
39615       ],
39616       [
39617         "Kyrgyzstan (Кыргызстан)",
39618         "kg",
39619         "996"
39620       ],
39621       [
39622         "Laos (ລາວ)",
39623         "la",
39624         "856"
39625       ],
39626       [
39627         "Latvia (Latvija)",
39628         "lv",
39629         "371"
39630       ],
39631       [
39632         "Lebanon (‫لبنان‬‎)",
39633         "lb",
39634         "961"
39635       ],
39636       [
39637         "Lesotho",
39638         "ls",
39639         "266"
39640       ],
39641       [
39642         "Liberia",
39643         "lr",
39644         "231"
39645       ],
39646       [
39647         "Libya (‫ليبيا‬‎)",
39648         "ly",
39649         "218"
39650       ],
39651       [
39652         "Liechtenstein",
39653         "li",
39654         "423"
39655       ],
39656       [
39657         "Lithuania (Lietuva)",
39658         "lt",
39659         "370"
39660       ],
39661       [
39662         "Luxembourg",
39663         "lu",
39664         "352"
39665       ],
39666       [
39667         "Macau (澳門)",
39668         "mo",
39669         "853"
39670       ],
39671       [
39672         "Macedonia (FYROM) (Македонија)",
39673         "mk",
39674         "389"
39675       ],
39676       [
39677         "Madagascar (Madagasikara)",
39678         "mg",
39679         "261"
39680       ],
39681       [
39682         "Malawi",
39683         "mw",
39684         "265"
39685       ],
39686       [
39687         "Malaysia",
39688         "my",
39689         "60"
39690       ],
39691       [
39692         "Maldives",
39693         "mv",
39694         "960"
39695       ],
39696       [
39697         "Mali",
39698         "ml",
39699         "223"
39700       ],
39701       [
39702         "Malta",
39703         "mt",
39704         "356"
39705       ],
39706       [
39707         "Marshall Islands",
39708         "mh",
39709         "692"
39710       ],
39711       [
39712         "Martinique",
39713         "mq",
39714         "596"
39715       ],
39716       [
39717         "Mauritania (‫موريتانيا‬‎)",
39718         "mr",
39719         "222"
39720       ],
39721       [
39722         "Mauritius (Moris)",
39723         "mu",
39724         "230"
39725       ],
39726       [
39727         "Mayotte",
39728         "yt",
39729         "262",
39730         1
39731       ],
39732       [
39733         "Mexico (México)",
39734         "mx",
39735         "52"
39736       ],
39737       [
39738         "Micronesia",
39739         "fm",
39740         "691"
39741       ],
39742       [
39743         "Moldova (Republica Moldova)",
39744         "md",
39745         "373"
39746       ],
39747       [
39748         "Monaco",
39749         "mc",
39750         "377"
39751       ],
39752       [
39753         "Mongolia (Монгол)",
39754         "mn",
39755         "976"
39756       ],
39757       [
39758         "Montenegro (Crna Gora)",
39759         "me",
39760         "382"
39761       ],
39762       [
39763         "Montserrat",
39764         "ms",
39765         "1664"
39766       ],
39767       [
39768         "Morocco (‫المغرب‬‎)",
39769         "ma",
39770         "212",
39771         0
39772       ],
39773       [
39774         "Mozambique (Moçambique)",
39775         "mz",
39776         "258"
39777       ],
39778       [
39779         "Myanmar (Burma) (မြန်မာ)",
39780         "mm",
39781         "95"
39782       ],
39783       [
39784         "Namibia (Namibië)",
39785         "na",
39786         "264"
39787       ],
39788       [
39789         "Nauru",
39790         "nr",
39791         "674"
39792       ],
39793       [
39794         "Nepal (नेपाल)",
39795         "np",
39796         "977"
39797       ],
39798       [
39799         "Netherlands (Nederland)",
39800         "nl",
39801         "31"
39802       ],
39803       [
39804         "New Caledonia (Nouvelle-Calédonie)",
39805         "nc",
39806         "687"
39807       ],
39808       [
39809         "New Zealand",
39810         "nz",
39811         "64"
39812       ],
39813       [
39814         "Nicaragua",
39815         "ni",
39816         "505"
39817       ],
39818       [
39819         "Niger (Nijar)",
39820         "ne",
39821         "227"
39822       ],
39823       [
39824         "Nigeria",
39825         "ng",
39826         "234"
39827       ],
39828       [
39829         "Niue",
39830         "nu",
39831         "683"
39832       ],
39833       [
39834         "Norfolk Island",
39835         "nf",
39836         "672"
39837       ],
39838       [
39839         "North Korea (조선 민주주의 인민 공화국)",
39840         "kp",
39841         "850"
39842       ],
39843       [
39844         "Northern Mariana Islands",
39845         "mp",
39846         "1670"
39847       ],
39848       [
39849         "Norway (Norge)",
39850         "no",
39851         "47",
39852         0
39853       ],
39854       [
39855         "Oman (‫عُمان‬‎)",
39856         "om",
39857         "968"
39858       ],
39859       [
39860         "Pakistan (‫پاکستان‬‎)",
39861         "pk",
39862         "92"
39863       ],
39864       [
39865         "Palau",
39866         "pw",
39867         "680"
39868       ],
39869       [
39870         "Palestine (‫فلسطين‬‎)",
39871         "ps",
39872         "970"
39873       ],
39874       [
39875         "Panama (Panamá)",
39876         "pa",
39877         "507"
39878       ],
39879       [
39880         "Papua New Guinea",
39881         "pg",
39882         "675"
39883       ],
39884       [
39885         "Paraguay",
39886         "py",
39887         "595"
39888       ],
39889       [
39890         "Peru (Perú)",
39891         "pe",
39892         "51"
39893       ],
39894       [
39895         "Philippines",
39896         "ph",
39897         "63"
39898       ],
39899       [
39900         "Poland (Polska)",
39901         "pl",
39902         "48"
39903       ],
39904       [
39905         "Portugal",
39906         "pt",
39907         "351"
39908       ],
39909       [
39910         "Puerto Rico",
39911         "pr",
39912         "1",
39913         3,
39914         ["787", "939"]
39915       ],
39916       [
39917         "Qatar (‫قطر‬‎)",
39918         "qa",
39919         "974"
39920       ],
39921       [
39922         "Réunion (La Réunion)",
39923         "re",
39924         "262",
39925         0
39926       ],
39927       [
39928         "Romania (România)",
39929         "ro",
39930         "40"
39931       ],
39932       [
39933         "Russia (Россия)",
39934         "ru",
39935         "7",
39936         0
39937       ],
39938       [
39939         "Rwanda",
39940         "rw",
39941         "250"
39942       ],
39943       [
39944         "Saint Barthélemy",
39945         "bl",
39946         "590",
39947         1
39948       ],
39949       [
39950         "Saint Helena",
39951         "sh",
39952         "290"
39953       ],
39954       [
39955         "Saint Kitts and Nevis",
39956         "kn",
39957         "1869"
39958       ],
39959       [
39960         "Saint Lucia",
39961         "lc",
39962         "1758"
39963       ],
39964       [
39965         "Saint Martin (Saint-Martin (partie française))",
39966         "mf",
39967         "590",
39968         2
39969       ],
39970       [
39971         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39972         "pm",
39973         "508"
39974       ],
39975       [
39976         "Saint Vincent and the Grenadines",
39977         "vc",
39978         "1784"
39979       ],
39980       [
39981         "Samoa",
39982         "ws",
39983         "685"
39984       ],
39985       [
39986         "San Marino",
39987         "sm",
39988         "378"
39989       ],
39990       [
39991         "São Tomé and Príncipe (São Tomé e Príncipe)",
39992         "st",
39993         "239"
39994       ],
39995       [
39996         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39997         "sa",
39998         "966"
39999       ],
40000       [
40001         "Senegal (Sénégal)",
40002         "sn",
40003         "221"
40004       ],
40005       [
40006         "Serbia (Србија)",
40007         "rs",
40008         "381"
40009       ],
40010       [
40011         "Seychelles",
40012         "sc",
40013         "248"
40014       ],
40015       [
40016         "Sierra Leone",
40017         "sl",
40018         "232"
40019       ],
40020       [
40021         "Singapore",
40022         "sg",
40023         "65"
40024       ],
40025       [
40026         "Sint Maarten",
40027         "sx",
40028         "1721"
40029       ],
40030       [
40031         "Slovakia (Slovensko)",
40032         "sk",
40033         "421"
40034       ],
40035       [
40036         "Slovenia (Slovenija)",
40037         "si",
40038         "386"
40039       ],
40040       [
40041         "Solomon Islands",
40042         "sb",
40043         "677"
40044       ],
40045       [
40046         "Somalia (Soomaaliya)",
40047         "so",
40048         "252"
40049       ],
40050       [
40051         "South Africa",
40052         "za",
40053         "27"
40054       ],
40055       [
40056         "South Korea (대한민국)",
40057         "kr",
40058         "82"
40059       ],
40060       [
40061         "South Sudan (‫جنوب السودان‬‎)",
40062         "ss",
40063         "211"
40064       ],
40065       [
40066         "Spain (España)",
40067         "es",
40068         "34"
40069       ],
40070       [
40071         "Sri Lanka (ශ්‍රී ලංකාව)",
40072         "lk",
40073         "94"
40074       ],
40075       [
40076         "Sudan (‫السودان‬‎)",
40077         "sd",
40078         "249"
40079       ],
40080       [
40081         "Suriname",
40082         "sr",
40083         "597"
40084       ],
40085       [
40086         "Svalbard and Jan Mayen",
40087         "sj",
40088         "47",
40089         1
40090       ],
40091       [
40092         "Swaziland",
40093         "sz",
40094         "268"
40095       ],
40096       [
40097         "Sweden (Sverige)",
40098         "se",
40099         "46"
40100       ],
40101       [
40102         "Switzerland (Schweiz)",
40103         "ch",
40104         "41"
40105       ],
40106       [
40107         "Syria (‫سوريا‬‎)",
40108         "sy",
40109         "963"
40110       ],
40111       [
40112         "Taiwan (台灣)",
40113         "tw",
40114         "886"
40115       ],
40116       [
40117         "Tajikistan",
40118         "tj",
40119         "992"
40120       ],
40121       [
40122         "Tanzania",
40123         "tz",
40124         "255"
40125       ],
40126       [
40127         "Thailand (ไทย)",
40128         "th",
40129         "66"
40130       ],
40131       [
40132         "Timor-Leste",
40133         "tl",
40134         "670"
40135       ],
40136       [
40137         "Togo",
40138         "tg",
40139         "228"
40140       ],
40141       [
40142         "Tokelau",
40143         "tk",
40144         "690"
40145       ],
40146       [
40147         "Tonga",
40148         "to",
40149         "676"
40150       ],
40151       [
40152         "Trinidad and Tobago",
40153         "tt",
40154         "1868"
40155       ],
40156       [
40157         "Tunisia (‫تونس‬‎)",
40158         "tn",
40159         "216"
40160       ],
40161       [
40162         "Turkey (Türkiye)",
40163         "tr",
40164         "90"
40165       ],
40166       [
40167         "Turkmenistan",
40168         "tm",
40169         "993"
40170       ],
40171       [
40172         "Turks and Caicos Islands",
40173         "tc",
40174         "1649"
40175       ],
40176       [
40177         "Tuvalu",
40178         "tv",
40179         "688"
40180       ],
40181       [
40182         "U.S. Virgin Islands",
40183         "vi",
40184         "1340"
40185       ],
40186       [
40187         "Uganda",
40188         "ug",
40189         "256"
40190       ],
40191       [
40192         "Ukraine (Україна)",
40193         "ua",
40194         "380"
40195       ],
40196       [
40197         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
40198         "ae",
40199         "971"
40200       ],
40201       [
40202         "United Kingdom",
40203         "gb",
40204         "44",
40205         0
40206       ],
40207       [
40208         "United States",
40209         "us",
40210         "1",
40211         0
40212       ],
40213       [
40214         "Uruguay",
40215         "uy",
40216         "598"
40217       ],
40218       [
40219         "Uzbekistan (Oʻzbekiston)",
40220         "uz",
40221         "998"
40222       ],
40223       [
40224         "Vanuatu",
40225         "vu",
40226         "678"
40227       ],
40228       [
40229         "Vatican City (Città del Vaticano)",
40230         "va",
40231         "39",
40232         1
40233       ],
40234       [
40235         "Venezuela",
40236         "ve",
40237         "58"
40238       ],
40239       [
40240         "Vietnam (Việt Nam)",
40241         "vn",
40242         "84"
40243       ],
40244       [
40245         "Wallis and Futuna (Wallis-et-Futuna)",
40246         "wf",
40247         "681"
40248       ],
40249       [
40250         "Western Sahara (‫الصحراء الغربية‬‎)",
40251         "eh",
40252         "212",
40253         1
40254       ],
40255       [
40256         "Yemen (‫اليمن‬‎)",
40257         "ye",
40258         "967"
40259       ],
40260       [
40261         "Zambia",
40262         "zm",
40263         "260"
40264       ],
40265       [
40266         "Zimbabwe",
40267         "zw",
40268         "263"
40269       ],
40270       [
40271         "Åland Islands",
40272         "ax",
40273         "358",
40274         1
40275       ]
40276   ];
40277   
40278   return d;
40279 }/**
40280 *    This script refer to:
40281 *    Title: International Telephone Input
40282 *    Author: Jack O'Connor
40283 *    Code version:  v12.1.12
40284 *    Availability: https://github.com/jackocnr/intl-tel-input.git
40285 **/
40286
40287 /**
40288  * @class Roo.bootstrap.PhoneInput
40289  * @extends Roo.bootstrap.TriggerField
40290  * An input with International dial-code selection
40291  
40292  * @cfg {String} defaultDialCode default '+852'
40293  * @cfg {Array} preferedCountries default []
40294   
40295  * @constructor
40296  * Create a new PhoneInput.
40297  * @param {Object} config Configuration options
40298  */
40299
40300 Roo.bootstrap.PhoneInput = function(config) {
40301     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
40302 };
40303
40304 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
40305         
40306         listWidth: undefined,
40307         
40308         selectedClass: 'active',
40309         
40310         invalidClass : "has-warning",
40311         
40312         validClass: 'has-success',
40313         
40314         allowed: '0123456789',
40315         
40316         max_length: 15,
40317         
40318         /**
40319          * @cfg {String} defaultDialCode The default dial code when initializing the input
40320          */
40321         defaultDialCode: '+852',
40322         
40323         /**
40324          * @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
40325          */
40326         preferedCountries: false,
40327         
40328         getAutoCreate : function()
40329         {
40330             var data = Roo.bootstrap.PhoneInputData();
40331             var align = this.labelAlign || this.parentLabelAlign();
40332             var id = Roo.id();
40333             
40334             this.allCountries = [];
40335             this.dialCodeMapping = [];
40336             
40337             for (var i = 0; i < data.length; i++) {
40338               var c = data[i];
40339               this.allCountries[i] = {
40340                 name: c[0],
40341                 iso2: c[1],
40342                 dialCode: c[2],
40343                 priority: c[3] || 0,
40344                 areaCodes: c[4] || null
40345               };
40346               this.dialCodeMapping[c[2]] = {
40347                   name: c[0],
40348                   iso2: c[1],
40349                   priority: c[3] || 0,
40350                   areaCodes: c[4] || null
40351               };
40352             }
40353             
40354             var cfg = {
40355                 cls: 'form-group',
40356                 cn: []
40357             };
40358             
40359             var input =  {
40360                 tag: 'input',
40361                 id : id,
40362                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40363                 maxlength: this.max_length,
40364                 cls : 'form-control tel-input',
40365                 autocomplete: 'new-password'
40366             };
40367             
40368             var hiddenInput = {
40369                 tag: 'input',
40370                 type: 'hidden',
40371                 cls: 'hidden-tel-input'
40372             };
40373             
40374             if (this.name) {
40375                 hiddenInput.name = this.name;
40376             }
40377             
40378             if (this.disabled) {
40379                 input.disabled = true;
40380             }
40381             
40382             var flag_container = {
40383                 tag: 'div',
40384                 cls: 'flag-box',
40385                 cn: [
40386                     {
40387                         tag: 'div',
40388                         cls: 'flag'
40389                     },
40390                     {
40391                         tag: 'div',
40392                         cls: 'caret'
40393                     }
40394                 ]
40395             };
40396             
40397             var box = {
40398                 tag: 'div',
40399                 cls: this.hasFeedback ? 'has-feedback' : '',
40400                 cn: [
40401                     hiddenInput,
40402                     input,
40403                     {
40404                         tag: 'input',
40405                         cls: 'dial-code-holder',
40406                         disabled: true
40407                     }
40408                 ]
40409             };
40410             
40411             var container = {
40412                 cls: 'roo-select2-container input-group',
40413                 cn: [
40414                     flag_container,
40415                     box
40416                 ]
40417             };
40418             
40419             if (this.fieldLabel.length) {
40420                 var indicator = {
40421                     tag: 'i',
40422                     tooltip: 'This field is required'
40423                 };
40424                 
40425                 var label = {
40426                     tag: 'label',
40427                     'for':  id,
40428                     cls: 'control-label',
40429                     cn: []
40430                 };
40431                 
40432                 var label_text = {
40433                     tag: 'span',
40434                     html: this.fieldLabel
40435                 };
40436                 
40437                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40438                 label.cn = [
40439                     indicator,
40440                     label_text
40441                 ];
40442                 
40443                 if(this.indicatorpos == 'right') {
40444                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40445                     label.cn = [
40446                         label_text,
40447                         indicator
40448                     ];
40449                 }
40450                 
40451                 if(align == 'left') {
40452                     container = {
40453                         tag: 'div',
40454                         cn: [
40455                             container
40456                         ]
40457                     };
40458                     
40459                     if(this.labelWidth > 12){
40460                         label.style = "width: " + this.labelWidth + 'px';
40461                     }
40462                     if(this.labelWidth < 13 && this.labelmd == 0){
40463                         this.labelmd = this.labelWidth;
40464                     }
40465                     if(this.labellg > 0){
40466                         label.cls += ' col-lg-' + this.labellg;
40467                         input.cls += ' col-lg-' + (12 - this.labellg);
40468                     }
40469                     if(this.labelmd > 0){
40470                         label.cls += ' col-md-' + this.labelmd;
40471                         container.cls += ' col-md-' + (12 - this.labelmd);
40472                     }
40473                     if(this.labelsm > 0){
40474                         label.cls += ' col-sm-' + this.labelsm;
40475                         container.cls += ' col-sm-' + (12 - this.labelsm);
40476                     }
40477                     if(this.labelxs > 0){
40478                         label.cls += ' col-xs-' + this.labelxs;
40479                         container.cls += ' col-xs-' + (12 - this.labelxs);
40480                     }
40481                 }
40482             }
40483             
40484             cfg.cn = [
40485                 label,
40486                 container
40487             ];
40488             
40489             var settings = this;
40490             
40491             ['xs','sm','md','lg'].map(function(size){
40492                 if (settings[size]) {
40493                     cfg.cls += ' col-' + size + '-' + settings[size];
40494                 }
40495             });
40496             
40497             this.store = new Roo.data.Store({
40498                 proxy : new Roo.data.MemoryProxy({}),
40499                 reader : new Roo.data.JsonReader({
40500                     fields : [
40501                         {
40502                             'name' : 'name',
40503                             'type' : 'string'
40504                         },
40505                         {
40506                             'name' : 'iso2',
40507                             'type' : 'string'
40508                         },
40509                         {
40510                             'name' : 'dialCode',
40511                             'type' : 'string'
40512                         },
40513                         {
40514                             'name' : 'priority',
40515                             'type' : 'string'
40516                         },
40517                         {
40518                             'name' : 'areaCodes',
40519                             'type' : 'string'
40520                         }
40521                     ]
40522                 })
40523             });
40524             
40525             if(!this.preferedCountries) {
40526                 this.preferedCountries = [
40527                     'hk',
40528                     'gb',
40529                     'us'
40530                 ];
40531             }
40532             
40533             var p = this.preferedCountries.reverse();
40534             
40535             if(p) {
40536                 for (var i = 0; i < p.length; i++) {
40537                     for (var j = 0; j < this.allCountries.length; j++) {
40538                         if(this.allCountries[j].iso2 == p[i]) {
40539                             var t = this.allCountries[j];
40540                             this.allCountries.splice(j,1);
40541                             this.allCountries.unshift(t);
40542                         }
40543                     } 
40544                 }
40545             }
40546             
40547             this.store.proxy.data = {
40548                 success: true,
40549                 data: this.allCountries
40550             };
40551             
40552             return cfg;
40553         },
40554         
40555         initEvents : function()
40556         {
40557             this.createList();
40558             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40559             
40560             this.indicator = this.indicatorEl();
40561             this.flag = this.flagEl();
40562             this.dialCodeHolder = this.dialCodeHolderEl();
40563             
40564             this.trigger = this.el.select('div.flag-box',true).first();
40565             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40566             
40567             var _this = this;
40568             
40569             (function(){
40570                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40571                 _this.list.setWidth(lw);
40572             }).defer(100);
40573             
40574             this.list.on('mouseover', this.onViewOver, this);
40575             this.list.on('mousemove', this.onViewMove, this);
40576             this.inputEl().on("keyup", this.onKeyUp, this);
40577             this.inputEl().on("keypress", this.onKeyPress, this);
40578             
40579             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40580
40581             this.view = new Roo.View(this.list, this.tpl, {
40582                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40583             });
40584             
40585             this.view.on('click', this.onViewClick, this);
40586             this.setValue(this.defaultDialCode);
40587         },
40588         
40589         onTriggerClick : function(e)
40590         {
40591             Roo.log('trigger click');
40592             if(this.disabled){
40593                 return;
40594             }
40595             
40596             if(this.isExpanded()){
40597                 this.collapse();
40598                 this.hasFocus = false;
40599             }else {
40600                 this.store.load({});
40601                 this.hasFocus = true;
40602                 this.expand();
40603             }
40604         },
40605         
40606         isExpanded : function()
40607         {
40608             return this.list.isVisible();
40609         },
40610         
40611         collapse : function()
40612         {
40613             if(!this.isExpanded()){
40614                 return;
40615             }
40616             this.list.hide();
40617             Roo.get(document).un('mousedown', this.collapseIf, this);
40618             Roo.get(document).un('mousewheel', this.collapseIf, this);
40619             this.fireEvent('collapse', this);
40620             this.validate();
40621         },
40622         
40623         expand : function()
40624         {
40625             Roo.log('expand');
40626
40627             if(this.isExpanded() || !this.hasFocus){
40628                 return;
40629             }
40630             
40631             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40632             this.list.setWidth(lw);
40633             
40634             this.list.show();
40635             this.restrictHeight();
40636             
40637             Roo.get(document).on('mousedown', this.collapseIf, this);
40638             Roo.get(document).on('mousewheel', this.collapseIf, this);
40639             
40640             this.fireEvent('expand', this);
40641         },
40642         
40643         restrictHeight : function()
40644         {
40645             this.list.alignTo(this.inputEl(), this.listAlign);
40646             this.list.alignTo(this.inputEl(), this.listAlign);
40647         },
40648         
40649         onViewOver : function(e, t)
40650         {
40651             if(this.inKeyMode){
40652                 return;
40653             }
40654             var item = this.view.findItemFromChild(t);
40655             
40656             if(item){
40657                 var index = this.view.indexOf(item);
40658                 this.select(index, false);
40659             }
40660         },
40661
40662         // private
40663         onViewClick : function(view, doFocus, el, e)
40664         {
40665             var index = this.view.getSelectedIndexes()[0];
40666             
40667             var r = this.store.getAt(index);
40668             
40669             if(r){
40670                 this.onSelect(r, index);
40671             }
40672             if(doFocus !== false && !this.blockFocus){
40673                 this.inputEl().focus();
40674             }
40675         },
40676         
40677         onViewMove : function(e, t)
40678         {
40679             this.inKeyMode = false;
40680         },
40681         
40682         select : function(index, scrollIntoView)
40683         {
40684             this.selectedIndex = index;
40685             this.view.select(index);
40686             if(scrollIntoView !== false){
40687                 var el = this.view.getNode(index);
40688                 if(el){
40689                     this.list.scrollChildIntoView(el, false);
40690                 }
40691             }
40692         },
40693         
40694         createList : function()
40695         {
40696             this.list = Roo.get(document.body).createChild({
40697                 tag: 'ul',
40698                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40699                 style: 'display:none'
40700             });
40701             
40702             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40703         },
40704         
40705         collapseIf : function(e)
40706         {
40707             var in_combo  = e.within(this.el);
40708             var in_list =  e.within(this.list);
40709             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40710             
40711             if (in_combo || in_list || is_list) {
40712                 return;
40713             }
40714             this.collapse();
40715         },
40716         
40717         onSelect : function(record, index)
40718         {
40719             if(this.fireEvent('beforeselect', this, record, index) !== false){
40720                 
40721                 this.setFlagClass(record.data.iso2);
40722                 this.setDialCode(record.data.dialCode);
40723                 this.hasFocus = false;
40724                 this.collapse();
40725                 this.fireEvent('select', this, record, index);
40726             }
40727         },
40728         
40729         flagEl : function()
40730         {
40731             var flag = this.el.select('div.flag',true).first();
40732             if(!flag){
40733                 return false;
40734             }
40735             return flag;
40736         },
40737         
40738         dialCodeHolderEl : function()
40739         {
40740             var d = this.el.select('input.dial-code-holder',true).first();
40741             if(!d){
40742                 return false;
40743             }
40744             return d;
40745         },
40746         
40747         setDialCode : function(v)
40748         {
40749             this.dialCodeHolder.dom.value = '+'+v;
40750         },
40751         
40752         setFlagClass : function(n)
40753         {
40754             this.flag.dom.className = 'flag '+n;
40755         },
40756         
40757         getValue : function()
40758         {
40759             var v = this.inputEl().getValue();
40760             if(this.dialCodeHolder) {
40761                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40762             }
40763             return v;
40764         },
40765         
40766         setValue : function(v)
40767         {
40768             var d = this.getDialCode(v);
40769             
40770             //invalid dial code
40771             if(v.length == 0 || !d || d.length == 0) {
40772                 if(this.rendered){
40773                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40774                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40775                 }
40776                 return;
40777             }
40778             
40779             //valid dial code
40780             this.setFlagClass(this.dialCodeMapping[d].iso2);
40781             this.setDialCode(d);
40782             this.inputEl().dom.value = v.replace('+'+d,'');
40783             this.hiddenEl().dom.value = this.getValue();
40784             
40785             this.validate();
40786         },
40787         
40788         getDialCode : function(v)
40789         {
40790             v = v ||  '';
40791             
40792             if (v.length == 0) {
40793                 return this.dialCodeHolder.dom.value;
40794             }
40795             
40796             var dialCode = "";
40797             if (v.charAt(0) != "+") {
40798                 return false;
40799             }
40800             var numericChars = "";
40801             for (var i = 1; i < v.length; i++) {
40802               var c = v.charAt(i);
40803               if (!isNaN(c)) {
40804                 numericChars += c;
40805                 if (this.dialCodeMapping[numericChars]) {
40806                   dialCode = v.substr(1, i);
40807                 }
40808                 if (numericChars.length == 4) {
40809                   break;
40810                 }
40811               }
40812             }
40813             return dialCode;
40814         },
40815         
40816         reset : function()
40817         {
40818             this.setValue(this.defaultDialCode);
40819             this.validate();
40820         },
40821         
40822         hiddenEl : function()
40823         {
40824             return this.el.select('input.hidden-tel-input',true).first();
40825         },
40826         
40827         // after setting val
40828         onKeyUp : function(e){
40829             this.setValue(this.getValue());
40830         },
40831         
40832         onKeyPress : function(e){
40833             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40834                 e.stopEvent();
40835             }
40836         }
40837         
40838 });
40839 /**
40840  * @class Roo.bootstrap.MoneyField
40841  * @extends Roo.bootstrap.ComboBox
40842  * Bootstrap MoneyField class
40843  * 
40844  * @constructor
40845  * Create a new MoneyField.
40846  * @param {Object} config Configuration options
40847  */
40848
40849 Roo.bootstrap.MoneyField = function(config) {
40850     
40851     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40852     
40853 };
40854
40855 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40856     
40857     /**
40858      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40859      */
40860     allowDecimals : true,
40861     /**
40862      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40863      */
40864     decimalSeparator : ".",
40865     /**
40866      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40867      */
40868     decimalPrecision : 0,
40869     /**
40870      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40871      */
40872     allowNegative : true,
40873     /**
40874      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40875      */
40876     allowZero: true,
40877     /**
40878      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40879      */
40880     minValue : Number.NEGATIVE_INFINITY,
40881     /**
40882      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40883      */
40884     maxValue : Number.MAX_VALUE,
40885     /**
40886      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40887      */
40888     minText : "The minimum value for this field is {0}",
40889     /**
40890      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40891      */
40892     maxText : "The maximum value for this field is {0}",
40893     /**
40894      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40895      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40896      */
40897     nanText : "{0} is not a valid number",
40898     /**
40899      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40900      */
40901     castInt : true,
40902     /**
40903      * @cfg {String} defaults currency of the MoneyField
40904      * value should be in lkey
40905      */
40906     defaultCurrency : false,
40907     /**
40908      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40909      */
40910     thousandsDelimiter : false,
40911     /**
40912      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40913      */
40914     max_length: false,
40915     
40916     inputlg : 9,
40917     inputmd : 9,
40918     inputsm : 9,
40919     inputxs : 6,
40920     
40921     store : false,
40922     
40923     getAutoCreate : function()
40924     {
40925         var align = this.labelAlign || this.parentLabelAlign();
40926         
40927         var id = Roo.id();
40928
40929         var cfg = {
40930             cls: 'form-group',
40931             cn: []
40932         };
40933
40934         var input =  {
40935             tag: 'input',
40936             id : id,
40937             cls : 'form-control roo-money-amount-input',
40938             autocomplete: 'new-password'
40939         };
40940         
40941         var hiddenInput = {
40942             tag: 'input',
40943             type: 'hidden',
40944             id: Roo.id(),
40945             cls: 'hidden-number-input'
40946         };
40947         
40948         if(this.max_length) {
40949             input.maxlength = this.max_length; 
40950         }
40951         
40952         if (this.name) {
40953             hiddenInput.name = this.name;
40954         }
40955
40956         if (this.disabled) {
40957             input.disabled = true;
40958         }
40959
40960         var clg = 12 - this.inputlg;
40961         var cmd = 12 - this.inputmd;
40962         var csm = 12 - this.inputsm;
40963         var cxs = 12 - this.inputxs;
40964         
40965         var container = {
40966             tag : 'div',
40967             cls : 'row roo-money-field',
40968             cn : [
40969                 {
40970                     tag : 'div',
40971                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40972                     cn : [
40973                         {
40974                             tag : 'div',
40975                             cls: 'roo-select2-container input-group',
40976                             cn: [
40977                                 {
40978                                     tag : 'input',
40979                                     cls : 'form-control roo-money-currency-input',
40980                                     autocomplete: 'new-password',
40981                                     readOnly : 1,
40982                                     name : this.currencyName
40983                                 },
40984                                 {
40985                                     tag :'span',
40986                                     cls : 'input-group-addon',
40987                                     cn : [
40988                                         {
40989                                             tag: 'span',
40990                                             cls: 'caret'
40991                                         }
40992                                     ]
40993                                 }
40994                             ]
40995                         }
40996                     ]
40997                 },
40998                 {
40999                     tag : 'div',
41000                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
41001                     cn : [
41002                         {
41003                             tag: 'div',
41004                             cls: this.hasFeedback ? 'has-feedback' : '',
41005                             cn: [
41006                                 input
41007                             ]
41008                         }
41009                     ]
41010                 }
41011             ]
41012             
41013         };
41014         
41015         if (this.fieldLabel.length) {
41016             var indicator = {
41017                 tag: 'i',
41018                 tooltip: 'This field is required'
41019             };
41020
41021             var label = {
41022                 tag: 'label',
41023                 'for':  id,
41024                 cls: 'control-label',
41025                 cn: []
41026             };
41027
41028             var label_text = {
41029                 tag: 'span',
41030                 html: this.fieldLabel
41031             };
41032
41033             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
41034             label.cn = [
41035                 indicator,
41036                 label_text
41037             ];
41038
41039             if(this.indicatorpos == 'right') {
41040                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
41041                 label.cn = [
41042                     label_text,
41043                     indicator
41044                 ];
41045             }
41046
41047             if(align == 'left') {
41048                 container = {
41049                     tag: 'div',
41050                     cn: [
41051                         container
41052                     ]
41053                 };
41054
41055                 if(this.labelWidth > 12){
41056                     label.style = "width: " + this.labelWidth + 'px';
41057                 }
41058                 if(this.labelWidth < 13 && this.labelmd == 0){
41059                     this.labelmd = this.labelWidth;
41060                 }
41061                 if(this.labellg > 0){
41062                     label.cls += ' col-lg-' + this.labellg;
41063                     input.cls += ' col-lg-' + (12 - this.labellg);
41064                 }
41065                 if(this.labelmd > 0){
41066                     label.cls += ' col-md-' + this.labelmd;
41067                     container.cls += ' col-md-' + (12 - this.labelmd);
41068                 }
41069                 if(this.labelsm > 0){
41070                     label.cls += ' col-sm-' + this.labelsm;
41071                     container.cls += ' col-sm-' + (12 - this.labelsm);
41072                 }
41073                 if(this.labelxs > 0){
41074                     label.cls += ' col-xs-' + this.labelxs;
41075                     container.cls += ' col-xs-' + (12 - this.labelxs);
41076                 }
41077             }
41078         }
41079
41080         cfg.cn = [
41081             label,
41082             container,
41083             hiddenInput
41084         ];
41085         
41086         var settings = this;
41087
41088         ['xs','sm','md','lg'].map(function(size){
41089             if (settings[size]) {
41090                 cfg.cls += ' col-' + size + '-' + settings[size];
41091             }
41092         });
41093         
41094         return cfg;
41095     },
41096     
41097     initEvents : function()
41098     {
41099         this.indicator = this.indicatorEl();
41100         
41101         this.initCurrencyEvent();
41102         
41103         this.initNumberEvent();
41104     },
41105     
41106     initCurrencyEvent : function()
41107     {
41108         if (!this.store) {
41109             throw "can not find store for combo";
41110         }
41111         
41112         this.store = Roo.factory(this.store, Roo.data);
41113         this.store.parent = this;
41114         
41115         this.createList();
41116         
41117         this.triggerEl = this.el.select('.input-group-addon', true).first();
41118         
41119         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
41120         
41121         var _this = this;
41122         
41123         (function(){
41124             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
41125             _this.list.setWidth(lw);
41126         }).defer(100);
41127         
41128         this.list.on('mouseover', this.onViewOver, this);
41129         this.list.on('mousemove', this.onViewMove, this);
41130         this.list.on('scroll', this.onViewScroll, this);
41131         
41132         if(!this.tpl){
41133             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
41134         }
41135         
41136         this.view = new Roo.View(this.list, this.tpl, {
41137             singleSelect:true, store: this.store, selectedClass: this.selectedClass
41138         });
41139         
41140         this.view.on('click', this.onViewClick, this);
41141         
41142         this.store.on('beforeload', this.onBeforeLoad, this);
41143         this.store.on('load', this.onLoad, this);
41144         this.store.on('loadexception', this.onLoadException, this);
41145         
41146         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
41147             "up" : function(e){
41148                 this.inKeyMode = true;
41149                 this.selectPrev();
41150             },
41151
41152             "down" : function(e){
41153                 if(!this.isExpanded()){
41154                     this.onTriggerClick();
41155                 }else{
41156                     this.inKeyMode = true;
41157                     this.selectNext();
41158                 }
41159             },
41160
41161             "enter" : function(e){
41162                 this.collapse();
41163                 
41164                 if(this.fireEvent("specialkey", this, e)){
41165                     this.onViewClick(false);
41166                 }
41167                 
41168                 return true;
41169             },
41170
41171             "esc" : function(e){
41172                 this.collapse();
41173             },
41174
41175             "tab" : function(e){
41176                 this.collapse();
41177                 
41178                 if(this.fireEvent("specialkey", this, e)){
41179                     this.onViewClick(false);
41180                 }
41181                 
41182                 return true;
41183             },
41184
41185             scope : this,
41186
41187             doRelay : function(foo, bar, hname){
41188                 if(hname == 'down' || this.scope.isExpanded()){
41189                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41190                 }
41191                 return true;
41192             },
41193
41194             forceKeyDown: true
41195         });
41196         
41197         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
41198         
41199     },
41200     
41201     initNumberEvent : function(e)
41202     {
41203         this.inputEl().on("keydown" , this.fireKey,  this);
41204         this.inputEl().on("focus", this.onFocus,  this);
41205         this.inputEl().on("blur", this.onBlur,  this);
41206         
41207         this.inputEl().relayEvent('keyup', this);
41208         
41209         if(this.indicator){
41210             this.indicator.addClass('invisible');
41211         }
41212  
41213         this.originalValue = this.getValue();
41214         
41215         if(this.validationEvent == 'keyup'){
41216             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
41217             this.inputEl().on('keyup', this.filterValidation, this);
41218         }
41219         else if(this.validationEvent !== false){
41220             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
41221         }
41222         
41223         if(this.selectOnFocus){
41224             this.on("focus", this.preFocus, this);
41225             
41226         }
41227         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41228             this.inputEl().on("keypress", this.filterKeys, this);
41229         } else {
41230             this.inputEl().relayEvent('keypress', this);
41231         }
41232         
41233         var allowed = "0123456789";
41234         
41235         if(this.allowDecimals){
41236             allowed += this.decimalSeparator;
41237         }
41238         
41239         if(this.allowNegative){
41240             allowed += "-";
41241         }
41242         
41243         if(this.thousandsDelimiter) {
41244             allowed += ",";
41245         }
41246         
41247         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41248         
41249         var keyPress = function(e){
41250             
41251             var k = e.getKey();
41252             
41253             var c = e.getCharCode();
41254             
41255             if(
41256                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41257                     allowed.indexOf(String.fromCharCode(c)) === -1
41258             ){
41259                 e.stopEvent();
41260                 return;
41261             }
41262             
41263             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41264                 return;
41265             }
41266             
41267             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41268                 e.stopEvent();
41269             }
41270         };
41271         
41272         this.inputEl().on("keypress", keyPress, this);
41273         
41274     },
41275     
41276     onTriggerClick : function(e)
41277     {   
41278         if(this.disabled){
41279             return;
41280         }
41281         
41282         this.page = 0;
41283         this.loadNext = false;
41284         
41285         if(this.isExpanded()){
41286             this.collapse();
41287             return;
41288         }
41289         
41290         this.hasFocus = true;
41291         
41292         if(this.triggerAction == 'all') {
41293             this.doQuery(this.allQuery, true);
41294             return;
41295         }
41296         
41297         this.doQuery(this.getRawValue());
41298     },
41299     
41300     getCurrency : function()
41301     {   
41302         var v = this.currencyEl().getValue();
41303         
41304         return v;
41305     },
41306     
41307     restrictHeight : function()
41308     {
41309         this.list.alignTo(this.currencyEl(), this.listAlign);
41310         this.list.alignTo(this.currencyEl(), this.listAlign);
41311     },
41312     
41313     onViewClick : function(view, doFocus, el, e)
41314     {
41315         var index = this.view.getSelectedIndexes()[0];
41316         
41317         var r = this.store.getAt(index);
41318         
41319         if(r){
41320             this.onSelect(r, index);
41321         }
41322     },
41323     
41324     onSelect : function(record, index){
41325         
41326         if(this.fireEvent('beforeselect', this, record, index) !== false){
41327         
41328             this.setFromCurrencyData(index > -1 ? record.data : false);
41329             
41330             this.collapse();
41331             
41332             this.fireEvent('select', this, record, index);
41333         }
41334     },
41335     
41336     setFromCurrencyData : function(o)
41337     {
41338         var currency = '';
41339         
41340         this.lastCurrency = o;
41341         
41342         if (this.currencyField) {
41343             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41344         } else {
41345             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
41346         }
41347         
41348         this.lastSelectionText = currency;
41349         
41350         //setting default currency
41351         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41352             this.setCurrency(this.defaultCurrency);
41353             return;
41354         }
41355         
41356         this.setCurrency(currency);
41357     },
41358     
41359     setFromData : function(o)
41360     {
41361         var c = {};
41362         
41363         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41364         
41365         this.setFromCurrencyData(c);
41366         
41367         var value = '';
41368         
41369         if (this.name) {
41370             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41371         } else {
41372             Roo.log('no value set for '+ (this.name ? this.name : this.id));
41373         }
41374         
41375         this.setValue(value);
41376         
41377     },
41378     
41379     setCurrency : function(v)
41380     {   
41381         this.currencyValue = v;
41382         
41383         if(this.rendered){
41384             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41385             this.validate();
41386         }
41387     },
41388     
41389     setValue : function(v)
41390     {
41391         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41392         
41393         this.value = v;
41394         
41395         if(this.rendered){
41396             
41397             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41398             
41399             this.inputEl().dom.value = (v == '') ? '' :
41400                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41401             
41402             if(!this.allowZero && v === '0') {
41403                 this.hiddenEl().dom.value = '';
41404                 this.inputEl().dom.value = '';
41405             }
41406             
41407             this.validate();
41408         }
41409     },
41410     
41411     getRawValue : function()
41412     {
41413         var v = this.inputEl().getValue();
41414         
41415         return v;
41416     },
41417     
41418     getValue : function()
41419     {
41420         return this.fixPrecision(this.parseValue(this.getRawValue()));
41421     },
41422     
41423     parseValue : function(value)
41424     {
41425         if(this.thousandsDelimiter) {
41426             value += "";
41427             r = new RegExp(",", "g");
41428             value = value.replace(r, "");
41429         }
41430         
41431         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41432         return isNaN(value) ? '' : value;
41433         
41434     },
41435     
41436     fixPrecision : function(value)
41437     {
41438         if(this.thousandsDelimiter) {
41439             value += "";
41440             r = new RegExp(",", "g");
41441             value = value.replace(r, "");
41442         }
41443         
41444         var nan = isNaN(value);
41445         
41446         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41447             return nan ? '' : value;
41448         }
41449         return parseFloat(value).toFixed(this.decimalPrecision);
41450     },
41451     
41452     decimalPrecisionFcn : function(v)
41453     {
41454         return Math.floor(v);
41455     },
41456     
41457     validateValue : function(value)
41458     {
41459         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41460             return false;
41461         }
41462         
41463         var num = this.parseValue(value);
41464         
41465         if(isNaN(num)){
41466             this.markInvalid(String.format(this.nanText, value));
41467             return false;
41468         }
41469         
41470         if(num < this.minValue){
41471             this.markInvalid(String.format(this.minText, this.minValue));
41472             return false;
41473         }
41474         
41475         if(num > this.maxValue){
41476             this.markInvalid(String.format(this.maxText, this.maxValue));
41477             return false;
41478         }
41479         
41480         return true;
41481     },
41482     
41483     validate : function()
41484     {
41485         if(this.disabled || this.allowBlank){
41486             this.markValid();
41487             return true;
41488         }
41489         
41490         var currency = this.getCurrency();
41491         
41492         if(this.validateValue(this.getRawValue()) && currency.length){
41493             this.markValid();
41494             return true;
41495         }
41496         
41497         this.markInvalid();
41498         return false;
41499     },
41500     
41501     getName: function()
41502     {
41503         return this.name;
41504     },
41505     
41506     beforeBlur : function()
41507     {
41508         if(!this.castInt){
41509             return;
41510         }
41511         
41512         var v = this.parseValue(this.getRawValue());
41513         
41514         if(v || v == 0){
41515             this.setValue(v);
41516         }
41517     },
41518     
41519     onBlur : function()
41520     {
41521         this.beforeBlur();
41522         
41523         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41524             //this.el.removeClass(this.focusClass);
41525         }
41526         
41527         this.hasFocus = false;
41528         
41529         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41530             this.validate();
41531         }
41532         
41533         var v = this.getValue();
41534         
41535         if(String(v) !== String(this.startValue)){
41536             this.fireEvent('change', this, v, this.startValue);
41537         }
41538         
41539         this.fireEvent("blur", this);
41540     },
41541     
41542     inputEl : function()
41543     {
41544         return this.el.select('.roo-money-amount-input', true).first();
41545     },
41546     
41547     currencyEl : function()
41548     {
41549         return this.el.select('.roo-money-currency-input', true).first();
41550     },
41551     
41552     hiddenEl : function()
41553     {
41554         return this.el.select('input.hidden-number-input',true).first();
41555     }
41556     
41557 });/**
41558  * @class Roo.bootstrap.BezierSignature
41559  * @extends Roo.bootstrap.Component
41560  * Bootstrap BezierSignature class
41561  * This script refer to:
41562  *    Title: Signature Pad
41563  *    Author: szimek
41564  *    Availability: https://github.com/szimek/signature_pad
41565  *
41566  * @constructor
41567  * Create a new BezierSignature
41568  * @param {Object} config The config object
41569  */
41570
41571 Roo.bootstrap.BezierSignature = function(config){
41572     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
41573     this.addEvents({
41574         "resize" : true
41575     });
41576 };
41577
41578 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
41579 {
41580      
41581     curve_data: [],
41582     
41583     is_empty: true,
41584     
41585     mouse_btn_down: true,
41586     
41587     /**
41588      * @cfg {int} canvas height
41589      */
41590     canvas_height: '200px',
41591     
41592     /**
41593      * @cfg {float|function} Radius of a single dot.
41594      */ 
41595     dot_size: false,
41596     
41597     /**
41598      * @cfg {float} Minimum width of a line. Defaults to 0.5.
41599      */
41600     min_width: 0.5,
41601     
41602     /**
41603      * @cfg {float} Maximum width of a line. Defaults to 2.5.
41604      */
41605     max_width: 2.5,
41606     
41607     /**
41608      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
41609      */
41610     throttle: 16,
41611     
41612     /**
41613      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
41614      */
41615     min_distance: 5,
41616     
41617     /**
41618      * @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.
41619      */
41620     bg_color: 'rgba(0, 0, 0, 0)',
41621     
41622     /**
41623      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
41624      */
41625     dot_color: 'black',
41626     
41627     /**
41628      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
41629      */ 
41630     velocity_filter_weight: 0.7,
41631     
41632     /**
41633      * @cfg {function} Callback when stroke begin. 
41634      */
41635     onBegin: false,
41636     
41637     /**
41638      * @cfg {function} Callback when stroke end.
41639      */
41640     onEnd: false,
41641     
41642     getAutoCreate : function()
41643     {
41644         var cls = 'roo-signature column';
41645         
41646         if(this.cls){
41647             cls += ' ' + this.cls;
41648         }
41649         
41650         var col_sizes = [
41651             'lg',
41652             'md',
41653             'sm',
41654             'xs'
41655         ];
41656         
41657         for(var i = 0; i < col_sizes.length; i++) {
41658             if(this[col_sizes[i]]) {
41659                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
41660             }
41661         }
41662         
41663         var cfg = {
41664             tag: 'div',
41665             cls: cls,
41666             cn: [
41667                 {
41668                     tag: 'div',
41669                     cls: 'roo-signature-body',
41670                     cn: [
41671                         {
41672                             tag: 'canvas',
41673                             cls: 'roo-signature-body-canvas',
41674                             height: this.canvas_height,
41675                             width: this.canvas_width
41676                         }
41677                     ]
41678                 },
41679                 {
41680                     tag: 'input',
41681                     type: 'file',
41682                     style: 'display: none'
41683                 }
41684             ]
41685         };
41686         
41687         return cfg;
41688     },
41689     
41690     initEvents: function() 
41691     {
41692         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
41693         
41694         var canvas = this.canvasEl();
41695         
41696         // mouse && touch event swapping...
41697         canvas.dom.style.touchAction = 'none';
41698         canvas.dom.style.msTouchAction = 'none';
41699         
41700         this.mouse_btn_down = false;
41701         canvas.on('mousedown', this._handleMouseDown, this);
41702         canvas.on('mousemove', this._handleMouseMove, this);
41703         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
41704         
41705         if (window.PointerEvent) {
41706             canvas.on('pointerdown', this._handleMouseDown, this);
41707             canvas.on('pointermove', this._handleMouseMove, this);
41708             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
41709         }
41710         
41711         if ('ontouchstart' in window) {
41712             canvas.on('touchstart', this._handleTouchStart, this);
41713             canvas.on('touchmove', this._handleTouchMove, this);
41714             canvas.on('touchend', this._handleTouchEnd, this);
41715         }
41716         
41717         Roo.EventManager.onWindowResize(this.resize, this, true);
41718         
41719         // file input event
41720         this.fileEl().on('change', this.uploadImage, this);
41721         
41722         this.clear();
41723         
41724         this.resize();
41725     },
41726     
41727     resize: function(){
41728         
41729         var canvas = this.canvasEl().dom;
41730         var ctx = this.canvasElCtx();
41731         var img_data = false;
41732         
41733         if(canvas.width > 0) {
41734             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
41735         }
41736         // setting canvas width will clean img data
41737         canvas.width = 0;
41738         
41739         var style = window.getComputedStyle ? 
41740             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
41741             
41742         var padding_left = parseInt(style.paddingLeft) || 0;
41743         var padding_right = parseInt(style.paddingRight) || 0;
41744         
41745         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
41746         
41747         if(img_data) {
41748             ctx.putImageData(img_data, 0, 0);
41749         }
41750     },
41751     
41752     _handleMouseDown: function(e)
41753     {
41754         if (e.browserEvent.which === 1) {
41755             this.mouse_btn_down = true;
41756             this.strokeBegin(e);
41757         }
41758     },
41759     
41760     _handleMouseMove: function (e)
41761     {
41762         if (this.mouse_btn_down) {
41763             this.strokeMoveUpdate(e);
41764         }
41765     },
41766     
41767     _handleMouseUp: function (e)
41768     {
41769         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
41770             this.mouse_btn_down = false;
41771             this.strokeEnd(e);
41772         }
41773     },
41774     
41775     _handleTouchStart: function (e) {
41776         
41777         e.preventDefault();
41778         if (e.browserEvent.targetTouches.length === 1) {
41779             // var touch = e.browserEvent.changedTouches[0];
41780             // this.strokeBegin(touch);
41781             
41782              this.strokeBegin(e); // assume e catching the correct xy...
41783         }
41784     },
41785     
41786     _handleTouchMove: function (e) {
41787         e.preventDefault();
41788         // var touch = event.targetTouches[0];
41789         // _this._strokeMoveUpdate(touch);
41790         this.strokeMoveUpdate(e);
41791     },
41792     
41793     _handleTouchEnd: function (e) {
41794         var wasCanvasTouched = e.target === this.canvasEl().dom;
41795         if (wasCanvasTouched) {
41796             e.preventDefault();
41797             // var touch = event.changedTouches[0];
41798             // _this._strokeEnd(touch);
41799             this.strokeEnd(e);
41800         }
41801     },
41802     
41803     reset: function () {
41804         this._lastPoints = [];
41805         this._lastVelocity = 0;
41806         this._lastWidth = (this.min_width + this.max_width) / 2;
41807         this.canvasElCtx().fillStyle = this.dot_color;
41808     },
41809     
41810     strokeMoveUpdate: function(e)
41811     {
41812         this.strokeUpdate(e);
41813         
41814         if (this.throttle) {
41815             this.throttleStroke(this.strokeUpdate, this.throttle);
41816         }
41817         else {
41818             this.strokeUpdate(e);
41819         }
41820     },
41821     
41822     strokeBegin: function(e)
41823     {
41824         var newPointGroup = {
41825             color: this.dot_color,
41826             points: []
41827         };
41828         
41829         if (typeof this.onBegin === 'function') {
41830             this.onBegin(e);
41831         }
41832         
41833         this.curve_data.push(newPointGroup);
41834         this.reset();
41835         this.strokeUpdate(e);
41836     },
41837     
41838     strokeUpdate: function(e)
41839     {
41840         var rect = this.canvasEl().dom.getBoundingClientRect();
41841         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
41842         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
41843         var lastPoints = lastPointGroup.points;
41844         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
41845         var isLastPointTooClose = lastPoint
41846             ? point.distanceTo(lastPoint) <= this.min_distance
41847             : false;
41848         var color = lastPointGroup.color;
41849         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
41850             var curve = this.addPoint(point);
41851             if (!lastPoint) {
41852                 this.drawDot({color: color, point: point});
41853             }
41854             else if (curve) {
41855                 this.drawCurve({color: color, curve: curve});
41856             }
41857             lastPoints.push({
41858                 time: point.time,
41859                 x: point.x,
41860                 y: point.y
41861             });
41862         }
41863     },
41864     
41865     strokeEnd: function(e)
41866     {
41867         this.strokeUpdate(e);
41868         if (typeof this.onEnd === 'function') {
41869             this.onEnd(e);
41870         }
41871     },
41872     
41873     addPoint:  function (point) {
41874         var _lastPoints = this._lastPoints;
41875         _lastPoints.push(point);
41876         if (_lastPoints.length > 2) {
41877             if (_lastPoints.length === 3) {
41878                 _lastPoints.unshift(_lastPoints[0]);
41879             }
41880             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
41881             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
41882             _lastPoints.shift();
41883             return curve;
41884         }
41885         return null;
41886     },
41887     
41888     calculateCurveWidths: function (startPoint, endPoint) {
41889         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
41890             (1 - this.velocity_filter_weight) * this._lastVelocity;
41891
41892         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
41893         var widths = {
41894             end: newWidth,
41895             start: this._lastWidth
41896         };
41897         
41898         this._lastVelocity = velocity;
41899         this._lastWidth = newWidth;
41900         return widths;
41901     },
41902     
41903     drawDot: function (_a) {
41904         var color = _a.color, point = _a.point;
41905         var ctx = this.canvasElCtx();
41906         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
41907         ctx.beginPath();
41908         this.drawCurveSegment(point.x, point.y, width);
41909         ctx.closePath();
41910         ctx.fillStyle = color;
41911         ctx.fill();
41912     },
41913     
41914     drawCurve: function (_a) {
41915         var color = _a.color, curve = _a.curve;
41916         var ctx = this.canvasElCtx();
41917         var widthDelta = curve.endWidth - curve.startWidth;
41918         var drawSteps = Math.floor(curve.length()) * 2;
41919         ctx.beginPath();
41920         ctx.fillStyle = color;
41921         for (var i = 0; i < drawSteps; i += 1) {
41922         var t = i / drawSteps;
41923         var tt = t * t;
41924         var ttt = tt * t;
41925         var u = 1 - t;
41926         var uu = u * u;
41927         var uuu = uu * u;
41928         var x = uuu * curve.startPoint.x;
41929         x += 3 * uu * t * curve.control1.x;
41930         x += 3 * u * tt * curve.control2.x;
41931         x += ttt * curve.endPoint.x;
41932         var y = uuu * curve.startPoint.y;
41933         y += 3 * uu * t * curve.control1.y;
41934         y += 3 * u * tt * curve.control2.y;
41935         y += ttt * curve.endPoint.y;
41936         var width = curve.startWidth + ttt * widthDelta;
41937         this.drawCurveSegment(x, y, width);
41938         }
41939         ctx.closePath();
41940         ctx.fill();
41941     },
41942     
41943     drawCurveSegment: function (x, y, width) {
41944         var ctx = this.canvasElCtx();
41945         ctx.moveTo(x, y);
41946         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
41947         this.is_empty = false;
41948     },
41949     
41950     clear: function()
41951     {
41952         var ctx = this.canvasElCtx();
41953         var canvas = this.canvasEl().dom;
41954         ctx.fillStyle = this.bg_color;
41955         ctx.clearRect(0, 0, canvas.width, canvas.height);
41956         ctx.fillRect(0, 0, canvas.width, canvas.height);
41957         this.curve_data = [];
41958         this.reset();
41959         this.is_empty = true;
41960     },
41961     
41962     fileEl: function()
41963     {
41964         return  this.el.select('input',true).first();
41965     },
41966     
41967     canvasEl: function()
41968     {
41969         return this.el.select('canvas',true).first();
41970     },
41971     
41972     canvasElCtx: function()
41973     {
41974         return this.el.select('canvas',true).first().dom.getContext('2d');
41975     },
41976     
41977     getImage: function(type)
41978     {
41979         if(this.is_empty) {
41980             return false;
41981         }
41982         
41983         // encryption ?
41984         return this.canvasEl().dom.toDataURL('image/'+type, 1);
41985     },
41986     
41987     drawFromImage: function(img_src)
41988     {
41989         var img = new Image();
41990         
41991         img.onload = function(){
41992             this.canvasElCtx().drawImage(img, 0, 0);
41993         }.bind(this);
41994         
41995         img.src = img_src;
41996         
41997         this.is_empty = false;
41998     },
41999     
42000     selectImage: function()
42001     {
42002         this.fileEl().dom.click();
42003     },
42004     
42005     uploadImage: function(e)
42006     {
42007         var reader = new FileReader();
42008         
42009         reader.onload = function(e){
42010             var img = new Image();
42011             img.onload = function(){
42012                 this.reset();
42013                 this.canvasElCtx().drawImage(img, 0, 0);
42014             }.bind(this);
42015             img.src = e.target.result;
42016         }.bind(this);
42017         
42018         reader.readAsDataURL(e.target.files[0]);
42019     },
42020     
42021     // Bezier Point Constructor
42022     Point: (function () {
42023         function Point(x, y, time) {
42024             this.x = x;
42025             this.y = y;
42026             this.time = time || Date.now();
42027         }
42028         Point.prototype.distanceTo = function (start) {
42029             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
42030         };
42031         Point.prototype.equals = function (other) {
42032             return this.x === other.x && this.y === other.y && this.time === other.time;
42033         };
42034         Point.prototype.velocityFrom = function (start) {
42035             return this.time !== start.time
42036             ? this.distanceTo(start) / (this.time - start.time)
42037             : 0;
42038         };
42039         return Point;
42040     }()),
42041     
42042     
42043     // Bezier Constructor
42044     Bezier: (function () {
42045         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
42046             this.startPoint = startPoint;
42047             this.control2 = control2;
42048             this.control1 = control1;
42049             this.endPoint = endPoint;
42050             this.startWidth = startWidth;
42051             this.endWidth = endWidth;
42052         }
42053         Bezier.fromPoints = function (points, widths, scope) {
42054             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
42055             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
42056             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
42057         };
42058         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
42059             var dx1 = s1.x - s2.x;
42060             var dy1 = s1.y - s2.y;
42061             var dx2 = s2.x - s3.x;
42062             var dy2 = s2.y - s3.y;
42063             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
42064             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
42065             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
42066             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
42067             var dxm = m1.x - m2.x;
42068             var dym = m1.y - m2.y;
42069             var k = l2 / (l1 + l2);
42070             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
42071             var tx = s2.x - cm.x;
42072             var ty = s2.y - cm.y;
42073             return {
42074                 c1: new scope.Point(m1.x + tx, m1.y + ty),
42075                 c2: new scope.Point(m2.x + tx, m2.y + ty)
42076             };
42077         };
42078         Bezier.prototype.length = function () {
42079             var steps = 10;
42080             var length = 0;
42081             var px;
42082             var py;
42083             for (var i = 0; i <= steps; i += 1) {
42084                 var t = i / steps;
42085                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
42086                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
42087                 if (i > 0) {
42088                     var xdiff = cx - px;
42089                     var ydiff = cy - py;
42090                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
42091                 }
42092                 px = cx;
42093                 py = cy;
42094             }
42095             return length;
42096         };
42097         Bezier.prototype.point = function (t, start, c1, c2, end) {
42098             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
42099             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
42100             + (3.0 * c2 * (1.0 - t) * t * t)
42101             + (end * t * t * t);
42102         };
42103         return Bezier;
42104     }()),
42105     
42106     throttleStroke: function(fn, wait) {
42107       if (wait === void 0) { wait = 250; }
42108       var previous = 0;
42109       var timeout = null;
42110       var result;
42111       var storedContext;
42112       var storedArgs;
42113       var later = function () {
42114           previous = Date.now();
42115           timeout = null;
42116           result = fn.apply(storedContext, storedArgs);
42117           if (!timeout) {
42118               storedContext = null;
42119               storedArgs = [];
42120           }
42121       };
42122       return function wrapper() {
42123           var args = [];
42124           for (var _i = 0; _i < arguments.length; _i++) {
42125               args[_i] = arguments[_i];
42126           }
42127           var now = Date.now();
42128           var remaining = wait - (now - previous);
42129           storedContext = this;
42130           storedArgs = args;
42131           if (remaining <= 0 || remaining > wait) {
42132               if (timeout) {
42133                   clearTimeout(timeout);
42134                   timeout = null;
42135               }
42136               previous = now;
42137               result = fn.apply(storedContext, storedArgs);
42138               if (!timeout) {
42139                   storedContext = null;
42140                   storedArgs = [];
42141               }
42142           }
42143           else if (!timeout) {
42144               timeout = window.setTimeout(later, remaining);
42145           }
42146           return result;
42147       };
42148   }
42149   
42150 });
42151
42152  
42153
42154